Import upstream version 2.8+git20220524.1.1ed280f
Debian Janitor
1 year, 11 months ago
0 | 0 | --- |
1 | Checks: '*,-abseil-*,-altera-*,-android-*,-cert-*,-cppcoreguidelines-*,-fuchsia-*,-google-*,-hicpp-*,-linuxkernel-*,-llvm-*,-llvmlibc-*,-mpi-*,-objc-*,-openmp-*,-zircon-*,-readability-braces-around-statements,-readability-magic-numbers,-bugprone-macro-parentheses,-readability-isolate-declaration,-readability-function-size,-modernize-use-trailing-return-type,-readability-implicit-bool-conversion,-readability-uppercase-literal-suffix,-modernize-use-auto,-readability-else-after-return,-modernize-use-default-member-init,-readability-named-parameter,-readability-redundant-member-init,-performance-faster-string-find,-modernize-avoid-c-arrays,-modernize-use-equals-default,-readability-container-size-empty,-readability-simplify-boolean-expr,-modernize-use-override,-modernize-pass-by-value,-bugprone-branch-clone,-bugprone-narrowing-conversions,-modernize-raw-string-literal,-readability-convert-member-functions-to-static,-modernize-loop-convert,-misc-unused-using-decls,-modernize-use-emplace,-readability-const-return-type,-performance-unnecessary-value-param,-modernize-return-braced-init-list,-performance-inefficient-string-concatenation,-misc-throw-by-value-catch-by-reference,-readability-avoid-const-params-in-decls,-readability-non-const-parameter,-misc-non-private-member-variables-in-classes,-bugprone-suspicious-string-compare,-clang-analyzer-*,-bugprone-signed-char-misuse,-readability-make-member-function-const,-misc-no-recursion,-readability-use-anyofallof,-performance-no-automatic-move,-bugprone-suspicious-include,-modernize-replace-random-shuffle,-readability-function-cognitive-complexity,-readability-redundant-access-specifiers,-modernize-use-equals-delete,-performance-noexcept-move-constructor,-concurrency-mt-unsafe,-bugprone-easily-swappable-parameters,-readability-suspicious-call-argument' | |
1 | Checks: '*,-abseil-*,-altera-*,-android-*,-cert-*,-cppcoreguidelines-*,-fuchsia-*,-google-*,-hicpp-*,-linuxkernel-*,-llvm-*,-llvmlibc-*,-mpi-*,-objc-*,-openmp-*,-zircon-*,-readability-braces-around-statements,-readability-magic-numbers,-bugprone-macro-parentheses,-readability-isolate-declaration,-readability-function-size,-modernize-use-trailing-return-type,-readability-implicit-bool-conversion,-readability-uppercase-literal-suffix,-modernize-use-auto,-readability-else-after-return,-modernize-use-default-member-init,-readability-named-parameter,-readability-redundant-member-init,-performance-faster-string-find,-modernize-avoid-c-arrays,-modernize-use-equals-default,-readability-container-size-empty,-readability-simplify-boolean-expr,-modernize-use-override,-modernize-pass-by-value,-bugprone-branch-clone,-bugprone-narrowing-conversions,-modernize-raw-string-literal,-readability-convert-member-functions-to-static,-modernize-loop-convert,-misc-unused-using-decls,-modernize-use-emplace,-readability-const-return-type,-performance-unnecessary-value-param,-modernize-return-braced-init-list,-performance-inefficient-string-concatenation,-misc-throw-by-value-catch-by-reference,-readability-avoid-const-params-in-decls,-readability-non-const-parameter,-misc-non-private-member-variables-in-classes,-bugprone-suspicious-string-compare,-clang-analyzer-*,-bugprone-signed-char-misuse,-readability-make-member-function-const,-misc-no-recursion,-readability-use-anyofallof,-performance-no-automatic-move,-bugprone-suspicious-include,-modernize-replace-random-shuffle,-readability-function-cognitive-complexity,-readability-redundant-access-specifiers,-modernize-use-equals-delete,-performance-noexcept-move-constructor,-concurrency-mt-unsafe,-bugprone-easily-swappable-parameters,-readability-suspicious-call-argument,-readability-identifier-length,-readability-container-data-pointer' | |
2 | 2 | WarningsAsErrors: '*' |
3 | 3 | CheckOptions: |
4 | 4 | - key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic |
8 | 8 | |
9 | 9 | strategy: |
10 | 10 | matrix: |
11 | image: ["centos:7", "ubuntu:14.04", "ubuntu:16.04", "ubuntu:21.10"] | |
11 | image: ["centos:7", "ubuntu:14.04", "ubuntu:16.04", "ubuntu:22.04"] | |
12 | 12 | fail-fast: false # Prefer quick result |
13 | 13 | |
14 | 14 | runs-on: ubuntu-20.04 |
22 | 22 | - name: Install missing software on CentOS 7 |
23 | 23 | if: matrix.image == 'centos:7' |
24 | 24 | run: | |
25 | yum install -y cmake gcc-c++ make | |
25 | yum install -y cmake gcc-c++ make which python3 | |
26 | 26 | yum install -y pcre-devel |
27 | 27 | |
28 | 28 | - name: Install missing software on ubuntu |
29 | 29 | if: matrix.image != 'centos:7' |
30 | 30 | run: | |
31 | 31 | apt-get update |
32 | apt-get install -y cmake g++ make python libxml2-utils | |
32 | apt-get install -y cmake g++ make python3 libxml2-utils | |
33 | 33 | apt-get install -y libpcre3-dev |
34 | 34 | |
35 | 35 | # tests require CMake 3.4 |
36 | 36 | - name: Test CMake build (no tests) |
37 | if: matrix.image != 'ubuntu:21.10' | |
37 | if: matrix.image != 'ubuntu:22.04' | |
38 | 38 | run: | |
39 | 39 | mkdir cmake.output |
40 | 40 | cd cmake.output |
43 | 43 | cd .. |
44 | 44 | |
45 | 45 | - name: Test CMake build |
46 | if: matrix.image == 'ubuntu:21.10' | |
46 | if: matrix.image == 'ubuntu:22.04' | |
47 | 47 | run: | |
48 | 48 | mkdir cmake.output |
49 | 49 | cd cmake.output |
9 | 9 | runs-on: ubuntu-20.04 |
10 | 10 | |
11 | 11 | container: |
12 | image: "ubuntu:21.10" | |
12 | image: "ubuntu:22.04" | |
13 | 13 | |
14 | 14 | env: |
15 | 15 | ASAN_OPTIONS: detect_stack_use_after_return=1 |
26 | 26 | run: | |
27 | 27 | apt-get update |
28 | 28 | apt-get install -y make libpcre3-dev |
29 | apt-get install -y clang-13 | |
29 | apt-get install -y clang-14 | |
30 | 30 | |
31 | 31 | - name: Build |
32 | 32 | run: make -j$(nproc) cppcheck testrunner HAVE_RULES=yes MATCHCOMPILER=yes VERIFY=1 |
33 | 33 | env: |
34 | CC: clang-13 | |
35 | CXX: clang++-13 | |
34 | CC: clang-14 | |
35 | CXX: clang++-14 | |
36 | 36 | CXXFLAGS: "-fsanitize=address -O2 -g3 -DCPPCHK_GLIBCXX_DEBUG" |
37 | 37 | CPPFLAGS: "-DCHECK_INTERNAL" |
38 | 38 |
9 | 9 | runs-on: ubuntu-20.04 |
10 | 10 | |
11 | 11 | container: |
12 | image: "ubuntu:21.10" | |
12 | image: "ubuntu:22.04" | |
13 | 13 | |
14 | 14 | env: |
15 | 15 | QT_VERSION: 5.15.2 |
20 | 20 | - name: Install missing software |
21 | 21 | run: | |
22 | 22 | apt-get update |
23 | apt-get install -y cmake clang-13 make | |
23 | apt-get install -y cmake clang-14 make | |
24 | 24 | apt-get install -y libpcre3-dev |
25 | 25 | apt-get install -y libffi7 # work around missing dependency for Qt install step |
26 | apt-get install -y clang-tidy-13 | |
26 | apt-get install -y clang-tidy-14 | |
27 | 27 | |
28 | 28 | - name: Cache Qt ${{ env.QT_VERSION }} |
29 | 29 | id: cache-qt |
47 | 47 | cmake -G "Unix Makefiles" -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=On -DWITH_QCHART=On -DCMAKE_GLOBAL_AUTOGEN_TARGET=On -DCPPCHK_GLIBCXX_DEBUG=Off .. |
48 | 48 | cd .. |
49 | 49 | env: |
50 | CC: clang-13 | |
51 | CXX: clang++-13 | |
50 | CC: clang-14 | |
51 | CXX: clang++-14 | |
52 | 52 | |
53 | 53 | - name: Prepare CMake dependencies |
54 | 54 | run: | |
9 | 9 | runs-on: ubuntu-20.04 |
10 | 10 | |
11 | 11 | container: |
12 | image: "ubuntu:21.10" | |
12 | image: "ubuntu:22.04" | |
13 | 13 | |
14 | 14 | env: |
15 | 15 | UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1 |
26 | 26 | run: | |
27 | 27 | apt-get update |
28 | 28 | apt-get install -y make libpcre3-dev |
29 | apt-get install -y clang-13 | |
29 | apt-get install -y clang-14 | |
30 | 30 | |
31 | 31 | - name: Build |
32 | 32 | run: make -j$(nproc) cppcheck testrunner HAVE_RULES=yes MATCHCOMPILER=yes VERIFY=1 |
33 | 33 | env: |
34 | CC: clang-13 | |
35 | CXX: clang++-13 | |
34 | CC: clang-14 | |
35 | CXX: clang++-14 | |
36 | 36 | CXXFLAGS: "-fsanitize=undefined -fsanitize=nullability -fno-sanitize=signed-integer-overflow -O2 -g3 -DCPPCHK_GLIBCXX_DEBUG" |
37 | 37 | CPPFLAGS: "-DCHECK_INTERNAL" |
38 | 38 |
11 | 11 | endif |
12 | 12 | ifeq ($(MATCHCOMPILER),yes) |
13 | 13 | # Find available Python interpreter |
14 | ifndef PYTHON_INTERPRETER | |
14 | ifeq ($(PYTHON_INTERPRETER),) | |
15 | 15 | PYTHON_INTERPRETER := $(shell which python3) |
16 | 16 | endif |
17 | ifndef PYTHON_INTERPRETER | |
17 | ifeq ($(PYTHON_INTERPRETER),) | |
18 | 18 | PYTHON_INTERPRETER := $(shell which python) |
19 | 19 | endif |
20 | ifndef PYTHON_INTERPRETER | |
20 | ifeq ($(PYTHON_INTERPRETER),) | |
21 | 21 | $(error Did not find a Python interpreter) |
22 | 22 | endif |
23 | 23 | ifdef VERIFY |
95 | 95 | CPPCHK_GLIBCXX_DEBUG= |
96 | 96 | endif |
97 | 97 | ifndef CXXFLAGS |
98 | CXXFLAGS=-std=c++0x -O2 -DNDEBUG -Wall -Wno-sign-compare | |
98 | 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 $(CPPCHK_GLIBCXX_DEBUG) -g | |
99 | 99 | endif |
100 | 100 | |
101 | 101 | ifeq (g++, $(findstring g++,$(CXX))) |
303 | 303 | ./dmake |
304 | 304 | |
305 | 305 | clean: |
306 | rm -f build/*.o lib/*.o cli/*.o test/*.o tools/*.o externals/*/*.o testrunner dmake cppcheck cppcheck.exe cppcheck.1 | |
306 | rm -f build/*.cpp build/*.o lib/*.o cli/*.o test/*.o tools/*.o externals/*/*.o testrunner dmake cppcheck cppcheck.exe cppcheck.1 | |
307 | 307 | |
308 | 308 | man: man/cppcheck.1 |
309 | 309 | |
382 | 382 | xmllint --noout --relaxng cppcheck-errors.rng /tmp/example.xml |
383 | 383 | |
384 | 384 | checkCWEEntries: /tmp/errorlist.xml |
385 | ./tools/listErrorsWithoutCWE.py -F /tmp/errorlist.xml | |
385 | $(eval PYTHON_INTERPRETER := $(if $(PYTHON_INTERPRETER),$(PYTHON_INTERPRETER),$(shell which python3))) | |
386 | $(eval PYTHON_INTERPRETER := $(if $(PYTHON_INTERPRETER),$(PYTHON_INTERPRETER),$(shell which python))) | |
387 | $(eval PYTHON_INTERPRETER := $(if $(PYTHON_INTERPRETER),$(PYTHON_INTERPRETER),$(error Did not find a Python interpreter))) | |
388 | $(PYTHON_INTERPRETER) tools/listErrorsWithoutCWE.py -F /tmp/errorlist.xml | |
386 | 389 | .PHONY: validateRules |
387 | 390 | validateRules: |
388 | 391 | xmllint --noout rules/*.xml |
3489 | 3489 | <not-uninit/> |
3490 | 3490 | <not-bool/> |
3491 | 3491 | </arg> |
3492 | </function> | |
3493 | <!-- https://man7.org/linux/man-pages/man3/lsearch.3.html --> | |
3494 | <!-- void *lfind(const void *key, const void *base, size_t *nmemb, size_t size, int(*compar)(const void *, const void *)); --> | |
3495 | <function name="lfind"> | |
3496 | <returnValue type="void *"/> | |
3497 | <noreturn>false</noreturn> | |
3498 | <arg nr="1" direction="in"> | |
3499 | <not-uninit/> | |
3500 | <not-null/> | |
3501 | </arg> | |
3502 | <arg nr="2" direction="in"> | |
3503 | <not-uninit/> | |
3504 | <not-null/> | |
3505 | </arg> | |
3506 | <arg nr="3" direction="in"> | |
3507 | <not-uninit/> | |
3508 | <not-null/> | |
3509 | <minsize type="argvalue" arg="4"/> | |
3510 | </arg> | |
3511 | <arg nr="4" direction="in"> | |
3512 | <not-uninit/> | |
3513 | <not-bool/> | |
3514 | <valid>1:</valid> | |
3515 | </arg> | |
3516 | <arg nr="5" direction="in"> | |
3517 | <not-uninit/> | |
3518 | <not-null/> | |
3519 | </arg> | |
3520 | </function> | |
3521 | <!-- https://man7.org/linux/man-pages/man3/lsearch.3.html --> | |
3522 | <!-- void *lsearch(const void *key, void *base, size_t *nmemb, size_t size, int(*compar)(const void *, const void *)); --> | |
3523 | <function name="lsearch"> | |
3524 | <returnValue type="void *"/> | |
3525 | <noreturn>false</noreturn> | |
3526 | <arg nr="1" direction="in"> | |
3527 | <not-uninit/> | |
3528 | <not-null/> | |
3529 | </arg> | |
3530 | <arg nr="2" direction="in"> | |
3531 | <not-null/> | |
3532 | </arg> | |
3533 | <arg nr="3" direction="in"> | |
3534 | <not-uninit/> | |
3535 | <not-null/> | |
3536 | <minsize type="argvalue" arg="4"/> | |
3537 | </arg> | |
3538 | <arg nr="4" direction="in"> | |
3539 | <not-uninit/> | |
3540 | <not-bool/> | |
3541 | <valid>1:</valid> | |
3542 | </arg> | |
3543 | <arg nr="5" direction="in"> | |
3544 | <not-uninit/> | |
3545 | <not-null/> | |
3546 | </arg> | |
3547 | </function> | |
3548 | <!-- https://man7.org/linux/man-pages/man3/mbsnrtowcs.3.html --> | |
3549 | <!-- size_t mbsnrtowcs(wchar_t *restrict dest, const char **restrict src, size_t nms, size_t len, mbstate_t *restrict ps) --> | |
3550 | <function name="mbsnrtowcs"> | |
3551 | <returnValue type="size_t"/> | |
3552 | <use-retval/> | |
3553 | <noreturn>false</noreturn> | |
3554 | <!-- If dest is NULL, len is ignored, and the conversion proceeds as | |
3555 | above, except that the converted wide characters are not written | |
3556 | out to memory, and that no length limit exists. --> | |
3557 | <arg nr="1" direction="out"/> | |
3558 | <arg nr="2" direction="in"> | |
3559 | <not-uninit/> | |
3560 | <not-null/> | |
3561 | <minsize type="argvalue" arg="3"/> | |
3562 | </arg> | |
3563 | <arg nr="3" direction="in"> | |
3564 | <not-uninit/> | |
3565 | <not-null/> | |
3566 | <valid>0:</valid> | |
3567 | </arg> | |
3568 | <arg nr="4" direction="in"> | |
3569 | <not-uninit/> | |
3570 | <not-bool/> | |
3571 | <valid>0:</valid> | |
3572 | </arg> | |
3573 | <!-- In both of the above cases, if ps is NULL, a static anonymous | |
3574 | state known only to the mbsrtowcs() function is used instead. --> | |
3575 | <arg nr="5" direction="in"/> | |
3492 | 3576 | </function> |
3493 | 3577 | <!-- https://pubs.opengroup.org/onlinepubs/9699919799/functions/regcomp.html --> |
3494 | 3578 | <!-- int regcomp(regex_t *restrict preg, const char *restrict pattern, int cflags); --> |
7312 | 7312 | </arg> |
7313 | 7313 | <arg nr="3" direction="in" default=""> |
7314 | 7314 | <not-uninit/> |
7315 | </arg> | |
7316 | </function> | |
7317 | <!-- template< class ForwardIt > std::pair<ForwardIt,ForwardIt> std::minmax_element( ForwardIt first, ForwardIt last ) --> | |
7318 | <!-- template< class ForwardIt > std::pair<ForwardIt,ForwardIt> std::minmax( ForwardIt first, ForwardIt last ) --> | |
7319 | <function name="std::minmax_element,std::minmax"> | |
7320 | <use-retval/> | |
7321 | <noreturn>false</noreturn> | |
7322 | <arg nr="1" direction="in"> | |
7323 | <not-uninit/> | |
7324 | <iterator container="1" type="first"/> | |
7325 | </arg> | |
7326 | <arg nr="2" direction="in"> | |
7327 | <not-uninit/> | |
7328 | <iterator container="1" type="last"/> | |
7315 | 7329 | </arg> |
7316 | 7330 | </function> |
7317 | 7331 | <!-- template<class InputIterator> typename iterator_traits<InputIterator>::difference_type std::distance (InputIterator first, InputIterator last); --> |
19 | 19 | /** |
20 | 20 | * |
21 | 21 | * @mainpage Cppcheck |
22 | * @version 2.8 | |
22 | * @version 2.7 | |
23 | 23 | * |
24 | 24 | * @section overview_sec Overview |
25 | 25 | * Cppcheck is a simple tool for static analysis of C/C++ code. |
6 | 6 | endif() |
7 | 7 | message(STATUS "NPROC=${NPROC}") |
8 | 8 | |
9 | find_program(RUN_CLANG_TIDY NAMES run-clang-tidy run-clang-tidy-13 run-clang-tidy-12 run-clang-tidy-11 run-clang-tidy-10 run-clang-tidy-9 run-clang-tidy-8) | |
9 | find_program(RUN_CLANG_TIDY NAMES run-clang-tidy run-clang-tidy-14 run-clang-tidy-13 run-clang-tidy-12 run-clang-tidy-11 run-clang-tidy-10 run-clang-tidy-9 run-clang-tidy-8) | |
10 | 10 | message(STATUS "RUN_CLANG_TIDY=${RUN_CLANG_TIDY}") |
11 | 11 | if (RUN_CLANG_TIDY) |
12 | 12 | # disable all compiler warnings since we are just interested in the tidy ones |
50 | 50 | add_compile_options(-Wsuggest-attribute=noreturn) |
51 | 51 | add_compile_options(-Wno-shadow) # whenever a local variable or type declaration shadows another one |
52 | 52 | elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") |
53 | if (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 14) | |
54 | if (CMAKE_BUILD_TYPE MATCHES "Release" OR CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo") | |
55 | # work around performance regression - see https://github.com/llvm/llvm-project/issues/53555 | |
56 | add_compile_options(-mllvm -inline-deferral) | |
57 | endif() | |
58 | ||
59 | # use force DWARF 4 debug format since not all tools might be able to handle DWARF 5 yet - e.g. valgrind on ubuntu 20.04 | |
60 | add_compile_options(-gdwarf-4) | |
61 | endif() | |
53 | 62 | |
54 | 63 | add_compile_options_safe(-Wno-documentation-unknown-command) |
55 | 64 | |
85 | 94 | add_compile_options_safe(-Wno-tautological-type-limit-compare) |
86 | 95 | add_compile_options_safe(-Wno-unused-member-function) |
87 | 96 | add_compile_options(-Wno-disabled-macro-expansion) |
97 | add_compile_options_safe(-Wno-bitwise-instead-of-logical) # TODO: fix these | |
88 | 98 | |
89 | 99 | # warnings we are not interested in |
90 | 100 | add_compile_options(-Wno-four-char-constants) |
0 | 0 | # Version for libraries CPP |
1 | SET(VERSION "2.8") | |
1 | SET(VERSION "2.7") | |
2 | 2 | STRING(REGEX MATCHALL "[0-9]" VERSION_PARTS "${VERSION}") |
3 | 3 | LIST(GET VERSION_PARTS 0 VERSION_MAJOR) |
4 | 4 | LIST(GET VERSION_PARTS 1 VERSION_MINOR) |
724 | 724 | |
725 | 725 | const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); |
726 | 726 | for (const Variable* var : symbolDatabase->variableList()) { |
727 | if (!var || !var->isLocal() || var->isStatic() || var->isReference()) | |
727 | if (!var || (!var->isLocal() && !(var->isArgument() && var->scope())) || var->isStatic() || var->isReference()) | |
728 | 728 | continue; |
729 | 729 | if (var->typeEndToken()->isStandardType()) |
730 | 730 | continue; |
788 | 788 | return deallocated; |
789 | 789 | }; |
790 | 790 | |
791 | for (const Token *tok2 = variable->nameToken(); tok2 && tok2 != variable->scope()->bodyEnd; tok2 = tok2->next()) { | |
791 | // return { memberTok, rhsTok } | |
792 | auto isMemberAssignment = [](const Token* varTok, int varId) -> std::pair<const Token*, const Token*> { | |
793 | if (varTok->varId() != varId) | |
794 | return {}; | |
795 | const Token* top = varTok; | |
796 | while (top->astParent()) { | |
797 | if (top->astParent()->str() == "(") | |
798 | return {}; | |
799 | top = top->astParent(); | |
800 | } | |
801 | if (!Token::simpleMatch(top, "=") || !precedes(varTok, top)) | |
802 | return {}; | |
803 | const Token* dot = top->astOperand1(); | |
804 | while (dot && dot->str() != ".") | |
805 | dot = dot->astOperand1(); | |
806 | if (!dot) | |
807 | return {}; | |
808 | return { dot->astOperand2(), top->next() }; | |
809 | }; | |
810 | std::pair<const Token*, const Token*> assignToks; | |
811 | ||
812 | const Token* tokStart = variable->nameToken(); | |
813 | if (variable->isArgument() && variable->scope()) | |
814 | tokStart = variable->scope()->bodyStart->next(); | |
815 | for (const Token *tok2 = tokStart; tok2 && tok2 != variable->scope()->bodyEnd; tok2 = tok2->next()) { | |
792 | 816 | if (tok2->str() == "{") |
793 | 817 | ++indentlevel2; |
794 | 818 | |
804 | 828 | break; |
805 | 829 | |
806 | 830 | // Struct member is allocated => check if it is also properly deallocated.. |
807 | else if (Token::Match(tok2->previous(), "[;{}] %varid% . %var% =", variable->declarationId())) { | |
808 | if (getAllocationType(tok2->tokAt(4), tok2->tokAt(2)->varId()) == AllocType::No) | |
831 | else if ((assignToks = isMemberAssignment(tok2, variable->declarationId())).first && assignToks.first->varId()) { | |
832 | if (getAllocationType(assignToks.second, assignToks.first->varId()) == AllocType::No) | |
809 | 833 | continue; |
810 | 834 | |
811 | 835 | const int structid(variable->declarationId()); |
812 | const int structmemberid(tok2->tokAt(2)->varId()); | |
836 | const int structmemberid(assignToks.first->varId()); | |
813 | 837 | |
814 | 838 | // This struct member is allocated.. check that it is deallocated |
815 | 839 | int indentlevel3 = indentlevel2; |
1177 | 1177 | size = settings->sizeof_pointer; |
1178 | 1178 | else if (var.type() && var.type()->classScope) |
1179 | 1179 | size = estimateSize(var.type(), settings, symbolDatabase, recursionDepth+1); |
1180 | else if (var.valueType()->type == ValueType::Type::CONTAINER) | |
1180 | else if (var.valueType() && var.valueType()->type == ValueType::Type::CONTAINER) | |
1181 | 1181 | size = 3 * settings->sizeof_pointer; // Just guess |
1182 | 1182 | else |
1183 | 1183 | size = symbolDatabase->sizeOfType(var.typeStartToken()); |
1599 | 1599 | const int indirect = p->isArray() ? p->dimensions().size() : 1; |
1600 | 1600 | if (isVariableChanged(start, p->scope()->bodyEnd, indirect, p->declarationId(), false, mSettings, mTokenizer->isCPP())) |
1601 | 1601 | continue; |
1602 | if (p->isArgument() && p->typeStartToken() && p->typeStartToken()->isSimplifiedTypedef() && !(Token::simpleMatch(p->typeEndToken(), "*") && !p->typeEndToken()->isSimplifiedTypedef())) | |
1603 | continue; | |
1602 | 1604 | constVariableError(p, nullptr); |
1603 | 1605 | } |
1604 | 1606 | } |
1614 | 1616 | |
1615 | 1617 | const std::string vartype(var->isArgument() ? "Parameter" : "Variable"); |
1616 | 1618 | const std::string varname(var->name()); |
1619 | const std::string ptrRefArray = var->isArray() ? "const array" : (var->isPointer() ? "pointer to const" : "reference to const"); | |
1617 | 1620 | |
1618 | 1621 | ErrorPath errorPath; |
1619 | 1622 | std::string id = "const" + vartype; |
1620 | std::string message = "$symbol:" + varname + "\n" + vartype + " '$symbol' can be declared with const"; | |
1623 | std::string message = "$symbol:" + varname + "\n" + vartype + " '$symbol' can be declared as " + ptrRefArray; | |
1621 | 1624 | errorPath.push_back(ErrorPathItem(var ? var->nameToken() : nullptr, message)); |
1622 | 1625 | if (var && var->isArgument() && function && function->functionPointerUsage) { |
1623 | 1626 | errorPath.push_front(ErrorPathItem(function->functionPointerUsage, "You might need to cast the function pointer here")); |
601 | 601 | setFlag(fIncompleteVar, b); |
602 | 602 | } |
603 | 603 | |
604 | bool isSimplifiedTypedef() const { | |
605 | return getFlag(fIsSimplifiedTypedef); | |
606 | } | |
607 | void isSimplifiedTypedef(bool b) { | |
608 | setFlag(fIsSimplifiedTypedef, b); | |
609 | } | |
610 | ||
604 | 611 | bool isIncompleteConstant() const { |
605 | 612 | return getFlag(fIsIncompleteConstant); |
606 | 613 | } |
648 | 655 | } |
649 | 656 | void isInline(bool b) { |
650 | 657 | setFlag(fIsInline, b); |
658 | } | |
659 | ||
660 | bool isRestrict() const { | |
661 | return getFlag(fIsRestrict); | |
662 | } | |
663 | void isRestrict(bool b) { | |
664 | setFlag(fIsRestrict, b); | |
651 | 665 | } |
652 | 666 | |
653 | 667 | bool isRemovedVoidParameter() const { |
1276 | 1290 | fIsSimplifedScope = (1ULL << 34), // scope added when simplifying e.g. if (int i = ...; ...) |
1277 | 1291 | fIsRemovedVoidParameter = (1ULL << 35), // A void function parameter has been removed |
1278 | 1292 | fIsIncompleteConstant = (1ULL << 36), |
1293 | fIsRestrict = (1ULL << 37), // Is this a restrict pointer type | |
1294 | fIsSimplifiedTypedef = (1ULL << 38), | |
1279 | 1295 | }; |
1280 | 1296 | |
1281 | 1297 | Token::Type mTokType; |
1377 | 1377 | typeEnd = typeStart; |
1378 | 1378 | |
1379 | 1379 | // start substituting at the typedef name by replacing it with the type |
1380 | Token* replStart = tok2; // track first replaced token | |
1381 | for (Token* tok3 = typeStart; tok3->str() != ";"; tok3 = tok3->next()) | |
1382 | tok3->isSimplifiedTypedef(true); | |
1380 | 1383 | tok2->str(typeStart->str()); |
1381 | 1384 | |
1382 | 1385 | // restore qualification if it was removed |
1385 | 1388 | tok2 = tok2->previous(); |
1386 | 1389 | |
1387 | 1390 | if (globalScope) { |
1388 | tok2->insertToken("::"); | |
1391 | replStart = tok2->insertToken("::"); | |
1389 | 1392 | tok2 = tok2->next(); |
1390 | 1393 | } |
1391 | 1394 | |
1414 | 1417 | startIdx = spaceIdx + 1; |
1415 | 1418 | } |
1416 | 1419 | tok2->previous()->insertToken(removed1.substr(startIdx)); |
1417 | tok2->previous()->insertToken("::"); | |
1420 | replStart = tok2->previous()->insertToken("::"); | |
1418 | 1421 | break; |
1419 | 1422 | } |
1420 | 1423 | idx = removed1.rfind(" ::"); |
1424 | 1427 | removed1.resize(idx); |
1425 | 1428 | } |
1426 | 1429 | } |
1430 | replStart->isSimplifiedTypedef(true); | |
1431 | Token* constTok = Token::simpleMatch(tok2->previous(), "const") ? tok2->previous() : nullptr; | |
1427 | 1432 | // add remainder of type |
1428 | 1433 | tok2 = TokenList::copyTokens(tok2, typeStart->next(), typeEnd); |
1429 | 1434 | |
1430 | 1435 | if (!pointers.empty()) { |
1431 | 1436 | for (const std::string &p : pointers) { |
1432 | 1437 | tok2->insertToken(p); |
1438 | tok2->isSimplifiedTypedef(true); | |
1439 | tok2 = tok2->next(); | |
1440 | } | |
1441 | if (constTok) { | |
1442 | constTok->deleteThis(); | |
1443 | tok2->insertToken("const"); | |
1444 | tok2->isSimplifiedTypedef(true); | |
1433 | 1445 | tok2 = tok2->next(); |
1434 | 1446 | } |
1435 | 1447 | } |
3749 | 3761 | for (Token *tok = list.front(); tok; tok = tok->next()) { |
3750 | 3762 | if (tok->isOp()) |
3751 | 3763 | continue; |
3764 | if (isCPP() && Token::simpleMatch(tok, "template <")) { | |
3765 | Token* closingBracket = tok->next()->findClosingBracket(); | |
3766 | if (closingBracket) | |
3767 | tok = closingBracket; | |
3768 | continue; | |
3769 | } | |
3770 | ||
3752 | 3771 | if (tok == functionDeclEndToken) { |
3753 | 3772 | functionDeclEndStack.pop(); |
3754 | 3773 | functionDeclEndToken = functionDeclEndStack.empty() ? nullptr : functionDeclEndStack.top(); |
5490 | 5509 | out << " isImplicitInt=\"true\""; |
5491 | 5510 | if (tok->isComplex()) |
5492 | 5511 | out << " isComplex=\"true\""; |
5512 | if (tok->isRestrict()) | |
5513 | out << " isRestrict=\"true\""; | |
5493 | 5514 | if (tok->link()) |
5494 | 5515 | out << " link=\"" << tok->link() << '\"'; |
5495 | 5516 | if (tok->varId() > 0) |
11241 | 11262 | if (keywords.find(tok->str()) != keywords.end()) { |
11242 | 11263 | // Don't remove struct members |
11243 | 11264 | if (!Token::simpleMatch(tok->previous(), ".")) { |
11244 | if (tok->str().find("inline") != std::string::npos) { | |
11245 | Token *temp = tok->next(); | |
11246 | while (temp != nullptr && Token::Match(temp, "%name%")) { | |
11247 | temp->isInline(true); | |
11248 | temp = temp->next(); | |
11265 | const bool isinline = (tok->str().find("inline") != std::string::npos); | |
11266 | const bool isrestrict = (tok->str().find("restrict") != std::string::npos); | |
11267 | if (isinline || isrestrict) { | |
11268 | for (Token *temp = tok->next(); Token::Match(temp, "%name%"); temp = temp->next()) { | |
11269 | if (isinline) | |
11270 | temp->isInline(true); | |
11271 | if (isrestrict) | |
11272 | temp->isRestrict(true); | |
11249 | 11273 | } |
11250 | 11274 | } |
11251 | 11275 | tok->deleteThis(); // Simplify.. |
11263 | 11287 | tok->deleteNext(); |
11264 | 11288 | |
11265 | 11289 | if (c99) { |
11266 | while (tok->str() == "restrict") | |
11290 | if (tok->str() == "restrict") { | |
11291 | for (Token *temp = tok->next(); Token::Match(temp, "%name%"); temp = temp->next()) { | |
11292 | temp->isRestrict(true); | |
11293 | } | |
11267 | 11294 | tok->deleteThis(); |
11295 | } | |
11268 | 11296 | |
11269 | 11297 | if (mSettings->standards.c >= Standards::C11) { |
11270 | 11298 | while (tok->str() == "_Atomic") |
936 | 936 | } |
937 | 937 | } |
938 | 938 | |
939 | // Array element | |
940 | else if (parent->str() == "[" && parent->isBinaryOp()) { | |
941 | for (const ValueFlow::Value &value1 : parent->astOperand1()->values()) { | |
942 | if (!value1.isTokValue()) | |
943 | continue; | |
944 | for (const ValueFlow::Value &value2 : parent->astOperand2()->values()) { | |
945 | if (!value2.isIntValue()) | |
946 | continue; | |
947 | if (value1.varId == 0 || value2.varId == 0 || | |
948 | (value1.varId == value2.varId && value1.varvalue == value2.varvalue)) { | |
949 | ValueFlow::Value result(0); | |
950 | result.condition = value1.condition ? value1.condition : value2.condition; | |
951 | result.setInconclusive(value1.isInconclusive() | value2.isInconclusive()); | |
952 | result.varId = (value1.varId != 0) ? value1.varId : value2.varId; | |
953 | result.varvalue = (result.varId == value1.varId) ? value1.intvalue : value2.intvalue; | |
954 | if (value1.valueKind == value2.valueKind) | |
955 | result.valueKind = value1.valueKind; | |
956 | if (value1.tokvalue->tokType() == Token::eString) { | |
957 | const std::string s = value1.tokvalue->strValue(); | |
958 | const MathLib::bigint index = value2.intvalue; | |
959 | if (index == s.size()) { | |
960 | result.intvalue = 0; | |
961 | setTokenValue(parent, result, settings); | |
962 | } else if (index >= 0 && index < s.size()) { | |
963 | result.intvalue = s[index]; | |
964 | setTokenValue(parent, result, settings); | |
965 | } | |
966 | } else if (value1.tokvalue->str() == "{") { | |
967 | MathLib::bigint index = value2.intvalue; | |
968 | const Token *element = value1.tokvalue->next(); | |
969 | while (index > 0 && element->str() != "}") { | |
970 | if (element->str() == ",") | |
971 | --index; | |
972 | if (Token::Match(element, "[{}()[]]")) | |
973 | break; | |
974 | element = element->next(); | |
975 | } | |
976 | if (Token::Match(element, "%num% [,}]")) { | |
977 | result.intvalue = MathLib::toLongNumber(element->str()); | |
978 | setTokenValue(parent, result, settings); | |
979 | } | |
980 | } | |
981 | } | |
982 | } | |
983 | } | |
984 | } | |
985 | ||
986 | 939 | else if (Token::Match(parent, ":: %name%") && parent->astOperand2() == tok) { |
987 | 940 | setTokenValue(parent, value, settings); |
988 | 941 | } |
1404 | 1357 | } |
1405 | 1358 | } |
1406 | 1359 | |
1360 | static void valueFlowArrayElement(TokenList* tokenlist, const Settings* settings) | |
1361 | { | |
1362 | for (Token* tok = tokenlist->front(); tok; tok = tok->next()) { | |
1363 | if (!Token::simpleMatch(tok, "[")) | |
1364 | continue; | |
1365 | if (!tok->isBinaryOp()) | |
1366 | continue; | |
1367 | if (tok->hasKnownIntValue()) | |
1368 | continue; | |
1369 | const Token* indexTok = tok->astOperand2(); | |
1370 | const Token* arrayTok = tok->astOperand1(); | |
1371 | ||
1372 | for (const ValueFlow::Value& arrayValue : arrayTok->values()) { | |
1373 | if (!arrayValue.isTokValue()) | |
1374 | continue; | |
1375 | for (const ValueFlow::Value& indexValue : indexTok->values()) { | |
1376 | if (!indexValue.isIntValue()) | |
1377 | continue; | |
1378 | if (arrayValue.varId != 0 && indexValue.varId != 0 && | |
1379 | !(arrayValue.varId == indexValue.varId && arrayValue.varvalue == indexValue.varvalue)) | |
1380 | continue; | |
1381 | ||
1382 | ValueFlow::Value result(0); | |
1383 | result.condition = arrayValue.condition ? arrayValue.condition : indexValue.condition; | |
1384 | result.setInconclusive(arrayValue.isInconclusive() | indexValue.isInconclusive()); | |
1385 | result.varId = (arrayValue.varId != 0) ? arrayValue.varId : indexValue.varId; | |
1386 | result.varvalue = (result.varId == arrayValue.varId) ? arrayValue.intvalue : indexValue.intvalue; | |
1387 | if (arrayValue.valueKind == indexValue.valueKind) | |
1388 | result.valueKind = arrayValue.valueKind; | |
1389 | ||
1390 | result.errorPath.insert(result.errorPath.end(), arrayValue.errorPath.begin(), arrayValue.errorPath.end()); | |
1391 | result.errorPath.insert(result.errorPath.end(), indexValue.errorPath.begin(), indexValue.errorPath.end()); | |
1392 | ||
1393 | const MathLib::bigint index = indexValue.intvalue; | |
1394 | ||
1395 | if (arrayValue.tokvalue->tokType() == Token::eString) { | |
1396 | const std::string s = arrayValue.tokvalue->strValue(); | |
1397 | if (index == s.size()) { | |
1398 | result.intvalue = 0; | |
1399 | setTokenValue(tok, result, settings); | |
1400 | } else if (index >= 0 && index < s.size()) { | |
1401 | result.intvalue = s[index]; | |
1402 | setTokenValue(tok, result, settings); | |
1403 | } | |
1404 | } else if (Token::simpleMatch(arrayValue.tokvalue, "{")) { | |
1405 | std::vector<const Token*> args = getArguments(arrayValue.tokvalue); | |
1406 | if (index < 0 || index >= args.size()) | |
1407 | continue; | |
1408 | const Token* arg = args[index]; | |
1409 | if (!arg->hasKnownIntValue()) | |
1410 | continue; | |
1411 | const ValueFlow::Value& v = arg->values().front(); | |
1412 | result.intvalue = v.intvalue; | |
1413 | result.errorPath.insert(result.errorPath.end(), v.errorPath.begin(), v.errorPath.end()); | |
1414 | setTokenValue(tok, result, settings); | |
1415 | } | |
1416 | } | |
1417 | } | |
1418 | } | |
1419 | } | |
1420 | ||
1407 | 1421 | static void valueFlowPointerAlias(TokenList *tokenlist) |
1408 | 1422 | { |
1409 | 1423 | for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { |
1812 | 1826 | } |
1813 | 1827 | } |
1814 | 1828 | |
1829 | ValuePtr<Analyzer> makeAnalyzer(const Token* exprTok, ValueFlow::Value value, const TokenList* tokenlist); | |
1830 | ValuePtr<Analyzer> makeReverseAnalyzer(const Token* exprTok, const ValueFlow::Value& value, const TokenList* tokenlist); | |
1831 | ||
1832 | static Analyzer::Result valueFlowForward(Token* startToken, | |
1833 | const Token* endToken, | |
1834 | const Token* exprTok, | |
1835 | const ValueFlow::Value& value, | |
1836 | TokenList* const tokenlist) | |
1837 | { | |
1838 | return valueFlowGenericForward(startToken, endToken, makeAnalyzer(exprTok, value, tokenlist), tokenlist->getSettings()); | |
1839 | } | |
1840 | ||
1815 | 1841 | static Analyzer::Result valueFlowForward(Token* startToken, |
1816 | 1842 | const Token* endToken, |
1817 | 1843 | const Token* exprTok, |
1818 | 1844 | const std::list<ValueFlow::Value>& values, |
1819 | TokenList* const tokenlist, | |
1820 | const Settings* settings); | |
1821 | ||
1845 | TokenList* const tokenlist) | |
1846 | { | |
1847 | Analyzer::Result result{}; | |
1848 | for (const ValueFlow::Value& v : values) { | |
1849 | result.update(valueFlowForward(startToken, endToken, exprTok, v, tokenlist)); | |
1850 | } | |
1851 | return result; | |
1852 | } | |
1853 | ||
1854 | template<class ValueOrValues> | |
1855 | static Analyzer::Result valueFlowForward(Token* startToken, const Token* exprTok, const ValueOrValues& v, TokenList* tokenlist) | |
1856 | { | |
1857 | const Token* endToken = nullptr; | |
1858 | const Function* f = Scope::nestedInFunction(startToken->scope()); | |
1859 | if (f && f->functionScope) | |
1860 | endToken = f->functionScope->bodyEnd; | |
1861 | return valueFlowForward(startToken, endToken, exprTok, v, tokenlist); | |
1862 | } | |
1863 | ||
1864 | static Analyzer::Result valueFlowForwardRecursive(Token* top, | |
1865 | const Token* exprTok, | |
1866 | const std::list<ValueFlow::Value>& values, | |
1867 | TokenList* const tokenlist) | |
1868 | { | |
1869 | Analyzer::Result result{}; | |
1870 | for (const ValueFlow::Value& v : values) { | |
1871 | result.update(valueFlowGenericForward(top, makeAnalyzer(exprTok, v, tokenlist), tokenlist->getSettings())); | |
1872 | } | |
1873 | return result; | |
1874 | } | |
1875 | ||
1876 | static void valueFlowReverse(Token* tok, | |
1877 | const Token* const endToken, | |
1878 | const Token* const varToken, | |
1879 | const std::list<ValueFlow::Value>& values, | |
1880 | TokenList* tokenlist) | |
1881 | { | |
1882 | for (const ValueFlow::Value& v : values) { | |
1883 | valueFlowGenericReverse(tok, endToken, makeReverseAnalyzer(varToken, v, tokenlist), tokenlist->getSettings()); | |
1884 | } | |
1885 | } | |
1886 | ||
1887 | // Deprecated | |
1822 | 1888 | static void valueFlowReverse(TokenList* tokenlist, |
1823 | 1889 | Token* tok, |
1824 | 1890 | const Token* const varToken, |
1825 | 1891 | ValueFlow::Value val, |
1826 | 1892 | const ValueFlow::Value& val2, |
1827 | ErrorLogger* errorLogger, | |
1828 | const Settings* settings); | |
1893 | ErrorLogger* /*errorLogger*/, | |
1894 | const Settings* = nullptr) | |
1895 | { | |
1896 | std::list<ValueFlow::Value> values = {val}; | |
1897 | if (val2.varId != 0) | |
1898 | values.push_back(val2); | |
1899 | valueFlowReverse(tok, nullptr, varToken, values, tokenlist); | |
1900 | } | |
1829 | 1901 | |
1830 | 1902 | static bool isConditionKnown(const Token* tok, bool then) |
1831 | 1903 | { |
2632 | 2704 | } |
2633 | 2705 | }; |
2634 | 2706 | |
2635 | ValuePtr<Analyzer> makeAnalyzer(const Token* exprTok, ValueFlow::Value value, const TokenList* tokenlist); | |
2636 | ||
2637 | 2707 | struct SingleValueFlowAnalyzer : ValueFlowAnalyzer { |
2638 | 2708 | std::unordered_map<nonneg int, const Variable*> varids; |
2639 | 2709 | std::unordered_map<nonneg int, const Variable*> aliases; |
2928 | 2998 | return tok->next()->str() == varname; |
2929 | 2999 | } |
2930 | 3000 | }; |
2931 | ||
2932 | static Analyzer::Result valueFlowForwardExpression(Token* startToken, | |
2933 | const Token* endToken, | |
2934 | const Token* exprTok, | |
2935 | const std::list<ValueFlow::Value>& values, | |
2936 | const TokenList* const tokenlist, | |
2937 | const Settings* settings) | |
2938 | { | |
2939 | Analyzer::Result result{}; | |
2940 | for (const ValueFlow::Value& v : values) { | |
2941 | ExpressionAnalyzer a(exprTok, v, tokenlist); | |
2942 | result.update(valueFlowGenericForward(startToken, endToken, a, settings)); | |
2943 | } | |
2944 | return result; | |
2945 | } | |
2946 | ||
2947 | static const Token* parseBinaryIntOp(const Token* expr, | |
2948 | const std::function<std::vector<MathLib::bigint>(const Token*)>& eval, | |
2949 | MathLib::bigint& known) | |
2950 | { | |
2951 | if (!expr) | |
2952 | return nullptr; | |
2953 | if (!expr->astOperand1() || !expr->astOperand2()) | |
2954 | return nullptr; | |
2955 | if (expr->astOperand1()->exprId() == 0 && expr->astOperand2()->exprId() == 0) | |
2956 | return nullptr; | |
2957 | std::vector<MathLib::bigint> x1 = eval(expr->astOperand1()); | |
2958 | std::vector<MathLib::bigint> x2 = eval(expr->astOperand2()); | |
2959 | if (expr->astOperand1()->exprId() == 0 && x1.empty()) | |
2960 | return nullptr; | |
2961 | if (expr->astOperand2()->exprId() == 0 && x2.empty()) | |
2962 | return nullptr; | |
2963 | const Token* varTok = nullptr; | |
2964 | if (!x1.empty() && x2.empty()) { | |
2965 | varTok = expr->astOperand2(); | |
2966 | known = x1.front(); | |
2967 | } else if (x1.empty() && !x2.empty()) { | |
2968 | varTok = expr->astOperand1(); | |
2969 | known = x2.front(); | |
2970 | } | |
2971 | return varTok; | |
2972 | } | |
2973 | ||
2974 | const Token* solveExprValue(const Token* expr, | |
2975 | const std::function<std::vector<MathLib::bigint>(const Token*)>& eval, | |
2976 | ValueFlow::Value& value) | |
2977 | { | |
2978 | if (!value.isIntValue() && !value.isIteratorValue() && !value.isSymbolicValue()) | |
2979 | return expr; | |
2980 | if (value.isSymbolicValue() && !Token::Match(expr, "+|-")) | |
2981 | return expr; | |
2982 | MathLib::bigint intval; | |
2983 | const Token* binaryTok = parseBinaryIntOp(expr, eval, intval); | |
2984 | bool rhs = astIsRHS(binaryTok); | |
2985 | // If its on the rhs, then -1 multiplication is needed, which is not possible with simple delta analysis used currently for symbolic values | |
2986 | if (value.isSymbolicValue() && rhs && Token::simpleMatch(expr, "-")) | |
2987 | return expr; | |
2988 | if (binaryTok && expr->str().size() == 1) { | |
2989 | switch (expr->str()[0]) { | |
2990 | case '+': { | |
2991 | value.intvalue -= intval; | |
2992 | return solveExprValue(binaryTok, eval, value); | |
2993 | } | |
2994 | case '-': { | |
2995 | if (rhs) | |
2996 | value.intvalue = intval - value.intvalue; | |
2997 | else | |
2998 | value.intvalue += intval; | |
2999 | return solveExprValue(binaryTok, eval, value); | |
3000 | } | |
3001 | case '*': { | |
3002 | if (intval == 0) | |
3003 | break; | |
3004 | value.intvalue /= intval; | |
3005 | return solveExprValue(binaryTok, eval, value); | |
3006 | } | |
3007 | case '^': { | |
3008 | value.intvalue ^= intval; | |
3009 | return solveExprValue(binaryTok, eval, value); | |
3010 | } | |
3011 | } | |
3012 | } | |
3013 | return expr; | |
3014 | } | |
3015 | ||
3016 | static const Token* solveExprValue(const Token* expr, ValueFlow::Value& value) | |
3017 | { | |
3018 | return solveExprValue( | |
3019 | expr, | |
3020 | [](const Token* tok) -> std::vector<MathLib::bigint> { | |
3021 | if (tok->hasKnownIntValue()) | |
3022 | return {tok->values().front().intvalue}; | |
3023 | return {}; | |
3024 | }, | |
3025 | value); | |
3026 | } | |
3027 | ||
3028 | ValuePtr<Analyzer> makeAnalyzer(const Token* exprTok, ValueFlow::Value value, const TokenList* tokenlist) | |
3029 | { | |
3030 | const Token* expr = solveExprValue(exprTok, value); | |
3031 | return ExpressionAnalyzer(expr, value, tokenlist); | |
3032 | } | |
3033 | ||
3034 | static Analyzer::Result valueFlowForward(Token* startToken, | |
3035 | const Token* endToken, | |
3036 | const Token* exprTok, | |
3037 | const std::list<ValueFlow::Value>& values, | |
3038 | TokenList* const tokenlist, | |
3039 | const Settings* settings) | |
3040 | { | |
3041 | Analyzer::Result result{}; | |
3042 | for (const ValueFlow::Value& v : values) { | |
3043 | result.update(valueFlowGenericForward(startToken, endToken, makeAnalyzer(exprTok, v, tokenlist), settings)); | |
3044 | } | |
3045 | return result; | |
3046 | } | |
3047 | ||
3048 | static Analyzer::Result valueFlowForward(Token* top, | |
3049 | const Token* exprTok, | |
3050 | const std::list<ValueFlow::Value>& values, | |
3051 | TokenList* const tokenlist, | |
3052 | const Settings* settings) | |
3053 | { | |
3054 | Analyzer::Result result{}; | |
3055 | for (const ValueFlow::Value& v : values) { | |
3056 | result.update(valueFlowGenericForward(top, makeAnalyzer(exprTok, v, tokenlist), settings)); | |
3057 | } | |
3058 | return result; | |
3059 | } | |
3060 | ||
3061 | static void valueFlowReverse(Token* tok, | |
3062 | const Token* const endToken, | |
3063 | const Token* const varToken, | |
3064 | const std::list<ValueFlow::Value>& values, | |
3065 | TokenList* tokenlist, | |
3066 | const Settings* settings) | |
3067 | { | |
3068 | for (const ValueFlow::Value& v : values) { | |
3069 | ExpressionAnalyzer a(varToken, v, tokenlist); | |
3070 | valueFlowGenericReverse(tok, endToken, a, settings); | |
3071 | } | |
3072 | } | |
3073 | ||
3074 | static void valueFlowReverse(TokenList* tokenlist, | |
3075 | Token* tok, | |
3076 | const Token* const varToken, | |
3077 | ValueFlow::Value val, | |
3078 | const ValueFlow::Value& val2, | |
3079 | ErrorLogger* /*errorLogger*/, | |
3080 | const Settings* settings) | |
3081 | { | |
3082 | std::list<ValueFlow::Value> values = {val}; | |
3083 | if (val2.varId != 0) | |
3084 | values.push_back(val2); | |
3085 | valueFlowReverse(tok, nullptr, varToken, values, tokenlist, settings); | |
3086 | } | |
3087 | 3001 | |
3088 | 3002 | enum class LifetimeCapture { Undefined, ByValue, ByReference }; |
3089 | 3003 | |
3576 | 3490 | if (Token::Match(tok->previous(), "%var% {|(") && isVariableDecl(tok->previous())) { |
3577 | 3491 | std::list<ValueFlow::Value> values = tok->values(); |
3578 | 3492 | values.remove_if(&isNotLifetimeValue); |
3579 | valueFlowForward(nextAfterAstRightmostLeaf(tok), getEndOfExprScope(tok), tok->previous(), values, tokenlist, settings); | |
3493 | valueFlowForward(nextAfterAstRightmostLeaf(tok), getEndOfExprScope(tok), tok->previous(), values, tokenlist); | |
3580 | 3494 | return; |
3581 | 3495 | } |
3582 | 3496 | Token *parent = tok->astParent(); |
3616 | 3530 | const Token *nextExpression = nextAfterAstRightmostLeaf(parent); |
3617 | 3531 | |
3618 | 3532 | if (expr->exprId() > 0) { |
3619 | valueFlowForwardExpression(const_cast<Token*>(nextExpression), | |
3620 | endOfVarScope->next(), | |
3621 | expr, | |
3622 | values, | |
3623 | tokenlist, | |
3624 | settings); | |
3533 | valueFlowForward(const_cast<Token*>(nextExpression), endOfVarScope->next(), expr, values, tokenlist); | |
3625 | 3534 | |
3626 | 3535 | for (ValueFlow::Value& val : values) { |
3627 | 3536 | if (val.lifetimeKind == ValueFlow::Value::LifetimeKind::Address) |
3632 | 3541 | const Token* parentLifetime = |
3633 | 3542 | getParentLifetime(tokenlist->isCPP(), parent->astOperand1()->astOperand2(), &settings->library); |
3634 | 3543 | if (parentLifetime && parentLifetime->exprId() > 0) { |
3635 | valueFlowForward(const_cast<Token*>(nextExpression), | |
3636 | endOfVarScope, | |
3637 | parentLifetime, | |
3638 | values, | |
3639 | tokenlist, | |
3640 | settings); | |
3544 | valueFlowForward(const_cast<Token*>(nextExpression), endOfVarScope, parentLifetime, values, tokenlist); | |
3641 | 3545 | } |
3642 | 3546 | } |
3643 | 3547 | } |
3658 | 3562 | const Token *nextExpression = nextAfterAstRightmostLeaf(parent); |
3659 | 3563 | // Only forward lifetime values |
3660 | 3564 | values.remove_if(&isNotLifetimeValue); |
3661 | valueFlowForward(const_cast<Token*>(nextExpression), endOfVarScope, tok, values, tokenlist, settings); | |
3565 | valueFlowForward(const_cast<Token*>(nextExpression), endOfVarScope, tok, values, tokenlist); | |
3662 | 3566 | // Cast |
3663 | 3567 | } else if (parent->isCast()) { |
3664 | 3568 | std::list<ValueFlow::Value> values = tok->values(); |
4709 | 4613 | continue; |
4710 | 4614 | const Token * const endOfVarScope = var->scope()->bodyEnd; |
4711 | 4615 | setTokenValue(varTok, value, settings); |
4712 | valueFlowForward(varTok->next(), endOfVarScope, varTok, values, tokenlist, settings); | |
4616 | valueFlowForward(varTok->next(), endOfVarScope, varTok, values, tokenlist); | |
4713 | 4617 | continue; |
4714 | 4618 | } |
4715 | 4619 | ValueFlow::Value::MoveKind moveKind; |
4745 | 4649 | const Token * openParentesisOfMove = findOpenParentesisOfMove(varTok); |
4746 | 4650 | const Token * endOfFunctionCall = findEndOfFunctionCallForParameter(openParentesisOfMove); |
4747 | 4651 | if (endOfFunctionCall) |
4748 | valueFlowForward( | |
4749 | const_cast<Token*>(endOfFunctionCall), endOfVarScope, varTok, values, tokenlist, settings); | |
4652 | valueFlowForward(const_cast<Token*>(endOfFunctionCall), endOfVarScope, varTok, values, tokenlist); | |
4750 | 4653 | } |
4751 | 4654 | } |
4752 | 4655 | } |
4966 | 4869 | rhs.errorPath.emplace_back(tok, |
4967 | 4870 | tok->astOperand1()->expressionString() + " is assigned '" + |
4968 | 4871 | tok->astOperand2()->expressionString() + "' here."); |
4969 | valueFlowForward(start, end, tok->astOperand1(), {rhs}, tokenlist, tokenlist->getSettings()); | |
4872 | valueFlowForward(start, end, tok->astOperand1(), rhs, tokenlist); | |
4970 | 4873 | |
4971 | 4874 | ValueFlow::Value lhs = makeSymbolic(tok->astOperand1()); |
4972 | 4875 | lhs.errorPath.emplace_back(tok, |
4973 | 4876 | tok->astOperand1()->expressionString() + " is assigned '" + |
4974 | 4877 | tok->astOperand2()->expressionString() + "' here."); |
4975 | valueFlowForward(start, end, tok->astOperand2(), {lhs}, tokenlist, tokenlist->getSettings()); | |
4878 | valueFlowForward(start, end, tok->astOperand2(), lhs, tokenlist); | |
4976 | 4879 | } |
4977 | 4880 | } |
4978 | 4881 | } |
5252 | 5155 | constValues.splice(constValues.end(), values, it, values.end()); |
5253 | 5156 | valueFlowForwardConst(const_cast<Token*>(nextExpression), endOfVarScope, expr->variable(), constValues, settings); |
5254 | 5157 | } |
5255 | valueFlowForward(const_cast<Token*>(nextExpression), endOfVarScope, expr, values, tokenlist, settings); | |
5158 | valueFlowForward(const_cast<Token*>(nextExpression), endOfVarScope, expr, values, tokenlist); | |
5256 | 5159 | } |
5257 | 5160 | |
5258 | 5161 | static void valueFlowForwardAssign(Token* const tok, |
5424 | 5327 | value.errorPath.emplace_back(tok, |
5425 | 5328 | tok->astOperand1()->expressionString() + " is assigned '" + |
5426 | 5329 | tok->astOperand2()->expressionString() + "' here."); |
5427 | valueFlowForward(start, end, expr, {value}, tokenlist, settings); | |
5330 | valueFlowForward(start, end, expr, value, tokenlist); | |
5428 | 5331 | } |
5429 | 5332 | } |
5430 | 5333 | } |
5530 | 5433 | Condition() : vartok(nullptr), true_values(), false_values(), inverted(false), impossible(true) {} |
5531 | 5434 | }; |
5532 | 5435 | |
5436 | virtual std::vector<Condition> parse(const Token* tok, const Settings* settings) const = 0; | |
5437 | ||
5533 | 5438 | virtual Analyzer::Result forward(Token* start, |
5534 | 5439 | const Token* stop, |
5535 | 5440 | const Token* exprTok, |
5536 | 5441 | const std::list<ValueFlow::Value>& values, |
5537 | TokenList* tokenlist, | |
5538 | const Settings* settings) const = 0; | |
5442 | TokenList* tokenlist) const | |
5443 | { | |
5444 | return valueFlowForward(start->next(), stop, exprTok, values, tokenlist); | |
5445 | } | |
5539 | 5446 | |
5540 | 5447 | virtual Analyzer::Result forward(Token* top, |
5541 | 5448 | const Token* exprTok, |
5542 | 5449 | const std::list<ValueFlow::Value>& values, |
5543 | TokenList* tokenlist, | |
5544 | const Settings* settings) const = 0; | |
5450 | TokenList* tokenlist) const | |
5451 | { | |
5452 | return valueFlowForwardRecursive(top, exprTok, values, tokenlist); | |
5453 | } | |
5545 | 5454 | |
5546 | 5455 | virtual void reverse(Token* start, |
5547 | 5456 | const Token* endToken, |
5548 | 5457 | const Token* exprTok, |
5549 | 5458 | const std::list<ValueFlow::Value>& values, |
5550 | TokenList* tokenlist, | |
5551 | const Settings* settings) const = 0; | |
5552 | ||
5553 | virtual std::vector<Condition> parse(const Token* tok, const Settings* settings) const = 0; | |
5459 | TokenList* tokenlist) const | |
5460 | { | |
5461 | return valueFlowReverse(start, endToken, exprTok, values, tokenlist); | |
5462 | } | |
5554 | 5463 | |
5555 | 5464 | void traverseCondition(TokenList* tokenlist, |
5556 | 5465 | SymbolDatabase* symboldatabase, |
5671 | 5580 | })) { |
5672 | 5581 | // Start at the end of the loop body |
5673 | 5582 | Token* bodyTok = top->link()->next(); |
5674 | reverse(bodyTok->link(), bodyTok, cond.vartok, values, tokenlist, settings); | |
5583 | reverse(bodyTok->link(), bodyTok, cond.vartok, values, tokenlist); | |
5675 | 5584 | } |
5676 | 5585 | if (settings->debugwarnings) |
5677 | 5586 | bailout(tokenlist, |
5690 | 5599 | if (!startTok) |
5691 | 5600 | startTok = tok->previous(); |
5692 | 5601 | |
5693 | reverse(startTok, nullptr, cond.vartok, values, tokenlist, settings); | |
5602 | reverse(startTok, nullptr, cond.vartok, values, tokenlist); | |
5694 | 5603 | }); |
5695 | 5604 | } |
5696 | 5605 | |
5777 | 5686 | return v.isImpossible(); |
5778 | 5687 | }); |
5779 | 5688 | for (Token* start:nextExprs) { |
5780 | Analyzer::Result r = forward(start, cond.vartok, values, tokenlist, settings); | |
5689 | Analyzer::Result r = forward(start, cond.vartok, values, tokenlist); | |
5781 | 5690 | if (r.terminate != Analyzer::Terminate::None) |
5782 | 5691 | return; |
5783 | 5692 | } |
5837 | 5746 | |
5838 | 5747 | if (Token::simpleMatch(condTop, "?")) { |
5839 | 5748 | Token* colon = condTop->astOperand2(); |
5840 | forward(colon->astOperand1(), cond.vartok, thenValues, tokenlist, settings); | |
5841 | forward(colon->astOperand2(), cond.vartok, elseValues, tokenlist, settings); | |
5749 | forward(colon->astOperand1(), cond.vartok, thenValues, tokenlist); | |
5750 | forward(colon->astOperand2(), cond.vartok, elseValues, tokenlist); | |
5842 | 5751 | // TODO: Handle after condition |
5843 | 5752 | return; |
5844 | 5753 | } |
5922 | 5831 | std::list<ValueFlow::Value>& values = (i == 0 ? thenValues : elseValues); |
5923 | 5832 | valueFlowSetConditionToKnown(condTok, values, i == 0); |
5924 | 5833 | |
5925 | Analyzer::Result r = | |
5926 | forward(startTokens[i], startTokens[i]->link(), cond.vartok, values, tokenlist, settings); | |
5834 | Analyzer::Result r = forward(startTokens[i], startTokens[i]->link(), cond.vartok, values, tokenlist); | |
5927 | 5835 | deadBranch[i] = r.terminate == Analyzer::Terminate::Escape; |
5928 | 5836 | if (r.action.isModified() && !deadBranch[i]) |
5929 | 5837 | changeBlock = i; |
6025 | 5933 | } |
6026 | 5934 | if (values.empty()) |
6027 | 5935 | return; |
6028 | forward(after, getEndOfExprScope(cond.vartok, scope), cond.vartok, values, tokenlist, settings); | |
5936 | forward(after, getEndOfExprScope(cond.vartok, scope), cond.vartok, values, tokenlist); | |
6029 | 5937 | } |
6030 | 5938 | }); |
6031 | 5939 | } |
6043 | 5951 | } |
6044 | 5952 | |
6045 | 5953 | struct SimpleConditionHandler : ConditionHandler { |
6046 | virtual Analyzer::Result forward(Token* start, | |
6047 | const Token* stop, | |
6048 | const Token* exprTok, | |
6049 | const std::list<ValueFlow::Value>& values, | |
6050 | TokenList* tokenlist, | |
6051 | const Settings* settings) const override { | |
6052 | return valueFlowForward(start->next(), stop, exprTok, values, tokenlist, settings); | |
6053 | } | |
6054 | ||
6055 | virtual Analyzer::Result forward(Token* top, | |
6056 | const Token* exprTok, | |
6057 | const std::list<ValueFlow::Value>& values, | |
6058 | TokenList* tokenlist, | |
6059 | const Settings* settings) const override { | |
6060 | return valueFlowForward(top, exprTok, values, tokenlist, settings); | |
6061 | } | |
6062 | ||
6063 | virtual void reverse(Token* start, | |
6064 | const Token* endToken, | |
6065 | const Token* exprTok, | |
6066 | const std::list<ValueFlow::Value>& values, | |
6067 | TokenList* tokenlist, | |
6068 | const Settings* settings) const override { | |
6069 | return valueFlowReverse(start, endToken, exprTok, values, tokenlist, settings); | |
6070 | } | |
6071 | ||
6072 | 5954 | virtual std::vector<Condition> parse(const Token* tok, const Settings*) const override { |
6073 | 5955 | Condition cond; |
6074 | 5956 | ValueFlow::Value true_value; |
6438 | 6320 | } |
6439 | 6321 | } |
6440 | 6322 | |
6441 | static void valueFlowForLoopSimplifyAfter(Token* fortok, | |
6442 | nonneg int varid, | |
6443 | const MathLib::bigint num, | |
6444 | TokenList* tokenlist, | |
6445 | const Settings* settings) | |
6323 | static void valueFlowForLoopSimplifyAfter(Token* fortok, nonneg int varid, const MathLib::bigint num, TokenList* tokenlist) | |
6446 | 6324 | { |
6447 | 6325 | const Token *vartok = nullptr; |
6448 | 6326 | for (const Token *tok = fortok; tok; tok = tok->next()) { |
6467 | 6345 | values.back().errorPath.emplace_back(fortok,"After for loop, " + var->name() + " has value " + values.back().infoString()); |
6468 | 6346 | |
6469 | 6347 | if (blockTok != endToken) { |
6470 | valueFlowForward(blockTok->next(), endToken, vartok, values, tokenlist, settings); | |
6348 | valueFlowForward(blockTok->next(), endToken, vartok, values, tokenlist); | |
6471 | 6349 | } |
6472 | 6350 | } |
6473 | 6351 | |
6495 | 6373 | std::list<ValueFlow::Value> initValues; |
6496 | 6374 | initValues.emplace_back(initValue, ValueFlow::Value::Bound::Lower); |
6497 | 6375 | initValues.push_back(asImpossible(initValues.back())); |
6498 | Analyzer::Result result = | |
6499 | valueFlowForward(bodyStart, bodyStart->link(), vartok, initValues, tokenlist, settings); | |
6376 | Analyzer::Result result = valueFlowForward(bodyStart, bodyStart->link(), vartok, initValues, tokenlist); | |
6500 | 6377 | |
6501 | 6378 | if (!result.action.isModified()) { |
6502 | 6379 | std::list<ValueFlow::Value> lastValues; |
6505 | 6382 | lastValues.push_back(asImpossible(lastValues.back())); |
6506 | 6383 | if (stepValue != 1) |
6507 | 6384 | lastValues.pop_front(); |
6508 | valueFlowForward(bodyStart, bodyStart->link(), vartok, lastValues, tokenlist, settings); | |
6385 | valueFlowForward(bodyStart, bodyStart->link(), vartok, lastValues, tokenlist); | |
6509 | 6386 | } |
6510 | 6387 | } |
6511 | 6388 | const MathLib::bigint afterValue = executeBody ? lastValue + stepValue : initValue; |
6512 | valueFlowForLoopSimplifyAfter(tok, varid, afterValue, tokenlist, settings); | |
6389 | valueFlowForLoopSimplifyAfter(tok, varid, afterValue, tokenlist); | |
6513 | 6390 | } else { |
6514 | 6391 | ProgramMemory mem1, mem2, memAfter; |
6515 | 6392 | if (valueFlowForLoop2(tok, &mem1, &mem2, &memAfter)) { |
6532 | 6409 | continue; |
6533 | 6410 | if (p.first.tok->varId() == 0) |
6534 | 6411 | continue; |
6535 | valueFlowForLoopSimplifyAfter(tok, p.first.getExpressionId(), p.second.intvalue, tokenlist, settings); | |
6412 | valueFlowForLoopSimplifyAfter(tok, p.first.getExpressionId(), p.second.intvalue, tokenlist); | |
6536 | 6413 | } |
6537 | 6414 | } |
6538 | 6415 | } |
6789 | 6666 | } |
6790 | 6667 | |
6791 | 6668 | static void valueFlowInjectParameter(TokenList* tokenlist, |
6792 | const Settings* settings, | |
6793 | 6669 | const Variable* arg, |
6794 | 6670 | const Scope* functionScope, |
6795 | 6671 | const std::list<ValueFlow::Value>& argvalues) |
6807 | 6683 | functionScope->bodyEnd, |
6808 | 6684 | arg->nameToken(), |
6809 | 6685 | argvalues, |
6810 | tokenlist, | |
6811 | settings); | |
6686 | tokenlist); | |
6812 | 6687 | } |
6813 | 6688 | |
6814 | 6689 | static void valueFlowSwitchVariable(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings) |
7031 | 6906 | } |
7032 | 6907 | } |
7033 | 6908 | |
7034 | static void valueFlowFunctionDefaultParameter(TokenList* tokenlist, SymbolDatabase* symboldatabase, const Settings* settings) | |
6909 | static void valueFlowFunctionDefaultParameter(TokenList* tokenlist, SymbolDatabase* symboldatabase) | |
7035 | 6910 | { |
7036 | 6911 | if (!tokenlist->isCPP()) |
7037 | 6912 | return; |
7053 | 6928 | argvalues.push_back(v); |
7054 | 6929 | } |
7055 | 6930 | if (!argvalues.empty()) |
7056 | valueFlowInjectParameter(tokenlist, settings, var, scope, argvalues); | |
6931 | valueFlowInjectParameter(tokenlist, var, scope, argvalues); | |
7057 | 6932 | } |
7058 | 6933 | } |
7059 | 6934 | } |
7249 | 7124 | if (partial) |
7250 | 7125 | continue; |
7251 | 7126 | |
7252 | valueFlowForward(tok->next(), tok->scope()->bodyEnd, var->nameToken(), {uninitValue}, tokenlist, settings); | |
7127 | valueFlowForward(tok->next(), tok->scope()->bodyEnd, var->nameToken(), uninitValue, tokenlist); | |
7253 | 7128 | } |
7254 | 7129 | } |
7255 | 7130 | |
7407 | 7282 | } |
7408 | 7283 | }; |
7409 | 7284 | |
7410 | static Analyzer::Result valueFlowContainerForward(Token* startToken, | |
7411 | const Token* endToken, | |
7412 | const Token* exprTok, | |
7413 | const ValueFlow::Value& value, | |
7414 | TokenList* tokenlist) | |
7415 | { | |
7416 | ContainerExpressionAnalyzer a(exprTok, value, tokenlist); | |
7417 | return valueFlowGenericForward(startToken, endToken, a, tokenlist->getSettings()); | |
7418 | } | |
7419 | ||
7420 | static Analyzer::Result valueFlowContainerForwardRecursive(Token* top, | |
7421 | const Token* exprTok, | |
7422 | const ValueFlow::Value& value, | |
7423 | TokenList* tokenlist) | |
7424 | { | |
7425 | ContainerExpressionAnalyzer a(exprTok, value, tokenlist); | |
7426 | return valueFlowGenericForward(top, a, tokenlist->getSettings()); | |
7427 | } | |
7428 | ||
7429 | static Analyzer::Result valueFlowContainerForward(Token* startToken, | |
7430 | const Token* exprTok, | |
7431 | const ValueFlow::Value& value, | |
7432 | TokenList* tokenlist) | |
7433 | { | |
7434 | const Token* endToken = nullptr; | |
7435 | const Function* f = Scope::nestedInFunction(startToken->scope()); | |
7436 | if (f && f->functionScope) | |
7437 | endToken = f->functionScope->bodyEnd; | |
7438 | return valueFlowContainerForward(startToken, endToken, exprTok, value, tokenlist); | |
7439 | } | |
7440 | ||
7441 | static void valueFlowContainerReverse(Token* tok, | |
7442 | const Token* const endToken, | |
7443 | const Token* const varToken, | |
7444 | const std::list<ValueFlow::Value>& values, | |
7445 | TokenList* tokenlist, | |
7446 | const Settings* settings) | |
7447 | { | |
7448 | for (const ValueFlow::Value& value : values) { | |
7449 | ContainerExpressionAnalyzer a(varToken, value, tokenlist); | |
7450 | valueFlowGenericReverse(tok, endToken, a, settings); | |
7451 | } | |
7285 | static const Token* parseBinaryIntOp(const Token* expr, | |
7286 | const std::function<std::vector<MathLib::bigint>(const Token*)>& eval, | |
7287 | MathLib::bigint& known) | |
7288 | { | |
7289 | if (!expr) | |
7290 | return nullptr; | |
7291 | if (!expr->astOperand1() || !expr->astOperand2()) | |
7292 | return nullptr; | |
7293 | if (expr->astOperand1()->exprId() == 0 && expr->astOperand2()->exprId() == 0) | |
7294 | return nullptr; | |
7295 | std::vector<MathLib::bigint> x1 = eval(expr->astOperand1()); | |
7296 | std::vector<MathLib::bigint> x2 = eval(expr->astOperand2()); | |
7297 | if (expr->astOperand1()->exprId() == 0 && x1.empty()) | |
7298 | return nullptr; | |
7299 | if (expr->astOperand2()->exprId() == 0 && x2.empty()) | |
7300 | return nullptr; | |
7301 | const Token* varTok = nullptr; | |
7302 | if (!x1.empty() && x2.empty()) { | |
7303 | varTok = expr->astOperand2(); | |
7304 | known = x1.front(); | |
7305 | } else if (x1.empty() && !x2.empty()) { | |
7306 | varTok = expr->astOperand1(); | |
7307 | known = x2.front(); | |
7308 | } | |
7309 | return varTok; | |
7310 | } | |
7311 | ||
7312 | const Token* solveExprValue(const Token* expr, | |
7313 | const std::function<std::vector<MathLib::bigint>(const Token*)>& eval, | |
7314 | ValueFlow::Value& value) | |
7315 | { | |
7316 | if (!value.isIntValue() && !value.isIteratorValue() && !value.isSymbolicValue()) | |
7317 | return expr; | |
7318 | if (value.isSymbolicValue() && !Token::Match(expr, "+|-")) | |
7319 | return expr; | |
7320 | MathLib::bigint intval; | |
7321 | const Token* binaryTok = parseBinaryIntOp(expr, eval, intval); | |
7322 | bool rhs = astIsRHS(binaryTok); | |
7323 | // If its on the rhs, then -1 multiplication is needed, which is not possible with simple delta analysis used currently for symbolic values | |
7324 | if (value.isSymbolicValue() && rhs && Token::simpleMatch(expr, "-")) | |
7325 | return expr; | |
7326 | if (binaryTok && expr->str().size() == 1) { | |
7327 | switch (expr->str()[0]) { | |
7328 | case '+': { | |
7329 | value.intvalue -= intval; | |
7330 | return solveExprValue(binaryTok, eval, value); | |
7331 | } | |
7332 | case '-': { | |
7333 | if (rhs) | |
7334 | value.intvalue = intval - value.intvalue; | |
7335 | else | |
7336 | value.intvalue += intval; | |
7337 | return solveExprValue(binaryTok, eval, value); | |
7338 | } | |
7339 | case '*': { | |
7340 | if (intval == 0) | |
7341 | break; | |
7342 | value.intvalue /= intval; | |
7343 | return solveExprValue(binaryTok, eval, value); | |
7344 | } | |
7345 | case '^': { | |
7346 | value.intvalue ^= intval; | |
7347 | return solveExprValue(binaryTok, eval, value); | |
7348 | } | |
7349 | } | |
7350 | } | |
7351 | return expr; | |
7352 | } | |
7353 | ||
7354 | static const Token* solveExprValue(const Token* expr, ValueFlow::Value& value) | |
7355 | { | |
7356 | return solveExprValue( | |
7357 | expr, | |
7358 | [](const Token* tok) -> std::vector<MathLib::bigint> { | |
7359 | if (tok->hasKnownIntValue()) | |
7360 | return {tok->values().front().intvalue}; | |
7361 | return {}; | |
7362 | }, | |
7363 | value); | |
7364 | } | |
7365 | ||
7366 | ValuePtr<Analyzer> makeAnalyzer(const Token* exprTok, ValueFlow::Value value, const TokenList* tokenlist) | |
7367 | { | |
7368 | if (value.isContainerSizeValue()) | |
7369 | return ContainerExpressionAnalyzer(exprTok, value, tokenlist); | |
7370 | const Token* expr = solveExprValue(exprTok, value); | |
7371 | return ExpressionAnalyzer(expr, value, tokenlist); | |
7372 | } | |
7373 | ||
7374 | ValuePtr<Analyzer> makeReverseAnalyzer(const Token* exprTok, const ValueFlow::Value& value, const TokenList* tokenlist) | |
7375 | { | |
7376 | if (value.isContainerSizeValue()) | |
7377 | return ContainerExpressionAnalyzer(exprTok, value, tokenlist); | |
7378 | return ExpressionAnalyzer(exprTok, value, tokenlist); | |
7452 | 7379 | } |
7453 | 7380 | |
7454 | 7381 | bool isContainerSizeChanged(const Token* tok, const Settings* settings, int depth) |
7864 | 7791 | if (constSize) |
7865 | 7792 | valueFlowForwardConst(var->nameToken()->next(), var->scope()->bodyEnd, var, values, settings); |
7866 | 7793 | else |
7867 | valueFlowContainerForward(var->nameToken()->next(), var->nameToken(), value, tokenlist); | |
7794 | valueFlowForward(var->nameToken()->next(), var->nameToken(), value, tokenlist); | |
7868 | 7795 | } |
7869 | 7796 | } |
7870 | 7797 | |
7879 | 7806 | ValueFlow::Value value(Token::getStrLength(containerTok->tokAt(2))); |
7880 | 7807 | value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; |
7881 | 7808 | value.setKnown(); |
7882 | valueFlowContainerForward(containerTok->next(), containerTok, value, tokenlist); | |
7809 | valueFlowForward(containerTok->next(), containerTok, value, tokenlist); | |
7883 | 7810 | } |
7884 | 7811 | } else if (Token::Match(tok, "%name%|;|{|}|> %var% = {") && Token::simpleMatch(tok->linkAt(3), "} ;")) { |
7885 | 7812 | const Token* containerTok = tok->next(); |
7889 | 7816 | std::vector<ValueFlow::Value> values = |
7890 | 7817 | getInitListSize(tok->tokAt(3), containerTok->valueType(), settings); |
7891 | 7818 | for (const ValueFlow::Value& value : values) |
7892 | valueFlowContainerForward(containerTok->next(), containerTok, value, tokenlist); | |
7819 | valueFlowForward(containerTok->next(), containerTok, value, tokenlist); | |
7893 | 7820 | } |
7894 | 7821 | } else if (Token::Match(tok, ". %name% (") && tok->astOperand1() && tok->astOperand1()->valueType() && |
7895 | 7822 | tok->astOperand1()->valueType()->container) { |
7901 | 7828 | ValueFlow::Value value(0); |
7902 | 7829 | value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; |
7903 | 7830 | value.setKnown(); |
7904 | valueFlowContainerForward(tok->next(), containerTok, value, tokenlist); | |
7831 | valueFlowForward(tok->next(), containerTok, value, tokenlist); | |
7905 | 7832 | } else if (action == Library::Container::Action::RESIZE && tok->tokAt(2)->astOperand2() && |
7906 | 7833 | tok->tokAt(2)->astOperand2()->hasKnownIntValue()) { |
7907 | 7834 | ValueFlow::Value value(tok->tokAt(2)->astOperand2()->values().front()); |
7908 | 7835 | value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; |
7909 | 7836 | value.setKnown(); |
7910 | valueFlowContainerForward(tok->next(), containerTok, value, tokenlist); | |
7837 | valueFlowForward(tok->next(), containerTok, value, tokenlist); | |
7911 | 7838 | } |
7912 | 7839 | } |
7913 | 7840 | } |
7915 | 7842 | } |
7916 | 7843 | |
7917 | 7844 | struct ContainerConditionHandler : ConditionHandler { |
7918 | virtual Analyzer::Result forward(Token* start, | |
7919 | const Token* stop, | |
7920 | const Token* exprTok, | |
7921 | const std::list<ValueFlow::Value>& values, | |
7922 | TokenList* tokenlist, | |
7923 | const Settings*) const override { | |
7924 | Analyzer::Result result{}; | |
7925 | for (const ValueFlow::Value& value : values) | |
7926 | result.update(valueFlowContainerForward(start->next(), stop, exprTok, value, tokenlist)); | |
7927 | return result; | |
7928 | } | |
7929 | ||
7930 | virtual Analyzer::Result forward(Token* top, | |
7931 | const Token* exprTok, | |
7932 | const std::list<ValueFlow::Value>& values, | |
7933 | TokenList* tokenlist, | |
7934 | const Settings*) const override { | |
7935 | Analyzer::Result result{}; | |
7936 | for (const ValueFlow::Value& value : values) | |
7937 | result.update(valueFlowContainerForwardRecursive(top, exprTok, value, tokenlist)); | |
7938 | return result; | |
7939 | } | |
7940 | ||
7941 | virtual void reverse(Token* start, | |
7942 | const Token* endTok, | |
7943 | const Token* exprTok, | |
7944 | const std::list<ValueFlow::Value>& values, | |
7945 | TokenList* tokenlist, | |
7946 | const Settings* settings) const override { | |
7947 | return valueFlowContainerReverse(start, endTok, exprTok, values, tokenlist, settings); | |
7948 | } | |
7949 | ||
7950 | 7845 | virtual std::vector<Condition> parse(const Token* tok, const Settings* settings) const override |
7951 | 7846 | { |
7952 | 7847 | Condition cond; |
8112 | 8007 | value.valueType = ValueFlow::Value::ValueType::BUFFER_SIZE; |
8113 | 8008 | value.setKnown(); |
8114 | 8009 | const std::list<ValueFlow::Value> values{value}; |
8115 | valueFlowForward(const_cast<Token*>(rhs), functionScope->bodyEnd, tok->next(), values, tokenlist, settings); | |
8010 | valueFlowForward(const_cast<Token*>(rhs), functionScope->bodyEnd, tok->next(), values, tokenlist); | |
8116 | 8011 | } |
8117 | 8012 | } |
8118 | 8013 | } |
8213 | 8108 | argValues.back().errorPath.emplace_back(arg.nameToken(), "Assuming " + arg.name() + " size is 1000000"); |
8214 | 8109 | argValues.back().safe = true; |
8215 | 8110 | for (const ValueFlow::Value &value : argValues) |
8216 | valueFlowContainerForward( | |
8217 | const_cast<Token*>(functionScope->bodyStart), arg.nameToken(), value, tokenlist); | |
8111 | valueFlowForward(const_cast<Token*>(functionScope->bodyStart), arg.nameToken(), value, tokenlist); | |
8218 | 8112 | continue; |
8219 | 8113 | } |
8220 | 8114 | |
8252 | 8146 | functionScope->bodyEnd, |
8253 | 8147 | arg.nameToken(), |
8254 | 8148 | argValues, |
8255 | tokenlist, | |
8256 | settings); | |
8149 | tokenlist); | |
8257 | 8150 | continue; |
8258 | 8151 | } |
8259 | 8152 | } |
8275 | 8168 | functionScope->bodyEnd, |
8276 | 8169 | arg.nameToken(), |
8277 | 8170 | argValues, |
8278 | tokenlist, | |
8279 | settings); | |
8171 | tokenlist); | |
8280 | 8172 | } |
8281 | 8173 | } |
8282 | 8174 | } |
8493 | 8385 | valueFlowCondition(SymbolicConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings); |
8494 | 8386 | valueFlowSymbolicInfer(tokenlist, symboldatabase); |
8495 | 8387 | valueFlowArrayBool(tokenlist); |
8388 | valueFlowArrayElement(tokenlist, settings); | |
8496 | 8389 | valueFlowRightShift(tokenlist, settings); |
8497 | 8390 | valueFlowAfterAssign(tokenlist, symboldatabase, errorLogger, settings); |
8498 | 8391 | valueFlowAfterSwap(tokenlist, symboldatabase, errorLogger, settings); |
8503 | 8396 | valueFlowSubFunction(tokenlist, symboldatabase, errorLogger, settings); |
8504 | 8397 | valueFlowFunctionReturn(tokenlist, errorLogger); |
8505 | 8398 | valueFlowLifetime(tokenlist, symboldatabase, errorLogger, settings); |
8506 | valueFlowFunctionDefaultParameter(tokenlist, symboldatabase, settings); | |
8399 | valueFlowFunctionDefaultParameter(tokenlist, symboldatabase); | |
8507 | 8400 | valueFlowUninit(tokenlist, symboldatabase, settings); |
8508 | 8401 | if (tokenlist->isCPP()) { |
8509 | 8402 | valueFlowAfterMove(tokenlist, symboldatabase, settings); |
1 | 1 | // After a release the DEVMINOR is incremented. MAJOR=x MINOR=y, DEVMINOR=y+1 |
2 | 2 | |
3 | 3 | #define CPPCHECK_MAJOR 2 |
4 | #define CPPCHECK_MINOR 8 | |
5 | #define CPPCHECK_DEVMINOR 8 | |
4 | #define CPPCHECK_MINOR 7 | |
5 | #define CPPCHECK_DEVMINOR 7 | |
6 | 6 | |
7 | 7 | #define STRINGIFY(x) STRING(x) |
8 | 8 | #define STRING(VER) #VER |
0 | 0 | --- |
1 | 1 | title: Cppcheck manual |
2 | subtitle: Version 2.8 | |
2 | subtitle: Version 2.7 | |
3 | 3 | author: Cppcheck team |
4 | 4 | lang: en |
5 | 5 | documentclass: report |
0 | release notes for cppcheck-2.8 | |
0 | release notes for cppcheck-2.9 | |
1 | 1 | |
2 | - Lifetime analysis can now track lifetime across user-defined constructors when they are inline and using member initializer list. | |
3 | - SymbolDatabase can now deduce iterator types from how they are specified in the library files. | |
4 | - ValueFlow can evaluate class member functions that return known values. | |
5 | - Improve duplicateValueTenary to not warn when used as an lvalue or when one branch has side effects | |
6 | - Fix variableScope to not warn when variables are used in lambda functions | |
7 | - Fix unassignedVariable warnings when using structured bindings | |
8 | - Fix redundantInitialization warning when variable is used in a lambda | |
9 | - Fix variableScope warnings when using if/while init-statement | |
10 | - Improve lifetime analysis when returning variadic template expressions | |
11 | - Detect more statements with constStatement | |
12 | - Detect variableScope for more types | |
13 | - Improvements to unreadVariable | |
14 | - Detect more instances of C style casts | |
15 | - Warn if the return value of new is discarded | |
16 | - The pre-ValueFlow uninitialized checker now uses a different ID as legacyUninitvar | |
17 | - Extended library format to exclude specific function argument values |
1641 | 1641 | } |
1642 | 1642 | |
1643 | 1643 | void secondAlwaysTrueFalseWhenFirstTrueError() { |
1644 | check("void f(void) {\n" // #8892 | |
1645 | " const char c[1] = { \'x\' }; \n" | |
1646 | " if(c[0] == \'x\'){;}\n" | |
1647 | "}"); | |
1648 | ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'c[0]=='x'' is always true\n", errout.str()); | |
1649 | ||
1644 | 1650 | check("void f(int x) {\n" |
1645 | 1651 | " if (x > 5 && x != 1)\n" |
1646 | 1652 | " a++;\n" |
1707 | 1707 | TEST_CASE(assign1); |
1708 | 1708 | TEST_CASE(assign2); |
1709 | 1709 | TEST_CASE(assign3); |
1710 | TEST_CASE(assign4); // #11019 | |
1710 | 1711 | |
1711 | 1712 | // Failed allocation |
1712 | 1713 | TEST_CASE(failedAllocation); |
1883 | 1884 | " f2.a = malloc(100);\n" |
1884 | 1885 | " *f1 = f2;\n" |
1885 | 1886 | "}", false); |
1887 | ASSERT_EQUALS("", errout.str()); | |
1888 | } | |
1889 | ||
1890 | void assign4() { | |
1891 | check("struct S { int a, b, c; };\n" // #11019 | |
1892 | "void f() {\n" | |
1893 | " struct S s;\n" | |
1894 | " *&s.a = open(\"xx.log\", O_RDONLY);\n" | |
1895 | " ((s).b) = open(\"xx.log\", O_RDONLY);\n" | |
1896 | " (&s)->c = open(\"xx.log\", O_RDONLY);\n" | |
1897 | "}\n", false); | |
1898 | ASSERT_EQUALS("[test.c:7]: (error) Memory leak: s.a\n" | |
1899 | "[test.c:7]: (error) Memory leak: s.b\n" | |
1900 | "[test.c:7]: (error) Memory leak: s.c\n", | |
1901 | errout.str()); | |
1902 | ||
1903 | check("struct S { int *p, *q; };\n" // #7705 | |
1904 | "void f(S s) {\n" | |
1905 | " s.p = new int[10];\n" | |
1906 | " s.q = malloc(40);\n" | |
1907 | "}\n"); | |
1908 | ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: s.p\n" | |
1909 | "[test.cpp:5]: (error) Memory leak: s.q\n", | |
1910 | errout.str()); | |
1911 | ||
1912 | check("struct S** f(struct S** s) {\n" // don't throw | |
1913 | " struct S** ret = malloc(sizeof(*ret));\n" | |
1914 | " ret[0] = malloc(sizeof(**s));\n" | |
1915 | " ret[0]->g = strdup(s[0]->g);\n" | |
1916 | " return ret;\n" | |
1917 | "}\n"); | |
1918 | ASSERT_EQUALS("", errout.str()); | |
1919 | ||
1920 | check("void run_rcmd(enum rcommand rcmd, rsh_session *sess, char *cmd) {\n" | |
1921 | " sess->fp = popen(cmd, rcmd == RSH_PIPE_READ ? \"r\" : \"w\");\n" | |
1922 | "}\n", false); | |
1886 | 1923 | ASSERT_EQUALS("", errout.str()); |
1887 | 1924 | } |
1888 | 1925 | |
2136 | 2173 | " ((f)->realm) = strdup(realm);\n" |
2137 | 2174 | " if(f->realm == NULL) {}\n" |
2138 | 2175 | "}", false); |
2139 | TODO_ASSERT_EQUALS("[test.c:6]: (error) Memory leak: f.realm\n", "", errout.str()); | |
2176 | ASSERT_EQUALS("[test.c:6]: (error) Memory leak: f.realm\n", errout.str()); | |
2140 | 2177 | } |
2141 | 2178 | |
2142 | 2179 | void customAllocation() { // #4770 |
1815 | 1815 | "void f(S s) {}\n"); |
1816 | 1816 | ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 's' should be passed by const reference.\n", errout.str()); |
1817 | 1817 | |
1818 | check("union U {\n" // don't crash | |
1819 | " int a;\n" | |
1820 | " decltype(nullptr) b;\n" | |
1821 | "};\n" | |
1822 | "int* f(U u) { return u.b; }\n"); | |
1823 | ASSERT_EQUALS("", errout.str()); | |
1824 | ||
1818 | 1825 | Settings settings1; |
1819 | 1826 | settings1.platform(Settings::Win64); |
1820 | 1827 | check("using ui64 = unsigned __int64;\n" |
1850 | 1857 | check("void f(std::string str) {\n" |
1851 | 1858 | " std::string& s2 = str;\n" |
1852 | 1859 | "}"); |
1853 | ASSERT_EQUALS("[test.cpp:2]: (style) Variable 's2' can be declared with const\n", errout.str()); | |
1860 | ASSERT_EQUALS("[test.cpp:2]: (style) Variable 's2' can be declared as reference to const\n", errout.str()); | |
1854 | 1861 | |
1855 | 1862 | check("void f(std::string str) {\n" |
1856 | 1863 | " const std::string& s2 = str;\n" |
2005 | 2012 | " int& i = x[0];\n" |
2006 | 2013 | " return i;\n" |
2007 | 2014 | "}"); |
2008 | ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'i' can be declared with const\n", errout.str()); | |
2015 | ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'i' can be declared as reference to const\n", errout.str()); | |
2009 | 2016 | |
2010 | 2017 | check("int f(std::vector<int>& x) {\n" |
2011 | 2018 | " return x[0];\n" |
2012 | 2019 | "}"); |
2013 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'x' can be declared with const\n", errout.str()); | |
2020 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'x' can be declared as reference to const\n", errout.str()); | |
2014 | 2021 | |
2015 | 2022 | check("int f(std::vector<int> x) {\n" |
2016 | 2023 | " const int& i = x[0];\n" |
2051 | 2058 | check("const int& f(std::vector<int>& x) {\n" |
2052 | 2059 | " return x[0];\n" |
2053 | 2060 | "}"); |
2054 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'x' can be declared with const\n", errout.str()); | |
2061 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'x' can be declared as reference to const\n", errout.str()); | |
2055 | 2062 | |
2056 | 2063 | check("int f(std::vector<int>& x) {\n" |
2057 | 2064 | " x[0]++;\n" |
2168 | 2175 | " for(auto x:v)\n" |
2169 | 2176 | " x = 1;\n" |
2170 | 2177 | "}"); |
2171 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'v' can be declared with const\n", errout.str()); | |
2178 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'v' can be declared as reference to const\n", errout.str()); | |
2172 | 2179 | |
2173 | 2180 | check("void f(std::vector<int>& v) {\n" |
2174 | 2181 | " for(auto& x:v) {}\n" |
2175 | 2182 | "}"); |
2176 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'v' can be declared with const\n", errout.str()); | |
2183 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'v' can be declared as reference to const\n", errout.str()); | |
2177 | 2184 | |
2178 | 2185 | check("void f(std::vector<int>& v) {\n" |
2179 | 2186 | " for(const auto& x:v) {}\n" |
2180 | 2187 | "}"); |
2181 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'v' can be declared with const\n", errout.str()); | |
2188 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'v' can be declared as reference to const\n", errout.str()); | |
2182 | 2189 | |
2183 | 2190 | check("void f(int& i) {\n" |
2184 | 2191 | " int& j = i;\n" |
2282 | 2289 | " a x;\n" |
2283 | 2290 | " x(i);\n" |
2284 | 2291 | "}\n"); |
2285 | ASSERT_EQUALS("[test.cpp:4]: (style) Parameter 'i' can be declared with const\n", errout.str()); | |
2292 | ASSERT_EQUALS("[test.cpp:4]: (style) Parameter 'i' can be declared as reference to const\n", errout.str()); | |
2286 | 2293 | |
2287 | 2294 | //cast or assignment to a non-const reference should prevent the warning |
2288 | 2295 | check("struct T { void dostuff() const {}};\n" |
2289 | 2296 | "void a(T& x) {\n" |
2290 | 2297 | " x.dostuff();\n" |
2291 | 2298 | "}"); |
2292 | ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared with const\n", errout.str()); | |
2299 | ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared as reference to const\n", errout.str()); | |
2293 | 2300 | check("struct T : public U { void dostuff() const {}};\n" |
2294 | 2301 | "void a(T& x) {\n" |
2295 | 2302 | " x.dostuff();\n" |
2303 | 2310 | " x.dostuff();\n" |
2304 | 2311 | " const U& y = x\n" |
2305 | 2312 | "}"); |
2306 | ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared with const\n", errout.str()); | |
2313 | ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared as reference to const\n", errout.str()); | |
2307 | 2314 | check("struct T : public U { void dostuff() const {}};\n" |
2308 | 2315 | "void a(T& x) {\n" |
2309 | 2316 | " x.dostuff();\n" |
2324 | 2331 | " const U& y = static_cast<const U&>(x)\n" |
2325 | 2332 | " y.mutate();\n" //to avoid warnings that y can be const |
2326 | 2333 | "}"); |
2327 | ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared with const\n", errout.str()); | |
2334 | ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared as reference to const\n", errout.str()); | |
2328 | 2335 | check("struct T : public U { void dostuff() const {}};\n" |
2329 | 2336 | "void a(T& x) {\n" |
2330 | 2337 | " x.dostuff();\n" |
2337 | 2344 | " x.dostuff();\n" |
2338 | 2345 | " const U& y = dynamic_cast<const U&>(x)\n" |
2339 | 2346 | "}"); |
2340 | ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared with const\n", errout.str()); | |
2347 | ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared as reference to const\n", errout.str()); | |
2341 | 2348 | check( |
2342 | 2349 | "struct T : public U { void dostuff() const {}};\n" |
2343 | 2350 | "void a(T& x) {\n" |
2345 | 2352 | " const U& y = dynamic_cast<U const &>(x)\n" |
2346 | 2353 | "}" |
2347 | 2354 | ); |
2348 | ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared with const\n", errout.str()); | |
2355 | ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared as reference to const\n", errout.str()); | |
2349 | 2356 | check("struct T : public U { void dostuff() const {}};\n" |
2350 | 2357 | "void a(T& x) {\n" |
2351 | 2358 | " x.dostuff();\n" |
2352 | 2359 | " const U& y = dynamic_cast<U & const>(x)\n" |
2353 | 2360 | "}"); |
2354 | ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared with const\n", errout.str()); | |
2361 | ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared as reference to const\n", errout.str()); | |
2355 | 2362 | check("struct T : public U { void dostuff() const {}};\n" |
2356 | 2363 | "void a(T& x) {\n" |
2357 | 2364 | " x.dostuff();\n" |
2364 | 2371 | " x.dostuff();\n" |
2365 | 2372 | " const U& y = dynamic_cast<typename const U&>(x)\n" |
2366 | 2373 | "}"); |
2367 | ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared with const\n", errout.str()); | |
2374 | ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared as reference to const\n", errout.str()); | |
2368 | 2375 | check("struct T : public U { void dostuff() const {}};\n" |
2369 | 2376 | "void a(T& x) {\n" |
2370 | 2377 | " x.dostuff();\n" |
2435 | 2442 | " x.dostuff();\n" |
2436 | 2443 | " const U& y = (const U&)(x)\n" |
2437 | 2444 | "}"); |
2438 | ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared with const\n", errout.str()); | |
2445 | ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared as reference to const\n", errout.str()); | |
2439 | 2446 | check("struct T : public U { void dostuff() const {}};\n" |
2440 | 2447 | "void a(T& x) {\n" |
2441 | 2448 | " x.dostuff();\n" |
2448 | 2455 | " x.dostuff();\n" |
2449 | 2456 | " const U& y = (typename const U&)(x)\n" |
2450 | 2457 | "}"); |
2451 | ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared with const\n", errout.str()); | |
2458 | ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared as reference to const\n", errout.str()); | |
2452 | 2459 | check("struct T : public U { void dostuff() const {}};\n" |
2453 | 2460 | "void a(T& x) {\n" |
2454 | 2461 | " x.dostuff();\n" |
2487 | 2494 | "void f(int& i) {\n" |
2488 | 2495 | " a()(i);\n" |
2489 | 2496 | "}\n"); |
2490 | ASSERT_EQUALS("[test.cpp:4]: (style) Parameter 'i' can be declared with const\n", errout.str()); | |
2497 | ASSERT_EQUALS("[test.cpp:4]: (style) Parameter 'i' can be declared as reference to const\n", errout.str()); | |
2491 | 2498 | |
2492 | 2499 | // #9767 |
2493 | 2500 | check("void fct1(MyClass& object) {\n" |
2736 | 2743 | " : c(i)\n" |
2737 | 2744 | "{\n" |
2738 | 2745 | "}"); |
2739 | TODO_ASSERT_EQUALS("[test.cpp:16]: (style) Parameter 'i' can be declared with const\n", "", errout.str()); | |
2746 | TODO_ASSERT_EQUALS("[test.cpp:16]: (style) Parameter 'i' can be declared as reference to const\n", "", errout.str()); | |
2740 | 2747 | |
2741 | 2748 | check("class C\n" |
2742 | 2749 | "{\n" |
2757 | 2764 | " : c(i)\n" |
2758 | 2765 | "{\n" |
2759 | 2766 | "}"); |
2760 | TODO_ASSERT_EQUALS("[test.cpp:16]: (style) Parameter 'i' can be declared with const\n", "", errout.str()); | |
2767 | TODO_ASSERT_EQUALS("[test.cpp:16]: (style) Parameter 'i' can be declared as reference to const\n", "", errout.str()); | |
2761 | 2768 | |
2762 | 2769 | check("class C\n" |
2763 | 2770 | "{\n" |
2778 | 2785 | " : c(0, i)\n" |
2779 | 2786 | "{\n" |
2780 | 2787 | "}"); |
2781 | TODO_ASSERT_EQUALS("[test.cpp:16]: (style) Parameter 'i' can be declared with const\n", "", errout.str()); | |
2788 | TODO_ASSERT_EQUALS("[test.cpp:16]: (style) Parameter 'i' can be declared as reference to const\n", "", errout.str()); | |
2782 | 2789 | |
2783 | 2790 | check("void f(std::map<int, std::vector<int>> &map) {\n" // #10266 |
2784 | 2791 | " for (auto &[slave, panels] : map)\n" |
2831 | 2838 | void constParameterCallback() { |
2832 | 2839 | check("int callback(std::vector<int>& x) { return x[0]; }\n" |
2833 | 2840 | "void f() { dostuff(callback); }"); |
2834 | ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:1]: (style) Parameter 'x' can be declared with 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()); | |
2841 | 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()); | |
2835 | 2842 | |
2836 | 2843 | // #9906 |
2837 | 2844 | check("class EventEngine : public IEventEngine {\n" |
2849 | 2856 | "void EventEngine::signalEvent(ev::sig& signal, int revents) {\n" |
2850 | 2857 | " switch (signal.signum) {}\n" |
2851 | 2858 | "}"); |
2852 | ASSERT_EQUALS("[test.cpp:10] -> [test.cpp:13]: (style) Parameter 'signal' can be declared with 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()); | |
2859 | 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()); | |
2853 | 2860 | } |
2854 | 2861 | |
2855 | 2862 | void constPointer() { |
2856 | 2863 | check("void foo(int *p) { return *p; }"); |
2857 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared with const\n", errout.str()); | |
2864 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout.str()); | |
2858 | 2865 | |
2859 | 2866 | check("void foo(int *p) { x = *p; }"); |
2860 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared with const\n", errout.str()); | |
2867 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout.str()); | |
2861 | 2868 | |
2862 | 2869 | check("void foo(int *p) { int &ref = *p; ref = 12; }"); |
2863 | 2870 | ASSERT_EQUALS("", errout.str()); |
2864 | 2871 | |
2865 | 2872 | check("void foo(int *p) { x = *p + 10; }"); |
2866 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared with const\n", errout.str()); | |
2873 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout.str()); | |
2867 | 2874 | |
2868 | 2875 | check("void foo(int *p) { return p[10]; }"); |
2869 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared with const\n", errout.str()); | |
2876 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout.str()); | |
2870 | 2877 | |
2871 | 2878 | check("void foo(int *p) { int &ref = p[0]; ref = 12; }"); |
2872 | 2879 | ASSERT_EQUALS("", errout.str()); |
2873 | 2880 | |
2874 | 2881 | check("void foo(int *p) { x[*p] = 12; }"); |
2875 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared with const\n", errout.str()); | |
2882 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout.str()); | |
2876 | 2883 | |
2877 | 2884 | check("void foo(int *p) { if (p) {} }"); |
2878 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared with const\n", errout.str()); | |
2885 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout.str()); | |
2879 | 2886 | |
2880 | 2887 | check("void foo(int *p) { if (p || x) {} }"); |
2881 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared with const\n", errout.str()); | |
2888 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout.str()); | |
2882 | 2889 | |
2883 | 2890 | check("void foo(int *p) { if (p == 0) {} }"); |
2884 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared with const\n", errout.str()); | |
2891 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout.str()); | |
2885 | 2892 | |
2886 | 2893 | check("void foo(int *p) { if (!p) {} }"); |
2887 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared with const\n", errout.str()); | |
2894 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout.str()); | |
2888 | 2895 | |
2889 | 2896 | check("void foo(int *p) { if (*p > 123) {} }"); |
2890 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared with const\n", errout.str()); | |
2897 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout.str()); | |
2891 | 2898 | |
2892 | 2899 | check("void foo(int *p) { return *p + 1; }"); |
2893 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared with const\n", errout.str()); | |
2900 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout.str()); | |
2894 | 2901 | |
2895 | 2902 | check("void foo(int *p) { return *p > 1; }"); |
2896 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared with const\n", errout.str()); | |
2903 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout.str()); | |
2897 | 2904 | |
2898 | 2905 | check("void foo(const int* c) { if (c == 0) {}; }"); |
2899 | 2906 | ASSERT_EQUALS("", errout.str()); |
2994 | 3001 | " static int i[1] = {};\n" |
2995 | 3002 | " return i[0];\n" |
2996 | 3003 | "}\n"); |
2997 | ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'i' can be declared with const\n", errout.str()); | |
3004 | ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'i' can be declared as const array\n", errout.str()); | |
2998 | 3005 | |
2999 | 3006 | check("int f() {\n" |
3000 | 3007 | " static int i[] = { 0 };\n" |
3001 | 3008 | " int j = i[0] + 1;\n" |
3002 | 3009 | " return j;\n" |
3003 | 3010 | "}\n"); |
3004 | ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'i' can be declared with const\n", errout.str()); | |
3011 | ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'i' can be declared as const array\n", errout.str()); | |
3005 | 3012 | |
3006 | 3013 | // #10471 |
3007 | 3014 | check("void f(std::array<int, 1> const& i) {\n" |
3024 | 3031 | " for (const auto* p : v)\n" |
3025 | 3032 | " if (p == nullptr) {}\n" |
3026 | 3033 | "}\n"); |
3027 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'v' can be declared with const\n", errout.str()); | |
3034 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'v' can be declared as reference to const\n", errout.str()); | |
3028 | 3035 | |
3029 | 3036 | check("void f(std::vector<const int*>& v) {\n" |
3030 | 3037 | " for (const auto& p : v)\n" |
3032 | 3039 | " for (const auto* p : v)\n" |
3033 | 3040 | " if (p == nullptr) {}\n" |
3034 | 3041 | "}\n"); |
3035 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'v' can be declared with const\n", errout.str()); | |
3042 | ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'v' can be declared as reference to const\n", errout.str()); | |
3036 | 3043 | |
3037 | 3044 | check("void f(const std::vector<const int*>& v) {\n" |
3038 | 3045 | " for (const auto& p : v)\n" |
3070 | 3077 | " tmp = b[i];\n" |
3071 | 3078 | " printf(\"%s\", tmp);\n" |
3072 | 3079 | "}\n"); |
3073 | ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'a' can be declared with const\n" | |
3074 | "[test.cpp:4]: (style) Variable 'b' can be declared with const\n", | |
3080 | ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'a' can be declared as const array\n" | |
3081 | "[test.cpp:4]: (style) Variable 'b' can be declared as const array\n", | |
3075 | 3082 | errout.str()); |
3083 | ||
3084 | check("typedef void* HWND;\n" | |
3085 | "void f(const HWND h) {\n" | |
3086 | " if (h == nullptr) {}\n" | |
3087 | "}\n"); | |
3088 | ASSERT_EQUALS("", errout.str()); | |
3089 | ||
3090 | check("using HWND = void*;\n" | |
3091 | "void f(const HWND h) {\n" | |
3092 | " if (h == nullptr) {}\n" | |
3093 | "}\n"); | |
3094 | ASSERT_EQUALS("", errout.str()); | |
3095 | ||
3096 | check("typedef int A;\n" | |
3097 | "void f(A* x) {\n" | |
3098 | " if (x == nullptr) {}\n" | |
3099 | "}\n"); | |
3100 | ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared as pointer to const\n", errout.str()); | |
3101 | ||
3102 | check("using A = int;\n" | |
3103 | "void f(A* x) {\n" | |
3104 | " if (x == nullptr) {}\n" | |
3105 | "}\n"); | |
3106 | ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared as pointer to const\n", errout.str()); | |
3076 | 3107 | } |
3077 | 3108 | |
3078 | 3109 | void switchRedundantAssignmentTest() { |
5246 | 5277 | |
5247 | 5278 | // #5535: Reference named like its type |
5248 | 5279 | check("void foo() { UMSConfig& UMSConfig = GetUMSConfiguration(); }"); |
5249 | ASSERT_EQUALS("[test.cpp:1]: (style) Variable 'UMSConfig' can be declared with const\n", errout.str()); | |
5280 | ASSERT_EQUALS("[test.cpp:1]: (style) Variable 'UMSConfig' can be declared as reference to const\n", errout.str()); | |
5250 | 5281 | |
5251 | 5282 | // #3868 - false positive (same expression on both sides of |) |
5252 | 5283 | check("void f(int x) {\n" |
9272 | 9303 | " int local_argc = 0;\n" |
9273 | 9304 | " local_argv[local_argc++] = argv[0];\n" |
9274 | 9305 | "}\n", "test.c"); |
9275 | ASSERT_EQUALS("[test.c:1]: (style) Parameter 'argv' can be declared with const\n", errout.str()); | |
9306 | ASSERT_EQUALS("[test.c:1]: (style) Parameter 'argv' can be declared as const array\n", errout.str()); | |
9276 | 9307 | |
9277 | 9308 | check("void f() {\n" |
9278 | 9309 | " int x = 0;\n" |
7743 | 7743 | ASSERT_EQUALS("const signed int", typeOf("; const auto x = 3;", "x")); |
7744 | 7744 | ASSERT_EQUALS("const signed int", typeOf("; constexpr auto x = 3;", "x")); |
7745 | 7745 | ASSERT_EQUALS("const signed int", typeOf("; const constexpr auto x = 3;", "x")); |
7746 | ASSERT_EQUALS("void * const", typeOf("typedef void* HWND; const HWND x = 0;", "x")); | |
7746 | 7747 | |
7747 | 7748 | // Variable declaration |
7748 | 7749 | ASSERT_EQUALS("char *", typeOf("; char abc[] = \"abc\";", "[")); |
150 | 150 | TEST_CASE(localvaralias17); // ticket #8911 |
151 | 151 | TEST_CASE(localvaralias18); // ticket #9234 - iterator |
152 | 152 | TEST_CASE(localvaralias19); // ticket #9828 |
153 | TEST_CASE(localvaralias20); // ticket #10966 | |
153 | 154 | TEST_CASE(localvarasm); |
154 | 155 | TEST_CASE(localvarstatic); |
155 | 156 | TEST_CASE(localvarextern); |
4673 | 4674 | ASSERT_EQUALS("", errout.str()); |
4674 | 4675 | } |
4675 | 4676 | |
4677 | void localvaralias20() { // #10966 | |
4678 | functionVariableUsage("struct A {};\n" | |
4679 | "A g();\n" | |
4680 | "void f() {\n" | |
4681 | " const auto& a = g();\n" | |
4682 | " const auto& b = a;\n" | |
4683 | " const auto&& c = g();\n" | |
4684 | " auto&& d = c;\n" | |
4685 | "}\n"); | |
4686 | TODO_ASSERT_EQUALS("[test.cpp:5]: (style) Variable 'b' is assigned a value that is never used.\n" | |
4687 | "[test.cpp:7]: (style) Variable 'd' is assigned a value that is never used.\n", | |
4688 | "[test.cpp:7]: (style) Variable 'd' is assigned a value that is never used.\n", | |
4689 | errout.str()); | |
4690 | } | |
4691 | ||
4676 | 4692 | void localvarasm() { |
4677 | 4693 | |
4678 | 4694 | functionVariableUsage("void foo(int &b)\n" |
753 | 753 | " return x[0];\n" |
754 | 754 | "}"; |
755 | 755 | ASSERT_EQUALS(0, valueOfTok(code, "[").intvalue); |
756 | ||
757 | code = "int g() { return 3; }\n" | |
758 | "void f() {\n" | |
759 | " const int x[2] = { g(), g() };\n" | |
760 | " return x[0];\n" | |
761 | "}\n"; | |
762 | ASSERT_EQUALS(3, valueOfTok(code, "[ 0").intvalue); | |
763 | ||
764 | code = "int g() { return 3; }\n" | |
765 | "void f() {\n" | |
766 | " const int x[2] = { g(), g() };\n" | |
767 | " return x[1];\n" | |
768 | "}\n"; | |
769 | ASSERT_EQUALS(3, valueOfTok(code, "[ 1").intvalue); | |
756 | 770 | } |
757 | 771 | |
758 | 772 | void valueFlowMove() { |
152 | 152 | TEST_CASE(varid_templateNamespaceFuncPtr); // #4172 |
153 | 153 | TEST_CASE(varid_templateArray); |
154 | 154 | TEST_CASE(varid_templateParameter); // #7046 set varid for "X": std::array<int,X> Y; |
155 | TEST_CASE(varid_templateParameterFunctionPointer); // #11050 | |
155 | 156 | TEST_CASE(varid_templateUsing); // #5781 #7273 |
156 | 157 | TEST_CASE(varid_not_template_in_condition); // #7988 |
157 | 158 | TEST_CASE(varid_cppcast); // #6190 |
2353 | 2354 | const char code[] = "std::optional<N::Foo<A>> Foo;\n"; // #11003 |
2354 | 2355 | |
2355 | 2356 | ASSERT_EQUALS("1: std :: optional < N :: Foo < A > > Foo@1 ;\n", |
2357 | tokenize(code)); | |
2358 | } | |
2359 | } | |
2360 | ||
2361 | void varid_templateParameterFunctionPointer() { | |
2362 | { | |
2363 | const char code[] = "template <class, void (*F)()>\n" | |
2364 | "struct a;\n"; | |
2365 | ||
2366 | ASSERT_EQUALS("1: template < class , void ( * F ) ( ) >\n" | |
2367 | "2: struct a ;\n", | |
2356 | 2368 | tokenize(code)); |
2357 | 2369 | } |
2358 | 2370 | } |
241 | 241 | << " $(warning Usage of SRCDIR to activate match compiler is deprecated. Use MATCHCOMPILER=yes instead.)\n" |
242 | 242 | << " MATCHCOMPILER:=yes\n" |
243 | 243 | << "endif\n"; |
244 | // TODO: bail out when matchcompiler.py fails (i.e. invalid PYTHON_INTERPRETER specified) | |
245 | // TODO: handle "PYTHON_INTERPRETER=" | |
244 | 246 | fout << "ifeq ($(MATCHCOMPILER),yes)\n" |
245 | 247 | << " # Find available Python interpreter\n" |
246 | << " ifndef PYTHON_INTERPRETER\n" | |
248 | << " ifeq ($(PYTHON_INTERPRETER),)\n" | |
247 | 249 | << " PYTHON_INTERPRETER := $(shell which python3)\n" |
248 | 250 | << " endif\n" |
249 | << " ifndef PYTHON_INTERPRETER\n" | |
251 | << " ifeq ($(PYTHON_INTERPRETER),)\n" | |
250 | 252 | << " PYTHON_INTERPRETER := $(shell which python)\n" |
251 | 253 | << " endif\n" |
252 | << " ifndef PYTHON_INTERPRETER\n" | |
254 | << " ifeq ($(PYTHON_INTERPRETER),)\n" | |
253 | 255 | << " $(error Did not find a Python interpreter)\n" |
254 | 256 | << " endif\n" |
255 | 257 | << " ifdef VERIFY\n" |
423 | 425 | fout << "run-dmake: dmake\n"; |
424 | 426 | fout << "\t./dmake\n\n"; |
425 | 427 | fout << "clean:\n"; |
426 | fout << "\trm -f build/*.o lib/*.o cli/*.o test/*.o tools/*.o externals/*/*.o testrunner dmake cppcheck cppcheck.exe cppcheck.1\n\n"; | |
428 | fout << "\trm -f build/*.cpp build/*.o lib/*.o cli/*.o test/*.o tools/*.o externals/*/*.o testrunner dmake cppcheck cppcheck.exe cppcheck.1\n\n"; | |
427 | 429 | fout << "man:\tman/cppcheck.1\n\n"; |
428 | 430 | fout << "man/cppcheck.1:\t$(MAN_SOURCE)\n\n"; |
429 | 431 | fout << "\t$(XP) $(DB2MAN) $(MAN_SOURCE)\n\n"; |
493 | 495 | fout << "\txmllint --noout --relaxng cppcheck-errors.rng /tmp/errorlist.xml\n"; |
494 | 496 | fout << "\txmllint --noout --relaxng cppcheck-errors.rng /tmp/example.xml\n"; |
495 | 497 | fout << "\ncheckCWEEntries: /tmp/errorlist.xml\n"; |
496 | fout << "\t./tools/listErrorsWithoutCWE.py -F /tmp/errorlist.xml\n"; | |
498 | // TODO: handle "PYTHON_INTERPRETER=" | |
499 | fout << "\t$(eval PYTHON_INTERPRETER := $(if $(PYTHON_INTERPRETER),$(PYTHON_INTERPRETER),$(shell which python3)))\n"; | |
500 | fout << "\t$(eval PYTHON_INTERPRETER := $(if $(PYTHON_INTERPRETER),$(PYTHON_INTERPRETER),$(shell which python)))\n"; | |
501 | fout << "\t$(eval PYTHON_INTERPRETER := $(if $(PYTHON_INTERPRETER),$(PYTHON_INTERPRETER),$(error Did not find a Python interpreter)))\n"; | |
502 | fout << "\t$(PYTHON_INTERPRETER) tools/listErrorsWithoutCWE.py -F /tmp/errorlist.xml\n"; | |
497 | 503 | fout << ".PHONY: validateRules\n"; |
498 | 504 | fout << "validateRules:\n"; |
499 | 505 | fout << "\txmllint --noout rules/*.xml\n"; |
24 | 24 | # Version scheme (MAJOR.MINOR.PATCH) should orientate on "Semantic Versioning" https://semver.org/ |
25 | 25 | # Every change in this script should result in increasing the version number accordingly (exceptions may be cosmetic |
26 | 26 | # changes) |
27 | SERVER_VERSION = "1.3.24" | |
28 | ||
29 | OLD_VERSION = '2.7' | |
27 | SERVER_VERSION = "1.3.25" | |
28 | ||
29 | OLD_VERSION = '2.8' | |
30 | 30 | |
31 | 31 | |
32 | 32 | # Set up logging |
10 | 10 | CPPCHECK=$(pwd)/bin/cppcheck |
11 | 11 | fi |
12 | 12 | |
13 | python $DIR/extracttests.py --code=$(pwd)/test1 $1 | |
13 | python3 $DIR/extracttests.py --code=$(pwd)/test1 $1 | |
14 | 14 | |
15 | 15 | cd test1 |
16 | 16 |
262 | 262 | const QStringList lines = item->text().split("\n"); |
263 | 263 | if (lines.size() < 2) |
264 | 264 | return; |
265 | const QString url = lines[0]; | |
265 | const QString &url = lines[0]; | |
266 | 266 | QString msg = lines[1]; |
267 | 267 | const QRegularExpressionMatch matchRes = mVersionRe.match(msg); |
268 | 268 | if (matchRes.hasMatch()) |