diff --git a/.github/workflows/CI-unixish.yml b/.github/workflows/CI-unixish.yml index 0c941e05756..902417375a8 100644 --- a/.github/workflows/CI-unixish.yml +++ b/.github/workflows/CI-unixish.yml @@ -85,6 +85,18 @@ jobs: sudo apt-get update sudo apt-get install libxml2-utils qtbase5-dev qttools5-dev libqt5charts5-dev qtchooser + - name: Set up GCC + uses: egor-tensin/setup-gcc@v1 + if: matrix.os == 'ubuntu-22.04' + with: + version: 13 + platform: x64 + + - name: Select compiler + run: | + echo "CXX=g++-13" >> $GITHUB_ENV + if: matrix.os == 'ubuntu-22.04' + # coreutils contains "nproc" - name: Install missing software on macos if: contains(matrix.os, 'macos') @@ -380,6 +392,7 @@ jobs: run: | ./cppcheck --error-exitcode=1 --inline-suppr --addon=threadsafety addons/test/threadsafety ./cppcheck --error-exitcode=1 --inline-suppr --addon=threadsafety --std=c++03 addons/test/threadsafety + ./cppcheck --error-exitcode=1 --inline-suppr --addon=misra addons/test/misra/crash*.c ./cppcheck --addon=misra --enable=style --inline-suppr --enable=information --error-exitcode=1 addons/test/misra/misra-ctu-*-test.c pushd addons/test # We'll force C89 standard to enable an additional verification for @@ -403,11 +416,6 @@ jobs: ../../cppcheck --dump namingng_test.c python3 ../namingng.py --configfile ../naming.json --verify namingng_test.c.dump - - name: Ensure misra addon does not crash - if: contains(matrix.os, 'ubuntu') - run: | - ./cppcheck --addon=misra addons/test/misra/crash1.c | ( ! grep 'Bailing out from checking' ) - - name: Build democlient if: matrix.os == 'ubuntu-22.04' run: | diff --git a/.github/workflows/asan.yml b/.github/workflows/asan.yml index df2cb0534d4..fdc029ad9f8 100644 --- a/.github/workflows/asan.yml +++ b/.github/workflows/asan.yml @@ -39,6 +39,7 @@ jobs: - name: Install clang run: | + sudo apt-get purge --auto-remove llvm python3-lldb-14 llvm-14 wget https://apt.llvm.org/llvm.sh chmod +x llvm.sh sudo ./llvm.sh 16 diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml index 5352c28118a..c8546db9ab2 100644 --- a/.github/workflows/clang-tidy.yml +++ b/.github/workflows/clang-tidy.yml @@ -27,6 +27,7 @@ jobs: - name: Install clang run: | + sudo apt-get purge --auto-remove llvm python3-lldb-14 llvm-14 wget https://apt.llvm.org/llvm.sh chmod +x llvm.sh sudo ./llvm.sh 16 diff --git a/.github/workflows/iwyu.yml b/.github/workflows/iwyu.yml index 01c705b9da5..f920b0ece67 100644 --- a/.github/workflows/iwyu.yml +++ b/.github/workflows/iwyu.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-22.04 container: - image: "archlinux:latest" + image: "fedora:latest" steps: - uses: actions/checkout@v3 @@ -34,8 +34,8 @@ jobs: pacman -Sy pacman -S cmake make gcc qt5-base qt5-tools qt5-charts pcre wget --noconfirm pacman-key --init - pacman-key --recv-key FBA220DFC880C036 --keyserver keyserver.ubuntu.com - pacman-key --lsign-key FBA220DFC880C036 + pacman-key --recv-key 3056513887B78AEB --keyserver keyserver.ubuntu.com + pacman-key --lsign-key 3056513887B78AEB pacman -U 'https://cdn-mirror.chaotic.cx/chaotic-aur/chaotic-keyring.pkg.tar.zst' 'https://cdn-mirror.chaotic.cx/chaotic-aur/chaotic-mirrorlist.pkg.tar.zst' --noconfirm echo "[chaotic-aur]" >> /etc/pacman.conf echo "Include = /etc/pacman.d/chaotic-mirrorlist" >> /etc/pacman.conf @@ -43,6 +43,14 @@ jobs: pacman -S include-what-you-use --noconfirm ln -s iwyu-tool /usr/sbin/iwyu_tool + # TODO: the necessary packages are excessive - mostly because of Qt - use a pre-built image + - name: Install missing software on Fedora + run: | + dnf install -y cmake gcc-c++ qt5-qtbase-devel qt5-linguist qt5-qttools-devel qt5-qtcharts-devel pcre-devel + dnf install -y wget iwyu + ln -s iwyu_tool.py /usr/bin/iwyu_tool + ln -s qt5 /usr/include/qt + - name: Prepare CMake run: | cmake -S . -B cmake.output -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=On -DWITH_QCHART=On -DENABLE_CHECK_INTERNAL=On -DCMAKE_GLOBAL_AUTOGEN_TARGET=On -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCPPCHK_GLIBCXX_DEBUG=Off -DUSE_MATCHCOMPILER=Off @@ -107,6 +115,7 @@ jobs: - name: Install clang run: | + sudo apt-get purge --auto-remove llvm python3-lldb-14 llvm-14 wget https://apt.llvm.org/llvm.sh chmod +x llvm.sh sudo ./llvm.sh 17 diff --git a/.github/workflows/scriptcheck.yml b/.github/workflows/scriptcheck.yml index 13b87c37ff5..6ef6d90ee4c 100644 --- a/.github/workflows/scriptcheck.yml +++ b/.github/workflows/scriptcheck.yml @@ -37,12 +37,11 @@ jobs: needs: build # 'ubuntu-22.04' removes Python 2.7, 3.5 and 3.6 so keep the previous LTS version + # 'ubutunu-20.04' no longer works on 2.7 - TODO: re-added in a different way or remove support for it? runs-on: ubuntu-20.04 - container: - image: python:2.7.18-buster strategy: matrix: - python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9, '3.10', '3.11'] + python-version: [3.5, 3.6, 3.7, 3.8, 3.9, '3.10', '3.11'] fail-fast: false steps: diff --git a/.github/workflows/tsan.yml b/.github/workflows/tsan.yml index fc3af112481..5a5aaa2c900 100644 --- a/.github/workflows/tsan.yml +++ b/.github/workflows/tsan.yml @@ -39,6 +39,7 @@ jobs: - name: Install clang run: | + sudo apt-get purge --auto-remove llvm python3-lldb-14 llvm-14 wget https://apt.llvm.org/llvm.sh chmod +x llvm.sh sudo ./llvm.sh 16 diff --git a/.github/workflows/ubsan.yml b/.github/workflows/ubsan.yml index 1de89f66ff5..ed6d4e61f6c 100644 --- a/.github/workflows/ubsan.yml +++ b/.github/workflows/ubsan.yml @@ -39,6 +39,7 @@ jobs: - name: Install clang run: | + sudo apt-get purge --auto-remove llvm python3-lldb-14 llvm-14 wget https://apt.llvm.org/llvm.sh chmod +x llvm.sh sudo ./llvm.sh 16 diff --git a/AUTHORS b/AUTHORS index e3610324298..4463df0c5ec 100644 --- a/AUTHORS +++ b/AUTHORS @@ -315,6 +315,7 @@ Ryan Pavlik Samir Aguiar Sam Truscott Samuel Degrande +Samuel Poláček Sandeep Dutta Savvas Etairidis Scott Furry diff --git a/Makefile b/Makefile index 81e18beca52..fe2665a7726 100644 --- a/Makefile +++ b/Makefile @@ -667,7 +667,7 @@ cli/stacktrace.o: cli/stacktrace.cpp cli/stacktrace.h lib/config.h lib/utils.h cli/threadexecutor.o: cli/threadexecutor.cpp cli/cppcheckexecutor.h cli/executor.h cli/threadexecutor.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/threadexecutor.cpp -test/fixture.o: test/fixture.cpp externals/tinyxml2/tinyxml2.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h test/fixture.h test/options.h test/redirect.h +test/fixture.o: test/fixture.cpp externals/tinyxml2/tinyxml2.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h test/fixture.h test/options.h test/redirect.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/fixture.cpp test/helpers.o: test/helpers.cpp externals/simplecpp/simplecpp.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/helpers.h @@ -862,7 +862,7 @@ test/testunusedprivfunc.o: test/testunusedprivfunc.cpp externals/simplecpp/simpl test/testunusedvar.o: test/testunusedvar.cpp externals/simplecpp/simplecpp.h lib/check.h lib/checkunusedvar.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testunusedvar.cpp -test/testutils.o: test/testutils.cpp lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h +test/testutils.o: test/testutils.cpp lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h test/fixture.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testutils.cpp test/testvaarg.o: test/testvaarg.cpp lib/check.h lib/checkvaarg.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h diff --git a/addons/misra_9.py b/addons/misra_9.py index 8a9a6287871..f0f73371906 100644 --- a/addons/misra_9.py +++ b/addons/misra_9.py @@ -1,3 +1,5 @@ +import cppcheckdata + # Holds information about an array, struct or union's element definition. class ElementDef: def __init__(self, elementType, name, valueType, dimensions = None): @@ -30,7 +32,7 @@ def __repr__(self): attrs = ["childIndex", "elementType", "valueType"] return "{}({}, {}, {})".format( - "ED", + "ElementDef", self.getLongName(), inits, ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) @@ -253,6 +255,16 @@ def parseInitializer(self, root, token): isFirstElement = False isDesignated = True + elif self.token.isString and self.ed and self.ed.isArray: + self.ed.setInitialized(isDesignated) + if self.token == self.token.astParent.astOperand1 and self.token.astParent.astOperand2: + self.token = self.token.astParent.astOperand2 + self.ed.markAsCurrent() + self.ed = self.root.getNextChild() + else: + self.unwindAndContinue() + continue + elif self.token.str == '{': nextChild = self.root.getNextChild() if self.root is not None else None @@ -314,6 +326,7 @@ def parseInitializer(self, root, token): else: self.ed.parent.setInitialized(isDesignated) self.ed.parent.initializeChildren() + else: if self.ed.parent != self.root: # Check if token is correct value type for self.root.children[?] @@ -333,6 +346,8 @@ def parseInitializer(self, root, token): parent = parent.parent isDesignated = False + if self.token.isString and self.ed.parent.isArray: + self.ed = self.ed.parent self.unwindAndContinue() def pushToRootStackAndMarkAsDesignated(self): @@ -381,6 +396,39 @@ def unwindAndContinue(self): break def misra_9_x(self, data, rule, rawTokens = None): + # If there are arrays with unknown size constants then we need to warn about missing configuration + # and bailout + has_config_errors = False + for var in data.variables: + if not var.isArray or var.nameToken is None or not cppcheckdata.simpleMatch(var.nameToken.next,'['): + continue + tok = var.nameToken.next + while tok.str == '[': + sz = tok.astOperand2 + if sz and sz.getKnownIntValue() is None: + has_var = False + unknown_constant = False + tokens = [sz] + while len(tokens) > 0: + t = tokens[-1] + tokens = tokens[:-1] + if t: + if t.isName and t.getKnownIntValue() is None: + if t.varId or t.variable: + has_var = True + continue + unknown_constant = True + cppcheckdata.reportError(sz, 'error', 'Unknown constant {}, please review configuration'.format(t.str), 'misra', 'config') + has_config_errors = True + if t.isArithmeticalOp: + tokens += [t.astOperand1, t.astOperand2] + if not unknown_constant and not has_var: + cppcheckdata.reportError(sz, 'error', 'Unknown array size, please review configuration', 'misra', 'config') + has_config_errors = True + tok = tok.link.next + if has_config_errors: + return + parser = InitializerParser() for variable in data.variables: @@ -441,7 +489,7 @@ def getElementDef(nameToken, rawTokens = None): return ed def createArrayChildrenDefs(ed, token, var, rawTokens = None): - if token.str == '[': + if token and token.str == '[': if rawTokens is not None: foundToken = next((rawToken for rawToken in rawTokens if rawToken.file == token.file @@ -476,7 +524,15 @@ def createRecordChildrenDefs(ed, var): valueType = ed.valueType if not valueType or not valueType.typeScope: return - + if var is None: + return + typeToken = var.typeEndToken + while typeToken and typeToken.isName: + typeToken = typeToken.previous + if typeToken and typeToken.str == '*': + child = ElementDef("pointer", var.nameToken, var.nameToken.valueType) + ed.addChild(child) + return for variable in valueType.typeScope.varlist: if variable is var: continue diff --git a/addons/test/misra/crash2.c b/addons/test/misra/crash2.c new file mode 100644 index 00000000000..c736a265d15 --- /dev/null +++ b/addons/test/misra/crash2.c @@ -0,0 +1,9 @@ + +// #11793 + +typedef struct pfmlib_pmu { + int flags ; + int (*get_event_encoding[10])(void* this, pfmlib_event_desc_t* e); +} pfmlib_pmu_t ; + +pfmlib_pmu_t sparc_ultra3_support = { .flags = 0 }; diff --git a/addons/test/misra/crash3.c b/addons/test/misra/crash3.c new file mode 100644 index 00000000000..19583f009a5 --- /dev/null +++ b/addons/test/misra/crash3.c @@ -0,0 +1,30 @@ + + + + +/* This is the representation of the expressions to determine the + plural form. */ +struct expression +{ + int nargs; /* Number of arguments. */ + union + { + unsigned long int num; /* Number value for `num'. */ + struct expression *args[3]; /* Up to three arguments. */ + } val; +}; + + +struct expression GERMANIC_PLURAL = +{ + .nargs = 2, + .val = + { + .args = + { + [0] = (struct expression *) &plvar, + [1] = (struct expression *) &plone + } + } +}; + diff --git a/addons/test/misra/crash4.c b/addons/test/misra/crash4.c new file mode 100644 index 00000000000..7862581849f --- /dev/null +++ b/addons/test/misra/crash4.c @@ -0,0 +1,11 @@ + +struct ConDesDesc { + unsigned Order; + unsigned Import; +}; + +// cppcheck-suppress misra-config +static ConDesDesc ConDes[CD_TYPE_COUNT] = { + { 0, 0 }, + { 0, 0 }, +}; diff --git a/addons/test/misra/crash5.c b/addons/test/misra/crash5.c new file mode 100644 index 00000000000..860101539da --- /dev/null +++ b/addons/test/misra/crash5.c @@ -0,0 +1,23 @@ + +struct _boardcnf_ch { + // cppcheck-suppress misra-config + uint8_t ddr_density[CS_CNT]; + uint64_t ca_swap; +}; + +struct _boardcnf { + + uint16_t dqdm_dly_r; + // cppcheck-suppress misra-config + struct _boardcnf_ch ch[DRAM_CH_CNT]; +}; + +static const struct _boardcnf boardcnfs[1] = { + { + 0x0a0, + { + { {0x02, 0x02}, 0x00345201 }, + { {0x02, 0x02}, 0x00302154 } + } + }, +}; diff --git a/addons/test/misra/crash6.c b/addons/test/misra/crash6.c new file mode 100644 index 00000000000..bf77e5f650b --- /dev/null +++ b/addons/test/misra/crash6.c @@ -0,0 +1,17 @@ + + +typedef struct _tGames +{ + char magicdirname[10]; + unsigned int expectedmask; + unsigned char pictureorder[3]; +} tGames; + +static const tGames games[1]={ + {"Pawn", 1, {0,1,2}} +}; + + + + + diff --git a/addons/test/misra/crash7.c b/addons/test/misra/crash7.c new file mode 100644 index 00000000000..210cfeb4df8 --- /dev/null +++ b/addons/test/misra/crash7.c @@ -0,0 +1,8 @@ + + +static const struct id3_frametype wordlist[] = +{ + {0, "Encryption method registration"}, + {1, "Popularimeter"}, +}; + diff --git a/addons/test/misra/crash8.c b/addons/test/misra/crash8.c new file mode 100644 index 00000000000..970d72540f0 --- /dev/null +++ b/addons/test/misra/crash8.c @@ -0,0 +1,10 @@ + +struct three_d_filter_t { + char name[16]; + double elem[2]; +}; + +static three_d_filter_t base_filters[] = { + {"Identity", { 1.0, 0.0 } }, + {"Echo", { 0.4, 0.0 } } +}; diff --git a/addons/test/misra/crash9.c b/addons/test/misra/crash9.c new file mode 100644 index 00000000000..5abf2b8ad02 --- /dev/null +++ b/addons/test/misra/crash9.c @@ -0,0 +1,3 @@ + +#line 3 "" +typedef int8_t flex_int8_t; diff --git a/addons/test/misra/misra-test.c b/addons/test/misra/misra-test.c index 8467433926c..2fd30c7e707 100644 --- a/addons/test/misra/misra-test.c +++ b/addons/test/misra/misra-test.c @@ -586,10 +586,6 @@ static void misra_9_struct_initializers(void) { struct1 os1 = { i1: 1, i2: 2 }; // 10.4 13.4 } -static void misra_9_broken_initializers(void) { - char a[UNKNOWN_MACRO] = { 19, 23, 0 }; // 18.8 -} - static void misra_9_2(void) { union misra_9_2_union { // 19.2 char c; diff --git a/cfg/posix.cfg b/cfg/posix.cfg index f07f8fe8b44..7cde306a3ad 100644 --- a/cfg/posix.cfg +++ b/cfg/posix.cfg @@ -2197,7 +2197,7 @@ The function 'mktemp' is considered to be dangerous due to race conditions and s - + @@ -2209,7 +2209,7 @@ The function 'mktemp' is considered to be dangerous due to race conditions and s - + @@ -2221,7 +2221,7 @@ The function 'mktemp' is considered to be dangerous due to race conditions and s - + diff --git a/cfg/std.cfg b/cfg/std.cfg index fa499cf16c3..9577a6fc366 100644 --- a/cfg/std.cfg +++ b/cfg/std.cfg @@ -8855,7 +8855,6 @@ initializer list (7) string& replace (const_iterator i1, const_iterator i2, init - diff --git a/cfg/zephyr.cfg b/cfg/zephyr.cfg new file mode 100644 index 00000000000..999de4ea1b5 --- /dev/null +++ b/cfg/zephyr.cfg @@ -0,0 +1,113 @@ + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + 0:2147483647 + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 36cab675dc8..b85227328d8 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -44,7 +44,6 @@ #include #include #include // IWYU pragma: keep -#include #include #include @@ -53,10 +52,6 @@ #include #endif -#ifdef __linux__ -#include -#endif - static bool addFilesToList(const std::string& fileList, std::vector& pathNames) { std::istream *files; @@ -259,7 +254,7 @@ bool CmdLineParser::parseFromArgs(int argc, const char* const argv[]) } else if (std::strncmp(argv[i], "--checks-max-time=", 18) == 0) { - if (!parseNumberArg(argv[i], 18, mSettings.checksMaxTime)) + if (!parseNumberArg(argv[i], 18, mSettings.checksMaxTime, true)) return false; } @@ -1027,7 +1022,7 @@ bool CmdLineParser::parseFromArgs(int argc, const char* const argv[]) else if ((def || mSettings.preprocessOnly) && !maxconfigs) mSettings.maxConfigs = 1U; - if (mSettings.checks.isEnabled(Checks::unusedFunction) && mSettings.jobs > 1) { + if (mSettings.checks.isEnabled(Checks::unusedFunction) && mSettings.jobs > 1 && mSettings.buildDir.empty()) { printMessage("unusedFunction check can't be used with '-j' option. Disabling unusedFunction check."); } diff --git a/cli/cmdlineparser.h b/cli/cmdlineparser.h index ae08452e3b3..2cd5df1cd56 100644 --- a/cli/cmdlineparser.h +++ b/cli/cmdlineparser.h @@ -19,6 +19,7 @@ #ifndef CMDLINE_PARSER_H #define CMDLINE_PARSER_H +#include #include #include @@ -123,9 +124,8 @@ class CmdLineParser { private: bool isCppcheckPremium() const; - // TODO: get rid of is_signed template - static bool parseNumberArg(const char* const arg, std::size_t offset, T& num, bool is_signed = false) + static bool parseNumberArg(const char* const arg, std::size_t offset, T& num, bool mustBePositive = false) { T tmp; std::string err; @@ -133,7 +133,7 @@ class CmdLineParser { printError("argument to '" + std::string(arg, offset) + "' is not valid - " + err + "."); return false; } - if (is_signed && tmp < 0) { + if (mustBePositive && tmp < 0) { printError("argument to '" + std::string(arg, offset) + "' needs to be a positive integer."); return false; } diff --git a/cli/processexecutor.cpp b/cli/processexecutor.cpp index 005cc0aa29b..e6aca40ee3a 100644 --- a/cli/processexecutor.cpp +++ b/cli/processexecutor.cpp @@ -257,7 +257,7 @@ unsigned int ProcessExecutor::check() std::exit(EXIT_FAILURE); } - if (fcntl(pipes[0], F_SETFL, flags | O_NONBLOCK) < 0) { + if (fcntl(pipes[0], F_SETFL, flags) < 0) { std::cerr << "#### ThreadExecutor::check, fcntl(F_SETFL) failed: "<< std::strerror(errno) << std::endl; std::exit(EXIT_FAILURE); } diff --git a/cmake/versions.cmake b/cmake/versions.cmake index bceb26b8918..3488176c896 100644 --- a/cmake/versions.cmake +++ b/cmake/versions.cmake @@ -1,4 +1,5 @@ # Version for libraries CPP +# Version string must have 3 "parts". https://sourceforge.net/p/cppcheck/discussion/development/thread/e57efb2b62/ SET(VERSION "2.11.99") STRING(REGEX MATCHALL "[0-9]+" VERSION_PARTS "${VERSION}") LIST(GET VERSION_PARTS 0 VERSION_MAJOR) diff --git a/createrelease b/createrelease index 905ce4b5d54..c99964be13d 100755 --- a/createrelease +++ b/createrelease @@ -31,7 +31,7 @@ # # Update version numbers in: # sed -i -r "s/version 2[.][0-9]+([.]99)*/version 2.9/" cli/main.cpp -# sed -i -r "s|2[.][0-9]+([.]99)*|2.9|" cmake/versions.cmake +# sed -i -r "s|2[.][0-9]+([.]99)*|2.9.0|" cmake/versions.cmake # version must have 3 parts. # sed -i -r "s/MINOR [0-9]+/MINOR 9/" lib/version.h # sed -i -r "s/2[.][0-9]+([.]99)*/2.9/" win_installer/productInfo.wxi # sed -i -r "s/subtitle: Version 2\.[0-9]+/subtitle: Version 2.9/" man/*.md diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index c646f15fdd1..2614b1a76ae 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -9,10 +9,10 @@ CheckOptions: - { key: HeaderFileExtensions, value: 'x' } ") - add_compile_definitions($<$>:-DQT_NO_DEBUG>) - add_compile_definitions($<$>:-DQT_NO_DEBUG_OUTPUT>) - add_compile_definitions($<$>:-DQT_NO_WARNING_OUTPUT>) - add_compile_definitions($<$:-DQT_DEBUG>) + add_compile_definitions($<$>:QT_NO_DEBUG>) + add_compile_definitions($<$>:QT_NO_DEBUG_OUTPUT>) + add_compile_definitions($<$>:QT_NO_WARNING_OUTPUT>) + add_compile_definitions($<$:QT_DEBUG>) file(GLOB hdrs "*.h") file(GLOB srcs "*.cpp") diff --git a/gui/compliancereportdialog.cpp b/gui/compliancereportdialog.cpp index 64c365e6568..10ab0b424de 100644 --- a/gui/compliancereportdialog.cpp +++ b/gui/compliancereportdialog.cpp @@ -1,3 +1,21 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #include "compliancereportdialog.h" #include "ui_compliancereportdialog.h" @@ -16,6 +34,7 @@ #include #include +#include #include #include #include @@ -97,9 +116,11 @@ void ComplianceReportDialog::buttonClicked(QAbstractButton* button) void ComplianceReportDialog::save() { + const QString std(mUI->mCodingStandard->currentText().toLower().replace(" ", "-")); + const QString outFile = QFileDialog::getSaveFileName(this, tr("Compliance report"), - QDir::homePath() + "/misra-c-2012-compliance-report.html", + QDir::homePath() + "/" + std + "-compliance-report.html", tr("HTML files (*.html)")); if (outFile.isEmpty()) return; @@ -166,11 +187,18 @@ void ComplianceReportDialog::save() tempFiles.close(); } - QStringList args{"--compliant=misra-c2012-1.1", - "--compliant=misra-c2012-1.2", - "--project-name=" + projectName, + QStringList args{"--project-name=" + projectName, "--project-version=" + projectVersion, "--output-file=" + outFile}; + + if (std.startsWith("misra-c-")) { + args << "--misra-c" + << "--compliant=misra-c2012-1.1" + << "--compliant=misra-c2012-1.2"; + } else { + args << ("--" + std); + } + if (files) args << "--files=" + tempFiles.fileName(); args << mResultsFile; diff --git a/gui/compliancereportdialog.h b/gui/compliancereportdialog.h index 99bf1848903..ad3b79c5c45 100644 --- a/gui/compliancereportdialog.h +++ b/gui/compliancereportdialog.h @@ -1,3 +1,21 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #ifndef COMPLIANCEREPORTDIALOG_H #define COMPLIANCEREPORTDIALOG_H diff --git a/gui/compliancereportdialog.ui b/gui/compliancereportdialog.ui index 8e260c2dc8c..b65ef626eed 100644 --- a/gui/compliancereportdialog.ui +++ b/gui/compliancereportdialog.ui @@ -16,25 +16,51 @@ - + + + + Project version + + + + Project name - + - - + + + + + - Project version + Coding Standard - - + + + + + Misra C 2023 + + + + + Cert C + + + + + Cert C++ + + + diff --git a/gui/cppcheck_ja.ts b/gui/cppcheck_ja.ts index 9d88ed3d802..8bb3d5079e9 100644 --- a/gui/cppcheck_ja.ts +++ b/gui/cppcheck_ja.ts @@ -120,42 +120,42 @@ Parameters: -l(line) (file) Compliance Report - + コンプライアンスレポート Project name - + プロジェクト名 Project version - + プロジェクトバージョン List of files with md5 checksums - + md5チェックサムつきファイルリスト Compliance report - + コンプライアンスレポート HTML files (*.html) - + HTMLファイル(*.html) Save compliance report - + コンプライアンスレポートの保存 Failed to import '%1', can not show files in compliance report - + '%1'のインポートに失敗しました、コンプライアンスレポートでファイルを表示できません @@ -778,12 +778,12 @@ Parameters: -l(line) (file) Checking for updates - + 更新の確認 Hide - 非表示 + 非表示 @@ -1019,7 +1019,7 @@ Parameters: -l(line) (file) Compliance report... - + コンプライアンスレポート... @@ -1173,34 +1173,36 @@ Do you want to load this project file instead? Failed to open file - + ファイルを開くのに失敗しました Unknown project file format - + プロジェクトファイルの形式が不明です Failed to import project file - + プロジェクトファイルのインポートに失敗しました Failed to import '%1': %2 Analysis is stopped. - + インポートに失敗'%1': %2 + +解析を停止しました。 Install - + インストール New version available: %1. %2 - + 新しいバージョンが利用可能です。: %1. %2 @@ -1628,12 +1630,12 @@ Options: Cert C++ - + Cert C++ Bug hunting (Premium) - + バグハンティング(プレミアム) @@ -2431,7 +2433,7 @@ To toggle what kind of errors are shown, open view menu. Check for updates - + 更新の確認 diff --git a/gui/librarydialog.cpp b/gui/librarydialog.cpp index e94dd95f8bd..47545679922 100644 --- a/gui/librarydialog.cpp +++ b/gui/librarydialog.cpp @@ -25,8 +25,6 @@ #include "ui_librarydialog.h" -#include - #include #include #include diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 6e2605979d0..2840c48330d 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -975,6 +975,10 @@ Settings MainWindow::getCppcheckSettings() result.maxCtuDepth = mProjectFile->getMaxCtuDepth(); result.maxTemplateRecursion = mProjectFile->getMaxTemplateRecursion(); + if (mProjectFile->isCheckLevelExhaustive()) + result.setCheckLevelExhaustive(); + else + result.setCheckLevelNormal(); result.checkHeaders = mProjectFile->getCheckHeaders(); result.checkUnusedTemplates = mProjectFile->getCheckUnusedTemplates(); result.safeChecks.classes = mProjectFile->safeChecks.classes; diff --git a/gui/projectfile.cpp b/gui/projectfile.cpp index 3c7d669f677..349c91ebe0a 100644 --- a/gui/projectfile.cpp +++ b/gui/projectfile.cpp @@ -23,7 +23,6 @@ #include "importproject.h" #include "settings.h" -#include #include #include @@ -58,6 +57,7 @@ void ProjectFile::clear() { const Settings settings; clangParser = false; + mCheckLevel = CheckLevel::normal; mRootPath.clear(); mBuildDir.clear(); mImportProject.clear(); @@ -141,6 +141,9 @@ bool ProjectFile::read(const QString &filename) if (xmlReader.name() == QString(CppcheckXml::CheckUnusedTemplatesElementName)) mCheckUnusedTemplates = readBool(xmlReader); + if (xmlReader.name() == QString(CppcheckXml::CheckLevelExhaustiveElementName)) + mCheckLevel = CheckLevel::exhaustive; + // Find include directory from inside project element if (xmlReader.name() == QString(CppcheckXml::IncludeDirElementName)) readIncludeDirs(xmlReader); @@ -780,6 +783,16 @@ void ProjectFile::setVSConfigurations(const QStringList &vsConfigs) mVsConfigurations = vsConfigs; } +void ProjectFile::setCheckLevel(ProjectFile::CheckLevel checkLevel) +{ + mCheckLevel = checkLevel; +} + +bool ProjectFile::isCheckLevelExhaustive() const +{ + return mCheckLevel == CheckLevel::exhaustive; +} + void ProjectFile::setWarningTags(std::size_t hash, const QString& tags) { if (tags.isEmpty()) @@ -978,6 +991,11 @@ bool ProjectFile::write(const QString &filename) } } + if (mCheckLevel == CheckLevel::exhaustive) { + xmlWriter.writeStartElement(CppcheckXml::CheckLevelExhaustiveElementName); + xmlWriter.writeEndElement(); + } + // Cppcheck Premium if (mBughunting) { xmlWriter.writeStartElement(CppcheckXml::BughuntingElementName); diff --git a/gui/projectfile.h b/gui/projectfile.h index 7926faf2b0a..50d87ea5663 100644 --- a/gui/projectfile.h +++ b/gui/projectfile.h @@ -53,6 +53,11 @@ class ProjectFile : public QObject { if (this == mActiveProject) mActiveProject = nullptr; } + enum class CheckLevel { + normal, + exhaustive + }; + static ProjectFile* getActiveProject() { return mActiveProject; } @@ -329,6 +334,10 @@ class ProjectFile : public QObject { */ void setVSConfigurations(const QStringList &vsConfigs); + /** CheckLevel: normal/exhaustive */ + void setCheckLevel(CheckLevel checkLevel); + bool isCheckLevelExhaustive() const; + /** * @brief Set tags. * @param tags tag list @@ -587,7 +596,10 @@ class ProjectFile : public QObject { */ QStringList mAddons; - bool mBughunting; + bool mBughunting = false; + + /** @brief Should Cppcheck run normal or exhaustive analysis? */ + CheckLevel mCheckLevel = CheckLevel::normal; /** * @brief List of coding standards, checked by Cppcheck Premium. @@ -597,6 +609,7 @@ class ProjectFile : public QObject { /** @brief Project name, used when generating compliance report */ QString mProjectName; + /** @brief Cppcheck Premium: This value is passed to the Cert C checker if that is enabled */ int mCertIntPrecision; /** @brief Execute clang analyzer? */ diff --git a/gui/projectfile.ui b/gui/projectfile.ui index a2e47484ec7..79ed0908fd5 100644 --- a/gui/projectfile.ui +++ b/gui/projectfile.ui @@ -7,7 +7,7 @@ 0 0 940 - 617 + 701 @@ -402,7 +402,7 @@ Analysis - + @@ -452,6 +452,29 @@ + + + + Check level + + + + + + Normal -- meant for normal analysis in CI. Analysis should finish in reasonable time. + + + + + + + Exhaustive -- meant for nightly builds etc. Analysis time can be longer (10x slower than compilation is OK). + + + + + + diff --git a/gui/projectfiledialog.cpp b/gui/projectfiledialog.cpp index 29dd2b41998..6aa5e0cd392 100644 --- a/gui/projectfiledialog.cpp +++ b/gui/projectfiledialog.cpp @@ -34,16 +34,13 @@ #include #include -#include #include #include #include #include #include -#include #include #include -#include #include #include #include @@ -307,6 +304,10 @@ void ProjectFileDialog::loadFromProjectFile(const ProjectFile *projectFile) else item->setCheckState(Qt::Unchecked); } + if (projectFile->isCheckLevelExhaustive()) + mUI->mCheckLevelExhaustive->setChecked(true); + else + mUI->mCheckLevelNormal->setChecked(true); mUI->mCheckHeaders->setChecked(projectFile->getCheckHeaders()); mUI->mCheckUnusedTemplates->setChecked(projectFile->getCheckUnusedTemplates()); mUI->mMaxCtuDepth->setValue(projectFile->getMaxCtuDepth()); @@ -428,6 +429,7 @@ void ProjectFileDialog::saveToProjectFile(ProjectFile *projectFile) const projectFile->setCheckPaths(getCheckPaths()); projectFile->setExcludedPaths(getExcludedPaths()); projectFile->setLibraries(getLibraries()); + projectFile->setCheckLevel(mUI->mCheckLevelExhaustive->isChecked() ? ProjectFile::CheckLevel::exhaustive : ProjectFile::CheckLevel::normal); projectFile->clangParser = mUI->mBtnClangParser->isChecked(); projectFile->safeChecks.classes = mUI->mBtnSafeClasses->isChecked(); if (mUI->mComboBoxPlatform->currentText().endsWith(".xml")) diff --git a/gui/resultstree.cpp b/gui/resultstree.cpp index 12574484f27..3b1895dd757 100644 --- a/gui/resultstree.cpp +++ b/gui/resultstree.cpp @@ -31,8 +31,6 @@ #include "threadhandler.h" #include "xmlreportv2.h" -#include - #include #include #include diff --git a/lib/astutils.cpp b/lib/astutils.cpp index 20d36c54ce7..94307ab276f 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -1458,10 +1458,10 @@ bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2 tok2 = tok2->astOperand2(); } // Skip double not - if (Token::simpleMatch(tok1, "!") && Token::simpleMatch(tok1->astOperand1(), "!") && !Token::simpleMatch(tok1->astParent(), "=")) { + if (Token::simpleMatch(tok1, "!") && Token::simpleMatch(tok1->astOperand1(), "!") && !Token::simpleMatch(tok1->astParent(), "=") && astIsBoolLike(tok2)) { return isSameExpression(cpp, macro, tok1->astOperand1()->astOperand1(), tok2, library, pure, followVar, errors); } - if (Token::simpleMatch(tok2, "!") && Token::simpleMatch(tok2->astOperand1(), "!") && !Token::simpleMatch(tok2->astParent(), "=")) { + if (Token::simpleMatch(tok2, "!") && Token::simpleMatch(tok2->astOperand1(), "!") && !Token::simpleMatch(tok2->astParent(), "=") && astIsBoolLike(tok1)) { return isSameExpression(cpp, macro, tok1, tok2->astOperand1()->astOperand1(), library, pure, followVar, errors); } const bool tok_str_eq = tok1->str() == tok2->str(); @@ -2196,7 +2196,7 @@ T* getTokenArgumentFunctionImpl(T* tok, int& argn) parent = parent->astParent(); // passing variable to subfunction? - if (Token::Match(parent, "[(,{]")) + if (Token::Match(parent, "[[(,{]")) ; else if (Token::simpleMatch(parent, ":")) { while (Token::Match(parent, "[?:]")) @@ -2977,9 +2977,12 @@ T* findLambdaEndTokenGeneric(T* first) return nullptr; if (!maybeLambda(first->previous())) return nullptr; - if (!Token::Match(first->link(), "] (|{")) + if (!Token::Match(first->link(), "] (|{|<")) return nullptr; - if (first->astOperand1() != first->link()->next()) + const Token* roundOrCurly = first->link()->next(); + if (roundOrCurly->link() && roundOrCurly->str() == "<") + roundOrCurly = roundOrCurly->link()->next(); + if (first->astOperand1() != roundOrCurly) return nullptr; T * tok = first; @@ -3030,7 +3033,7 @@ bool isLikelyStreamRead(bool cpp, const Token *op) const Token *parent = op; while (parent->astParent() && parent->astParent()->str() == op->str()) parent = parent->astParent(); - if (parent->astParent() && !Token::Match(parent->astParent(), "%oror%|&&|(|,|.|!|;")) + if (parent->astParent() && !Token::Match(parent->astParent(), "%oror%|&&|(|,|.|!|;|return")) return false; if (op->str() == "&" && parent->astParent()) return false; @@ -3130,6 +3133,8 @@ ExprUsage getExprUsage(const Token* tok, int indirect, const Settings* settings) return ExprUsage::NotUsed; if (tok->astParent()->isCast()) return ExprUsage::NotUsed; + if (Token::simpleMatch(tok->astParent(), ":") && Token::simpleMatch(tok->astParent()->astParent(), "?")) + return getExprUsage(tok->astParent()->astParent(), indirect, settings); } if (indirect == 0) { if (Token::Match(tok->astParent(), "%cop%|%assign%|++|--") && !Token::simpleMatch(tok->astParent(), "=") && diff --git a/lib/checkautovariables.cpp b/lib/checkautovariables.cpp index 8dca0990636..6e4322ebfad 100644 --- a/lib/checkautovariables.cpp +++ b/lib/checkautovariables.cpp @@ -303,7 +303,7 @@ void CheckAutoVariables::autoVariables() if (v.isImpossible()) continue; if ((v.isTokValue() && (isArrayVar(v.tokvalue) || ((v.tokvalue->tokType() == Token::eString)))) || - (v.isLocalLifetimeValue() && v.lifetimeKind == ValueFlow::Value::LifetimeKind::Address)) { + (v.isLocalLifetimeValue() && v.lifetimeKind == ValueFlow::Value::LifetimeKind::Address && !Token::simpleMatch(v.tokvalue, "("))) { errorInvalidDeallocation(tok, &v); break; } diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 0efd37f12a0..781b04e2cea 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -34,7 +34,6 @@ #include #include -#include #include #include #include @@ -1067,7 +1066,7 @@ void CheckClass::noConstructorError(const Token *tok, const std::string &classna void CheckClass::noExplicitConstructorError(const Token *tok, const std::string &classname, bool isStruct) { const std::string message(std::string(isStruct ? "Struct" : "Class") + " '$symbol' has a constructor with 1 argument that is not explicit."); - const std::string verbose(message + " Such constructors should in general be explicit for type safety reasons. Using the explicit keyword in the constructor means some mistakes when using the class can be avoided."); + const std::string verbose(message + " Such, so called \"Converting constructors\", should in general be explicit for type safety reasons as that prevents unintended implicit conversions."); reportError(tok, Severity::style, "noExplicitConstructor", "$symbol:" + classname + '\n' + message + '\n' + verbose, CWE398, Certainty::normal); } @@ -2098,6 +2097,17 @@ void CheckClass::checkConst() const bool returnsPtrOrRef = isPointerOrReference(func.retDef, func.tokenDef); + if (Function::returnsPointer(&func, /*unknown*/ true) || Function::returnsReference(&func, /*unknown*/ true, /*includeRValueRef*/ true)) { // returns const/non-const depending on template arg + bool isTemplateArg = false; + for (const Token* tok2 = func.retDef; precedes(tok2, func.token); tok2 = tok2->next()) + if (tok2->isTemplateArg() && tok2->str() == "const") { + isTemplateArg = true; + break; + } + if (isTemplateArg) + continue; + } + if (func.isOperator()) { // Operator without return type: conversion operator const std::string& opName = func.tokenDef->str(); if (opName.compare(8, 5, "const") != 0 && (endsWith(opName,'&') || endsWith(opName,'*'))) @@ -2457,8 +2467,9 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, Member auto hasOverloadedMemberAccess = [](const Token* end, const Scope* scope) -> bool { if (!end || !scope || !Token::simpleMatch(end->astParent(), ".")) return false; - auto it = std::find_if(scope->functionList.begin(), scope->functionList.end(), [](const Function& f) { - return f.isConst() && f.name() == "operator."; + const std::string op = "operator" + end->astParent()->originalName(); + auto it = std::find_if(scope->functionList.begin(), scope->functionList.end(), [&op](const Function& f) { + return f.isConst() && f.name() == op; }); if (it == scope->functionList.end() || !it->retType || !it->retType->classScope) return false; @@ -2570,7 +2581,7 @@ void CheckClass::checkConstError2(const Token *tok1, const Token *tok2, const st "passed to the function. This change should not cause compiler errors but it does not " "necessarily make sense conceptually. Think about your design and the task of the function first - " "is it a function that must not access members of class instances? And maybe it is more appropriate " - "to move this function to a unnamed namespace.", CWE398, Certainty::inconclusive); + "to move this function to an unnamed namespace.", CWE398, Certainty::inconclusive); } //--------------------------------------------------------------------------- @@ -2875,8 +2886,24 @@ void CheckClass::checkDuplInheritedMembers() } } -void CheckClass::checkDuplInheritedMembersRecursive(const Type* typeCurrent, const Type* typeBase) +namespace { + struct DuplMemberInfo { + DuplMemberInfo(const Variable* cv, const Variable* pcv, const Type::BaseInfo* pc) : classVar(cv), parentClassVar(pcv), parentClass(pc) {} + const Variable* classVar; + const Variable* parentClassVar; + const Type::BaseInfo* parentClass; + }; + struct DuplMemberFuncInfo { + DuplMemberFuncInfo(const Function* cf, const Function* pcf, const Type::BaseInfo* pc) : classFunc(cf), parentClassFunc(pcf), parentClass(pc) {} + const Function* classFunc; + const Function* parentClassFunc; + const Type::BaseInfo* parentClass; + }; +} + +static std::vector getDuplInheritedMembersRecursive(const Type* typeCurrent, const Type* typeBase, bool skipPrivate = true) { + std::vector results; for (const Type::BaseInfo &parentClassIt : typeBase->derivedFrom) { // Check if there is info about the 'Base' class if (!parentClassIt.type || !parentClassIt.type->classScope) @@ -2887,31 +2914,79 @@ void CheckClass::checkDuplInheritedMembersRecursive(const Type* typeCurrent, con // Check if they have a member variable in common for (const Variable &classVarIt : typeCurrent->classScope->varlist) { for (const Variable &parentClassVarIt : parentClassIt.type->classScope->varlist) { - if (classVarIt.name() == parentClassVarIt.name() && !parentClassVarIt.isPrivate()) { // Check if the class and its parent have a common variable - duplInheritedMembersError(classVarIt.nameToken(), parentClassVarIt.nameToken(), - typeCurrent->name(), parentClassIt.type->name(), classVarIt.name(), - typeCurrent->classScope->type == Scope::eStruct, - parentClassIt.type->classScope->type == Scope::eStruct); - } + if (classVarIt.name() == parentClassVarIt.name() && (!parentClassVarIt.isPrivate() || !skipPrivate)) // Check if the class and its parent have a common variable + results.emplace_back(&classVarIt, &parentClassVarIt, &parentClassIt); } } - if (typeCurrent != parentClassIt.type) - checkDuplInheritedMembersRecursive(typeCurrent, parentClassIt.type); + if (typeCurrent != parentClassIt.type) { + const auto recursive = getDuplInheritedMembersRecursive(typeCurrent, parentClassIt.type, skipPrivate); + results.insert(results.end(), recursive.begin(), recursive.end()); + } + } + return results; +} + +static std::vector getDuplInheritedMemberFunctionsRecursive(const Type* typeCurrent, const Type* typeBase, bool skipPrivate = true) +{ + std::vector results; + for (const Type::BaseInfo &parentClassIt : typeBase->derivedFrom) { + // Check if there is info about the 'Base' class + if (!parentClassIt.type || !parentClassIt.type->classScope) + continue; + // Don't crash on recursive templates + if (parentClassIt.type == typeBase) + continue; + for (const Function& classFuncIt : typeCurrent->classScope->functionList) { + if (classFuncIt.isImplicitlyVirtual()) + continue; + for (const Function& parentClassFuncIt : parentClassIt.type->classScope->functionList) { + if (classFuncIt.name() == parentClassFuncIt.name() && + (parentClassFuncIt.access != AccessControl::Private || !skipPrivate) && + !classFuncIt.isConstructor() && !classFuncIt.isDestructor() && + classFuncIt.argsMatch(parentClassIt.type->classScope, parentClassFuncIt.argDef, classFuncIt.argDef, emptyString, 0)) + results.emplace_back(&classFuncIt, &parentClassFuncIt, &parentClassIt); + } + } + if (typeCurrent != parentClassIt.type) { + const auto recursive = getDuplInheritedMemberFunctionsRecursive(typeCurrent, parentClassIt.type); + results.insert(results.end(), recursive.begin(), recursive.end()); + } + } + return results; +} + +void CheckClass::checkDuplInheritedMembersRecursive(const Type* typeCurrent, const Type* typeBase) +{ + const auto resultsVar = getDuplInheritedMembersRecursive(typeCurrent, typeBase); + for (const auto& r : resultsVar) { + duplInheritedMembersError(r.classVar->nameToken(), r.parentClassVar->nameToken(), + typeCurrent->name(), r.parentClass->type->name(), r.classVar->name(), + typeCurrent->classScope->type == Scope::eStruct, + r.parentClass->type->classScope->type == Scope::eStruct); + } + + const auto resultsFunc = getDuplInheritedMemberFunctionsRecursive(typeCurrent, typeBase); + for (const auto& r : resultsFunc) { + duplInheritedMembersError(r.classFunc->token, r.parentClassFunc->token, + typeCurrent->name(), r.parentClass->type->name(), r.classFunc->name(), + typeCurrent->classScope->type == Scope::eStruct, + r.parentClass->type->classScope->type == Scope::eStruct, /*isFunction*/ true); } } void CheckClass::duplInheritedMembersError(const Token *tok1, const Token* tok2, const std::string &derivedName, const std::string &baseName, - const std::string &variableName, bool derivedIsStruct, bool baseIsStruct) + const std::string &memberName, bool derivedIsStruct, bool baseIsStruct, bool isFunction) { ErrorPath errorPath; - errorPath.emplace_back(tok2, "Parent variable '" + baseName + "::" + variableName + "'"); - errorPath.emplace_back(tok1, "Derived variable '" + derivedName + "::" + variableName + "'"); + const std::string member = isFunction ? "function" : "variable"; + errorPath.emplace_back(tok2, "Parent " + member + " '" + baseName + "::" + memberName + "'"); + errorPath.emplace_back(tok1, "Derived " + member + " '" + derivedName + "::" + memberName + "'"); - const std::string symbols = "$symbol:" + derivedName + "\n$symbol:" + variableName + "\n$symbol:" + baseName; + const std::string symbols = "$symbol:" + derivedName + "\n$symbol:" + memberName + "\n$symbol:" + baseName; const std::string message = "The " + std::string(derivedIsStruct ? "struct" : "class") + " '" + derivedName + - "' defines member variable with name '" + variableName + "' also defined in its parent " + + "' defines member " + member + " with name '" + memberName + "' also defined in its parent " + std::string(baseIsStruct ? "struct" : "class") + " '" + baseName + "'."; reportError(errorPath, Severity::warning, "duplInheritedMember", symbols + '\n' + message, CWE398, Certainty::normal); } @@ -3025,6 +3100,125 @@ void CheckClass::overrideError(const Function *funcInBase, const Function *funcI Certainty::normal); } +void CheckClass::uselessOverrideError(const Function *funcInBase, const Function *funcInDerived, bool isSameCode) +{ + const std::string functionName = funcInDerived ? ((funcInDerived->isDestructor() ? "~" : "") + funcInDerived->name()) : ""; + const std::string funcType = (funcInDerived && funcInDerived->isDestructor()) ? "destructor" : "function"; + + ErrorPath errorPath; + if (funcInBase && funcInDerived) { + errorPath.emplace_back(funcInBase->tokenDef, "Virtual " + funcType + " in base class"); + errorPath.emplace_back(funcInDerived->tokenDef, char(std::toupper(funcType[0])) + funcType.substr(1) + " in derived class"); + } + + std::string errStr = "\nThe " + funcType + " '$symbol' overrides a " + funcType + " in a base class but "; + if (isSameCode) { + errStr += "is identical to the overridden function"; + } + else + errStr += "just delegates back to the base class."; + reportError(errorPath, Severity::style, "uselessOverride", + "$symbol:" + functionName + + errStr, + CWE(0U) /* Unknown CWE! */, + Certainty::normal); +} + +static const Token* getSingleFunctionCall(const Scope* scope) { + const Token* const start = scope->bodyStart->next(); + const Token* const end = Token::findsimplematch(start, ";", 1, scope->bodyEnd); + if (!end || end->next() != scope->bodyEnd) + return nullptr; + const Token* ftok = start; + if (ftok->str() == "return") + ftok = ftok->astOperand1(); + else { + while (Token::Match(ftok, "%name%|::")) + ftok = ftok->next(); + } + if (Token::simpleMatch(ftok, "(") && ftok->previous()->function()) + return ftok->previous(); + return nullptr; +} + +static bool compareTokenRanges(const Token* start1, const Token* end1, const Token* start2, const Token* end2) { + const Token* tok1 = start1; + const Token* tok2 = start2; + bool isEqual = false; + while (tok1 && tok2) { + if (tok1->str() != tok2->str()) + break; + if (tok1->str() == "this") + break; + if (tok1->isExpandedMacro() || tok2->isExpandedMacro()) + break; + if (tok1 == end1 && tok2 == end2) { + isEqual = true; + break; + } + tok1 = tok1->next(); + tok2 = tok2->next(); + } + return isEqual; +} + +void CheckClass::checkUselessOverride() +{ + if (!mSettings->severity.isEnabled(Severity::style)) + return; + + for (const Scope* classScope : mSymbolDatabase->classAndStructScopes) { + if (!classScope->definedType || classScope->definedType->derivedFrom.size() != 1) + continue; + for (const Function& func : classScope->functionList) { + if (!func.functionScope) + continue; + if (func.hasFinalSpecifier()) + continue; + const Function* baseFunc = func.getOverriddenFunction(); + if (!baseFunc || baseFunc->isPure() || baseFunc->access != func.access) + continue; + if (std::any_of(classScope->functionList.begin(), classScope->functionList.end(), [&func](const Function& f) { // check for overloads + if (&f == &func) + return false; + return f.name() == func.name(); + })) + continue; + if (func.token->isExpandedMacro() || baseFunc->token->isExpandedMacro()) + continue; + if (baseFunc->functionScope) { + bool isSameCode = compareTokenRanges(baseFunc->argDef, baseFunc->argDef->link(), func.argDef, func.argDef->link()); // function arguments + if (isSameCode) { + isSameCode = compareTokenRanges(baseFunc->functionScope->bodyStart, baseFunc->functionScope->bodyEnd, // function body + func.functionScope->bodyStart, func.functionScope->bodyEnd); + + if (isSameCode) { + // bailout for shadowed members + if (!classScope->definedType || + !getDuplInheritedMembersRecursive(classScope->definedType, classScope->definedType, /*skipPrivate*/ false).empty() || + !getDuplInheritedMemberFunctionsRecursive(classScope->definedType, classScope->definedType, /*skipPrivate*/ false).empty()) + continue; + uselessOverrideError(baseFunc, &func, true); + continue; + } + } + } + if (const Token* const call = getSingleFunctionCall(func.functionScope)) { + if (call->function() != baseFunc) + continue; + std::vector funcArgs = getArguments(func.tokenDef); + std::vector callArgs = getArguments(call); + if (funcArgs.size() != callArgs.size() || + !std::equal(funcArgs.begin(), funcArgs.end(), callArgs.begin(), [](const Token* t1, const Token* t2) { + return t1->str() == t2->str(); + })) + continue; + uselessOverrideError(baseFunc, &func); + } + } + } +} + void CheckClass::checkThisUseAfterFree() { if (!mSettings->severity.isEnabled(Severity::warning)) diff --git a/lib/checkclass.h b/lib/checkclass.h index 5affc9be6e3..ffb94d3a247 100644 --- a/lib/checkclass.h +++ b/lib/checkclass.h @@ -83,6 +83,7 @@ class CPPCHECKLIB CheckClass : public Check { checkClass.checkExplicitConstructors(); checkClass.checkCopyCtorAndEqOperator(); checkClass.checkOverride(); + checkClass.checkUselessOverride(); checkClass.checkThisUseAfterFree(); checkClass.checkUnsafeClassRefMember(); } @@ -146,6 +147,9 @@ class CPPCHECKLIB CheckClass : public Check { /** @brief Check that the override keyword is used when overriding virtual functions */ void checkOverride(); + /** @brief Check that the overriden function is not identical to the base function */ + void checkUselessOverride(); + /** @brief When "self pointer" is destroyed, 'this' might become invalid. */ void checkThisUseAfterFree(); @@ -224,9 +228,10 @@ class CPPCHECKLIB CheckClass : public Check { void selfInitializationError(const Token* tok, const std::string& varname); void pureVirtualFunctionCallInConstructorError(const Function * scopeFunction, const std::list & tokStack, const std::string &purefuncname); void virtualFunctionCallInConstructorError(const Function * scopeFunction, const std::list & tokStack, const std::string &funcname); - void duplInheritedMembersError(const Token* tok1, const Token* tok2, const std::string &derivedName, const std::string &baseName, const std::string &variableName, bool derivedIsStruct, bool baseIsStruct); + void duplInheritedMembersError(const Token* tok1, const Token* tok2, const std::string &derivedName, const std::string &baseName, const std::string &memberName, bool derivedIsStruct, bool baseIsStruct, bool isFunction = false); void copyCtorAndEqOperatorError(const Token *tok, const std::string &classname, bool isStruct, bool hasCopyCtor); void overrideError(const Function *funcInBase, const Function *funcInDerived); + void uselessOverrideError(const Function *funcInBase, const Function *funcInDerived, bool isSameCode = false); void thisUseAfterFree(const Token *self, const Token *free, const Token *use); void unsafeClassRefMemberError(const Token *tok, const std::string &varname); void checkDuplInheritedMembersRecursive(const Type* typeCurrent, const Type* typeBase); diff --git a/lib/checkcondition.cpp b/lib/checkcondition.cpp index 03b1cd75056..a0799b4b41c 100644 --- a/lib/checkcondition.cpp +++ b/lib/checkcondition.cpp @@ -317,8 +317,15 @@ void CheckCondition::checkBadBitmaskCheck() const bool isZero1 = (tok->astOperand1()->hasKnownIntValue() && tok->astOperand1()->values().front().intvalue == 0); const bool isZero2 = (tok->astOperand2()->hasKnownIntValue() && tok->astOperand2()->values().front().intvalue == 0); + if (!isZero1 && !isZero2) + continue; - if ((isZero1 || isZero2) && !tok->isExpandedMacro() && !(isZero1 && tok->astOperand1()->isExpandedMacro()) && !(isZero2 && tok->astOperand2()->isExpandedMacro())) + auto isOperandExpanded = [](const Token *op) { + return op->isExpandedMacro() || op->isEnumerator(); + }; + if (!tok->isExpandedMacro() && + !(isZero1 && isOperandExpanded(tok->astOperand1())) && + !(isZero2 && isOperandExpanded(tok->astOperand2()))) badBitmaskCheckError(tok, /*isNoOp*/ true); } } diff --git a/lib/checkleakautovar.cpp b/lib/checkleakautovar.cpp index 97678273155..ae53485d7c1 100644 --- a/lib/checkleakautovar.cpp +++ b/lib/checkleakautovar.cpp @@ -545,8 +545,10 @@ bool CheckLeakAutoVar::checkScope(const Token * const startToken, } closingParenthesis = closingParenthesis->linkAt(1); if (Token::simpleMatch(closingParenthesis, "} else {")) { - if (!checkScope(closingParenthesis->tokAt(2), varInfo2, notzero, recursiveCount)) - continue; + if (!checkScope(closingParenthesis->tokAt(2), varInfo2, notzero, recursiveCount)) { + varInfo.clear(); + return false; + } tok = closingParenthesis->linkAt(2)->previous(); } else { tok = closingParenthesis->previous(); diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index 06d7b779f2a..b6f4dde5b41 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -28,7 +28,6 @@ #include "tokenize.h" #include -#include #include #include diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 2a167a068aa..a0d2677ad58 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -991,7 +991,7 @@ void CheckOther::checkVariableScope() } } -bool CheckOther::checkInnerScope(const Token *tok, const Variable* var, bool& used) +bool CheckOther::checkInnerScope(const Token *tok, const Variable* var, bool& used) const { const Scope* scope = tok->next()->scope(); bool loopVariable = scope->isLoopScope(); @@ -1071,6 +1071,18 @@ bool CheckOther::checkInnerScope(const Token *tok, const Variable* var, bool& us if (scope->bodyStart && scope->bodyStart->isSimplifiedScope()) return false; // simplified if/for/switch init statement } + if (var->isArrayOrPointer()) { + int argn{}; + if (const Token* ftok = getTokenArgumentFunction(tok, argn)) { // var passed to function? + if (ftok->next()->astParent()) { // return value used? + if (ftok->function() && Function::returnsPointer(ftok->function())) + return false; + const std::string ret = mSettings->library.returnValueType(ftok); // assume that var is returned + if (!ret.empty() && ret.back() == '*') + return false; + } + } + } } } diff --git a/lib/checkother.h b/lib/checkother.h index 14e0409145e..747f1e63857 100644 --- a/lib/checkother.h +++ b/lib/checkother.h @@ -123,7 +123,7 @@ class CPPCHECKLIB CheckOther : public Check { /** @brief %Check scope of variables */ void checkVariableScope(); - static bool checkInnerScope(const Token *tok, const Variable* var, bool& used); + bool checkInnerScope(const Token *tok, const Variable* var, bool& used) const; /** @brief %Check for comma separated statements in return */ void checkCommaSeparatedReturn(); diff --git a/lib/checksizeof.cpp b/lib/checksizeof.cpp index 220195401e2..281f02a6a40 100644 --- a/lib/checksizeof.cpp +++ b/lib/checksizeof.cpp @@ -21,6 +21,7 @@ #include "checksizeof.h" #include "errortypes.h" +#include "library.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" diff --git a/lib/checkstl.cpp b/lib/checkstl.cpp index ceec3cd9b00..e89eb254d61 100644 --- a/lib/checkstl.cpp +++ b/lib/checkstl.cpp @@ -1992,7 +1992,7 @@ void CheckStl::string_c_str() } } } - } else if (printPerformance && Token::Match(tok, "%var% (|{ %var% . c_str|data ( )") && + } else if (printPerformance && Token::Match(tok, "%var% (|{ %var% . c_str|data ( ) !!,") && tok->variable() && (tok->variable()->isStlStringType() || tok->variable()->isStlStringViewType()) && tok->tokAt(2)->variable() && tok->tokAt(2)->variable()->isStlStringType()) { string_c_strConstructor(tok, tok->variable()->getTypeName()); @@ -2054,7 +2054,7 @@ void CheckStl::string_c_str() if (Token::Match(refTok, "%var% = %var% .|;|[")) refToNonLocal = !isLocal(refTok->tokAt(2)); } - ptrOrRef = refToNonLocal || (tok2->variable() && tok2->variable()->isPointer()); + ptrOrRef = refToNonLocal || (tok2->variable() && (tok2->variable()->isPointer() || tok2->variable()->isSmartPointer())); } while (tok2) { if (Token::Match(tok2, "%var% .|::")) { diff --git a/lib/checktype.cpp b/lib/checktype.cpp index 7cc2d4d7643..6f793273392 100644 --- a/lib/checktype.cpp +++ b/lib/checktype.cpp @@ -289,6 +289,26 @@ void CheckType::signConversionError(const Token *tok, const ValueFlow::Value *ne //--------------------------------------------------------------------------- // Checking for long cast of int result const long x = var1 * var2; //--------------------------------------------------------------------------- +static bool checkTypeCombination(const ValueType& src, const ValueType& tgt, const Settings* settings) +{ + static const std::pair typeCombinations[] = { + { ValueType::Type::INT, ValueType::Type::LONG }, + { ValueType::Type::INT, ValueType::Type::LONGLONG }, + { ValueType::Type::LONG, ValueType::Type::LONGLONG }, + { ValueType::Type::FLOAT, ValueType::Type::DOUBLE }, + { ValueType::Type::FLOAT, ValueType::Type::LONGDOUBLE }, + { ValueType::Type::DOUBLE, ValueType::Type::LONGDOUBLE }, + }; + + const std::size_t sizeSrc = ValueFlow::getSizeOf(src, settings); + const std::size_t sizeTgt = ValueFlow::getSizeOf(tgt, settings); + if (!(sizeSrc > 0 && sizeTgt > 0 && sizeSrc < sizeTgt)) + return false; + + return std::any_of(std::begin(typeCombinations), std::end(typeCombinations), [&](const std::pair& p) { + return src.type == p.first && tgt.type == p.second; + }); +} void CheckType::checkLongCast() { @@ -311,16 +331,15 @@ void CheckType::checkLongCast() if (!lhstype || !rhstype) continue; + if (!checkTypeCombination(*rhstype, *lhstype, mSettings)) + continue; // assign int result to long/longlong const nonpointer? - if (rhstype->type == ValueType::Type::INT && - rhstype->pointer == 0U && + if (rhstype->pointer == 0U && rhstype->originalTypeName.empty() && - (lhstype->type == ValueType::Type::LONG || lhstype->type == ValueType::Type::LONGLONG) && lhstype->pointer == 0U && - lhstype->constness == 1U && lhstype->originalTypeName.empty()) - longCastAssignError(tok); + longCastAssignError(tok, rhstype, lhstype); } // Return.. @@ -329,16 +348,9 @@ void CheckType::checkLongCast() // function must return long data const Token * def = scope->classDef; - bool islong = false; - while (Token::Match(def, "%type%|::")) { - if (def->str() == "long" && def->originalName().empty()) { - islong = true; - break; - } - def = def->previous(); - } - if (!islong) + if (!Token::Match(def, "%name% (") || !def->next()->valueType()) continue; + const ValueType* retVt = def->next()->valueType(); // return statements const Token *ret = nullptr; @@ -346,7 +358,9 @@ void CheckType::checkLongCast() if (tok->str() == "return") { if (Token::Match(tok->astOperand1(), "<<|*")) { const ValueType *type = tok->astOperand1()->valueType(); - if (type && type->type == ValueType::Type::INT && type->pointer == 0U && type->originalTypeName.empty()) + if (type && checkTypeCombination(*type, *retVt, mSettings) && + type->pointer == 0U && + type->originalTypeName.empty()) ret = tok; } // All return statements must have problem otherwise no warning @@ -358,26 +372,41 @@ void CheckType::checkLongCast() } if (ret) - longCastReturnError(ret); + longCastReturnError(ret, ret->astOperand1()->valueType(), retVt); } } -void CheckType::longCastAssignError(const Token *tok) +static void makeBaseTypeString(std::string& typeStr) +{ + const auto pos = typeStr.find("signed"); + if (pos != std::string::npos) + typeStr.erase(typeStr.begin(), typeStr.begin() + pos + 6 + 1); +} + +void CheckType::longCastAssignError(const Token *tok, const ValueType* src, const ValueType* tgt) { + std::string srcStr = src ? src->str() : "int"; + makeBaseTypeString(srcStr); + std::string tgtStr = tgt ? tgt->str() : "long"; + makeBaseTypeString(tgtStr); reportError(tok, Severity::style, "truncLongCastAssignment", - "int result is assigned to long variable. If the variable is long to avoid loss of information, then you have loss of information.\n" - "int result is assigned to long variable. If the variable is long to avoid loss of information, then there is loss of information. To avoid loss of information you must cast a calculation operand to long, for example 'l = a * b;' => 'l = (long)a * b;'.", CWE197, Certainty::normal); + srcStr + " result is assigned to " + tgtStr + " variable. If the variable is " + tgtStr + " to avoid loss of information, then you have loss of information.\n" + + srcStr + " result is assigned to " + tgtStr + " variable. If the variable is " + tgtStr + " to avoid loss of information, then there is loss of information. To avoid loss of information you must cast a calculation operand to " + tgtStr + ", for example 'l = a * b;' => 'l = (" + tgtStr + ")a * b;'.", CWE197, Certainty::normal); } -void CheckType::longCastReturnError(const Token *tok) +void CheckType::longCastReturnError(const Token *tok, const ValueType* src, const ValueType* tgt) { + std::string srcStr = src ? src->str() : "int"; + makeBaseTypeString(srcStr); + std::string tgtStr = tgt ? tgt->str() : "long"; + makeBaseTypeString(tgtStr); reportError(tok, Severity::style, "truncLongCastReturn", - "int result is returned as long value. If the return value is long to avoid loss of information, then you have loss of information.\n" - "int result is returned as long value. If the return value is long to avoid loss of information, then there is loss of information. To avoid loss of information you must cast a calculation operand to long, for example 'return a*b;' => 'return (long)a*b'.", CWE197, Certainty::normal); + srcStr +" result is returned as " + tgtStr + " value. If the return value is " + tgtStr + " to avoid loss of information, then you have loss of information.\n" + + srcStr +" result is returned as " + tgtStr + " value. If the return value is " + tgtStr + " to avoid loss of information, then there is loss of information. To avoid loss of information you must cast a calculation operand to long, for example 'return a*b;' => 'return (long)a*b'.", CWE197, Certainty::normal); } //--------------------------------------------------------------------------- diff --git a/lib/checktype.h b/lib/checktype.h index 34417b371f6..e403bf16dd0 100644 --- a/lib/checktype.h +++ b/lib/checktype.h @@ -84,8 +84,8 @@ class CPPCHECKLIB CheckType : public Check { void tooBigSignedBitwiseShiftError(const Token *tok, int lhsbits, const ValueFlow::Value &rhsbits); void integerOverflowError(const Token *tok, const ValueFlow::Value &value); void signConversionError(const Token *tok, const ValueFlow::Value *negativeValue, const bool constvalue); - void longCastAssignError(const Token *tok); - void longCastReturnError(const Token *tok); + void longCastAssignError(const Token *tok, const ValueType* src = nullptr, const ValueType* tgt = nullptr); + void longCastReturnError(const Token *tok, const ValueType* src = nullptr, const ValueType* tgt = nullptr); void floatToIntegerOverflowError(const Token *tok, const ValueFlow::Value &value); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { diff --git a/lib/checkuninitvar.cpp b/lib/checkuninitvar.cpp index aca9c6e87ce..e30dfebc14b 100644 --- a/lib/checkuninitvar.cpp +++ b/lib/checkuninitvar.cpp @@ -1248,7 +1248,7 @@ const Token* CheckUninitVar::isVariableUsage(bool cpp, const Token *vartok, cons } if (Token::simpleMatch(tok->astParent(), "=")) { if (astIsLhs(tok)) { - if (alloc == ARRAY || !derefValue || !derefValue->isUnaryOp("*")) + if (alloc == ARRAY || !derefValue || !derefValue->isUnaryOp("*") || !pointer) return nullptr; const Token* deref = derefValue->astOperand1(); while (deref && deref->isCast()) diff --git a/lib/checkunusedfunctions.cpp b/lib/checkunusedfunctions.cpp index d9ca653be7f..c0fa12c2f6b 100644 --- a/lib/checkunusedfunctions.cpp +++ b/lib/checkunusedfunctions.cpp @@ -33,7 +33,6 @@ #include #include -#include #include #include // IWYU pragma: keep #include diff --git a/lib/checkunusedvar.cpp b/lib/checkunusedvar.cpp index 21f6b70d030..4f45cfe2da9 100644 --- a/lib/checkunusedvar.cpp +++ b/lib/checkunusedvar.cpp @@ -1009,7 +1009,7 @@ void CheckUnusedVar::checkFunctionVariableUsage_iterateScopes(const Scope* const // assignment else if ((Token::Match(tok, "%name% [") && Token::simpleMatch(skipBracketsAndMembers(tok->next()), "=")) || - (Token::simpleMatch(tok, "* (") && Token::simpleMatch(tok->next()->link(), ") ="))) { + (tok->isUnaryOp("*") && Token::simpleMatch(tok->astParent(), "=") && Token::simpleMatch(tok->astOperand1(), "+"))) { const Token *eq = tok; while (eq && !eq->isAssignmentOp()) eq = eq->astParent(); @@ -1213,7 +1213,7 @@ void CheckUnusedVar::checkFunctionVariableUsage() continue; tok = tok->next(); } - if (tok->astParent() && !tok->astParent()->isAssignmentOp() && tok->str() != "(") { + if (!isInitialization && tok->astParent() && !tok->astParent()->isAssignmentOp() && tok->str() != "(") { const Token *parent = tok->astParent(); while (Token::Match(parent, "%oror%|%comp%|!|&&")) parent = parent->astParent(); @@ -1268,7 +1268,9 @@ void CheckUnusedVar::checkFunctionVariableUsage() op1Var->isClass() && (!op1Var->valueType() || op1Var->valueType()->type == ValueType::Type::UNKNOWN_TYPE)) { // Check in the library if we should bailout or not.. - const std::string typeName = op1Var->getTypeName(); + std::string typeName = op1Var->getTypeName(); + if (typeName.compare(0, 2, "::") == 0) + typeName.erase(typeName.begin(), typeName.begin() + 2); switch (mSettings->library.getTypeCheck("unusedvar", typeName)) { case Library::TypeCheck::def: bailoutTypeName = typeName; diff --git a/lib/clangimport.cpp b/lib/clangimport.cpp index 05a08a0929a..7b91d6c4b07 100644 --- a/lib/clangimport.cpp +++ b/lib/clangimport.cpp @@ -30,7 +30,6 @@ #include #include -#include #include #include #include diff --git a/lib/errorlogger.cpp b/lib/errorlogger.cpp index 35552d313f2..08ac7fbae7f 100644 --- a/lib/errorlogger.cpp +++ b/lib/errorlogger.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include // IWYU pragma: keep @@ -379,8 +378,17 @@ void ErrorMessage::deserialize(const std::string &data) } } -std::string ErrorMessage::getXMLHeader(const std::string& productName) +std::string ErrorMessage::getXMLHeader(std::string productName) { + std::string version = CppCheck::version(); + if (!productName.empty() && std::isdigit(productName.back())) { + const std::string::size_type pos = productName.find_last_not_of(".0123456789"); + if (pos > 1 && pos != std::string::npos && productName[pos] == ' ') { + version = productName.substr(pos+1); + productName.erase(pos); + } + } + tinyxml2::XMLPrinter printer; // standard xml header @@ -393,7 +401,7 @@ std::string ErrorMessage::getXMLHeader(const std::string& productName) printer.OpenElement("cppcheck", false); if (!productName.empty()) printer.PushAttribute("product-name", productName.c_str()); - printer.PushAttribute("version", CppCheck::version()); + printer.PushAttribute("version", version.c_str()); printer.CloseElement(false); printer.OpenElement("errors", false); diff --git a/lib/errorlogger.h b/lib/errorlogger.h index b47baff77ec..ef8d8a71937 100644 --- a/lib/errorlogger.h +++ b/lib/errorlogger.h @@ -148,7 +148,7 @@ class CPPCHECKLIB ErrorMessage { */ std::string toXML() const; - static std::string getXMLHeader(const std::string& productName); + static std::string getXMLHeader(std::string productName); static std::string getXMLFooter(); /** diff --git a/lib/forwardanalyzer.cpp b/lib/forwardanalyzer.cpp index 1e2461a2478..fea0526eb5d 100644 --- a/lib/forwardanalyzer.cpp +++ b/lib/forwardanalyzer.cpp @@ -593,7 +593,7 @@ struct ForwardTraversal { // TODO: Don't break, instead move to the outer scope if (!tok) return Break(); - } else if (Token::Match(tok, "%name% :") || tok->str() == "case") { + } else if (!tok->variable() && (Token::Match(tok, "%name% :") || tok->str() == "case")) { if (!analyzer->lowerToPossible()) return Break(Analyzer::Terminate::Bail); } else if (tok->link() && tok->str() == "}") { @@ -671,7 +671,8 @@ struct ForwardTraversal { return Break(); } else { Token* stepTok = getStepTok(tok); - if (updateLoop(end, endBlock, condTok, initTok, stepTok) == Progress::Break) + // Dont pass initTok since it was already evaluated + if (updateLoop(end, endBlock, condTok, nullptr, stepTok) == Progress::Break) return Break(); } tok = endBlock; @@ -795,14 +796,15 @@ struct ForwardTraversal { return Break(); return Break(); } else if (Token* callTok = callExpr(tok)) { + // TODO: Dont traverse tokens a second time + if (start != callTok && tok != callTok && updateRecursive(callTok->astOperand1()) == Progress::Break) + return Break(); // Since the call could be an unknown macro, traverse the tokens as a range instead of recursively if (!Token::simpleMatch(callTok, "( )") && updateRange(callTok->next(), callTok->link(), depth - 1) == Progress::Break) return Break(); if (updateTok(callTok) == Progress::Break) return Break(); - if (start != callTok && updateRecursive(callTok->astOperand1()) == Progress::Break) - return Break(); tok = callTok->link(); if (!tok) return Break(); diff --git a/lib/fwdanalysis.cpp b/lib/fwdanalysis.cpp index 3d6078c2d04..93c06c62754 100644 --- a/lib/fwdanalysis.cpp +++ b/lib/fwdanalysis.cpp @@ -364,6 +364,8 @@ struct FwdAnalysis::Result FwdAnalysis::checkRecursive(const Token *expr, const const Result &result1 = checkRecursive(expr, tok->tokAt(2), tok->linkAt(1), exprVarIds, local, inInnerClass, depth); if (result1.type == Result::Type::READ || result1.type == Result::Type::BAILOUT) return result1; + if (mWhat == What::UnusedValue && result1.type == Result::Type::WRITE && expr->variable() && expr->variable()->isReference()) + return result1; if (mWhat == What::ValueFlow && result1.type == Result::Type::WRITE) mValueFlowKnown = false; if (mWhat == What::Reassign && result1.type == Result::Type::BREAK) { @@ -530,6 +532,10 @@ bool FwdAnalysis::possiblyAliased(const Token *expr, const Token *startToken) co addrOf = tok->astOperand1(); else if (Token::simpleMatch(tok, "std :: ref (")) addrOf = tok->tokAt(3)->astOperand2(); + else if (tok->valueType() && tok->valueType()->pointer && + (Token::Match(tok, "%var% = %var% ;") || Token::Match(tok, "%var% {|( %var% }|)")) && + Token::Match(expr->previous(), "%varid% [", tok->tokAt(2)->varId())) + addrOf = tok->tokAt(2); else continue; diff --git a/lib/importproject.cpp b/lib/importproject.cpp index db36a827758..76403ff40db 100644 --- a/lib/importproject.cpp +++ b/lib/importproject.cpp @@ -1147,6 +1147,8 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti guiProject.analyzeAllVsConfigs.clear(); + bool checkLevelExhaustive = false; + // TODO: this should support all available command-line options for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { if (strcmp(node->Name(), CppcheckXml::RootPathName) == 0) { @@ -1216,6 +1218,8 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti } } else if (strcmp(node->Name(), CppcheckXml::CheckHeadersElementName) == 0) temp.checkHeaders = (strcmp(readSafe(node->GetText(), ""), "true") == 0); + else if (strcmp(node->Name(), CppcheckXml::CheckLevelExhaustiveElementName) == 0) + checkLevelExhaustive = true; else if (strcmp(node->Name(), CppcheckXml::CheckUnusedTemplatesElementName) == 0) temp.checkUnusedTemplates = (strcmp(readSafe(node->GetText(), ""), "true") == 0); else if (strcmp(node->Name(), CppcheckXml::MaxCtuDepthElementName) == 0) @@ -1281,6 +1285,11 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti settings->maxTemplateRecursion = temp.maxTemplateRecursion; settings->safeChecks = temp.safeChecks; + if (checkLevelExhaustive) + settings->setCheckLevelExhaustive(); + else + settings->setCheckLevelNormal(); + return true; } diff --git a/lib/importproject.h b/lib/importproject.h index 3a56b41be8b..1d7bb9ba72c 100644 --- a/lib/importproject.h +++ b/lib/importproject.h @@ -171,6 +171,7 @@ namespace CppcheckXml { const char TagAttributeName[] = "tag"; const char WarningElementName[] = "warning"; const char HashAttributeName[] = "hash"; + const char CheckLevelExhaustiveElementName[] = "check-level-exhaustive"; const char CheckHeadersElementName[] = "check-headers"; const char CheckUnusedTemplatesElementName[] = "check-unused-templates"; const char MaxCtuDepthElementName[] = "max-ctu-depth"; diff --git a/lib/library.cpp b/lib/library.cpp index f0e5788494e..4142dcd8ae1 100644 --- a/lib/library.cpp +++ b/lib/library.cpp @@ -31,12 +31,12 @@ #include #include #include -#include #include #include #include #include // IWYU pragma: keep #include +#include #include #include diff --git a/lib/programmemory.cpp b/lib/programmemory.cpp index 8181717aef4..05c42c461ca 100644 --- a/lib/programmemory.cpp +++ b/lib/programmemory.cpp @@ -636,6 +636,36 @@ static std::unordered_map createBuiltinLibr v.tokvalue = nullptr; return v; }; + functions["strcmp"] = [](const std::vector& args) { + if (args.size() != 2) + return ValueFlow::Value::unknown(); + const ValueFlow::Value& lhs = args[0]; + if (!(lhs.isTokValue() && lhs.tokvalue->tokType() == Token::eString)) + return ValueFlow::Value::unknown(); + const ValueFlow::Value& rhs = args[1]; + if (!(rhs.isTokValue() && rhs.tokvalue->tokType() == Token::eString)) + return ValueFlow::Value::unknown(); + ValueFlow::Value v(getStringLiteral(lhs.tokvalue->str()).compare(getStringLiteral(rhs.tokvalue->str()))); + ValueFlow::combineValueProperties(lhs, rhs, v); + return v; + }; + functions["strncmp"] = [](const std::vector& args) { + if (args.size() != 3) + return ValueFlow::Value::unknown(); + const ValueFlow::Value& lhs = args[0]; + if (!(lhs.isTokValue() && lhs.tokvalue->tokType() == Token::eString)) + return ValueFlow::Value::unknown(); + const ValueFlow::Value& rhs = args[1]; + if (!(rhs.isTokValue() && rhs.tokvalue->tokType() == Token::eString)) + return ValueFlow::Value::unknown(); + const ValueFlow::Value& len = args[2]; + if (!len.isIntValue()) + return ValueFlow::Value::unknown(); + ValueFlow::Value v(getStringLiteral(lhs.tokvalue->str()) + .compare(0, len.intvalue, getStringLiteral(rhs.tokvalue->str()), 0, len.intvalue)); + ValueFlow::combineValueProperties(lhs, rhs, v); + return v; + }; functions["sin"] = [](const std::vector& args) { if (args.size() != 1) return ValueFlow::Value::unknown(); @@ -1155,323 +1185,366 @@ static BuiltinLibraryFunction getBuiltinLibraryFunction(const std::string& name) return it->second; } -static ValueFlow::Value executeImpl(const Token* expr, ProgramMemory& pm, const Settings* settings) -{ - ValueFlow::Value unknown = ValueFlow::Value::unknown(); - const ValueFlow::Value* value = nullptr; - if (!expr) - return unknown; - if (expr->hasKnownIntValue() && !expr->isAssignmentOp() && expr->str() != ",") - return expr->values().front(); - if ((value = expr->getKnownValue(ValueFlow::Value::ValueType::FLOAT)) || - (value = expr->getKnownValue(ValueFlow::Value::ValueType::ITERATOR_START)) || - (value = expr->getKnownValue(ValueFlow::Value::ValueType::ITERATOR_END)) || - (value = expr->getKnownValue(ValueFlow::Value::ValueType::CONTAINER_SIZE))) { - return *value; - } - if (expr->isNumber()) { - if (MathLib::isFloat(expr->str())) - return unknown; - MathLib::bigint i = MathLib::toLongNumber(expr->str()); - if (i < 0 && astIsUnsigned(expr)) +struct Executor { + ProgramMemory* pm = nullptr; + const Settings* settings = nullptr; + int fdepth = 4; + + explicit Executor(ProgramMemory* pm = nullptr, const Settings* settings = nullptr) : pm(pm), settings(settings) {} + + ValueFlow::Value executeImpl(const Token* expr) + { + ValueFlow::Value unknown = ValueFlow::Value::unknown(); + const ValueFlow::Value* value = nullptr; + if (!expr) return unknown; - return ValueFlow::Value{i}; - } - if (expr->isBoolean()) - return ValueFlow::Value{ expr->str() == "true" }; - if (Token::Match(expr->tokAt(-2), ". %name% (") && astIsContainer(expr->tokAt(-2)->astOperand1())) { - const Token* containerTok = expr->tokAt(-2)->astOperand1(); - const Library::Container::Yield yield = containerTok->valueType()->container->getYield(expr->strAt(-1)); - if (yield == Library::Container::Yield::SIZE) { - ValueFlow::Value v = execute(containerTok, pm); - if (!v.isContainerSizeValue()) - return unknown; - v.valueType = ValueFlow::Value::ValueType::INT; - return v; + if (expr->hasKnownIntValue() && !expr->isAssignmentOp() && expr->str() != ",") + return expr->values().front(); + if ((value = expr->getKnownValue(ValueFlow::Value::ValueType::FLOAT)) || + (value = expr->getKnownValue(ValueFlow::Value::ValueType::TOK)) || + (value = expr->getKnownValue(ValueFlow::Value::ValueType::ITERATOR_START)) || + (value = expr->getKnownValue(ValueFlow::Value::ValueType::ITERATOR_END)) || + (value = expr->getKnownValue(ValueFlow::Value::ValueType::CONTAINER_SIZE))) { + return *value; } - if (yield == Library::Container::Yield::EMPTY) { - ValueFlow::Value v = execute(containerTok, pm); - if (!v.isContainerSizeValue()) + if (expr->isNumber()) { + if (MathLib::isFloat(expr->str())) return unknown; - if (v.isImpossible() && v.intvalue == 0) - return ValueFlow::Value{0}; - if (!v.isImpossible()) - return ValueFlow::Value{v.intvalue == 0}; + MathLib::bigint i = MathLib::toLongNumber(expr->str()); + if (i < 0 && astIsUnsigned(expr)) + return unknown; + return ValueFlow::Value{i}; } - } else if (expr->isAssignmentOp() && expr->astOperand1() && expr->astOperand2() && expr->astOperand1()->exprId() > 0) { - ValueFlow::Value rhs = execute(expr->astOperand2(), pm); - if (rhs.isUninitValue()) - return unknown; - if (expr->str() != "=") { - if (!pm.hasValue(expr->astOperand1()->exprId())) + if (expr->isBoolean()) + return ValueFlow::Value{expr->str() == "true"}; + if (Token::Match(expr->tokAt(-2), ". %name% (") && astIsContainer(expr->tokAt(-2)->astOperand1())) { + const Token* containerTok = expr->tokAt(-2)->astOperand1(); + const Library::Container::Yield yield = containerTok->valueType()->container->getYield(expr->strAt(-1)); + if (yield == Library::Container::Yield::SIZE) { + ValueFlow::Value v = execute(containerTok); + if (!v.isContainerSizeValue()) + return unknown; + v.valueType = ValueFlow::Value::ValueType::INT; + return v; + } + if (yield == Library::Container::Yield::EMPTY) { + ValueFlow::Value v = execute(containerTok); + if (!v.isContainerSizeValue()) + return unknown; + if (v.isImpossible() && v.intvalue == 0) + return ValueFlow::Value{0}; + if (!v.isImpossible()) + return ValueFlow::Value{v.intvalue == 0}; + } + } else if (expr->isAssignmentOp() && expr->astOperand1() && expr->astOperand2() && + expr->astOperand1()->exprId() > 0) { + ValueFlow::Value rhs = execute(expr->astOperand2()); + if (rhs.isUninitValue()) return unknown; - ValueFlow::Value& lhs = pm.at(expr->astOperand1()->exprId()); - rhs = evaluate(removeAssign(expr->str()), lhs, rhs); - if (lhs.isIntValue()) - ValueFlow::Value::visitValue(rhs, std::bind(assign{}, std::ref(lhs.intvalue), std::placeholders::_1)); - else if (lhs.isFloatValue()) - ValueFlow::Value::visitValue(rhs, std::bind(assign{}, std::ref(lhs.floatValue), std::placeholders::_1)); - else + if (expr->str() != "=") { + if (!pm->hasValue(expr->astOperand1()->exprId())) + return unknown; + ValueFlow::Value& lhs = pm->at(expr->astOperand1()->exprId()); + rhs = evaluate(removeAssign(expr->str()), lhs, rhs); + if (lhs.isIntValue()) + ValueFlow::Value::visitValue(rhs, std::bind(assign{}, std::ref(lhs.intvalue), std::placeholders::_1)); + else if (lhs.isFloatValue()) + ValueFlow::Value::visitValue(rhs, + std::bind(assign{}, std::ref(lhs.floatValue), std::placeholders::_1)); + else + return unknown; + return lhs; + } + pm->setValue(expr->astOperand1(), rhs); + return rhs; + } else if (expr->str() == "&&" && expr->astOperand1() && expr->astOperand2()) { + ValueFlow::Value lhs = execute(expr->astOperand1()); + if (!lhs.isIntValue()) return unknown; - return lhs; - } - pm.setValue(expr->astOperand1(), rhs); - return rhs; - } else if (expr->str() == "&&" && expr->astOperand1() && expr->astOperand2()) { - ValueFlow::Value lhs = execute(expr->astOperand1(), pm); - if (!lhs.isIntValue()) + if (isFalse(lhs)) + return lhs; + if (isTrue(lhs)) + return execute(expr->astOperand2()); return unknown; - if (isFalse(lhs)) - return lhs; - if (isTrue(lhs)) - return execute(expr->astOperand2(), pm); - return unknown; - } else if (expr->str() == "||" && expr->astOperand1() && expr->astOperand2()) { - ValueFlow::Value lhs = execute(expr->astOperand1(), pm); - if (!lhs.isIntValue() || lhs.isImpossible()) - return unknown; - if (isTrue(lhs)) - return lhs; - if (isFalse(lhs)) - return execute(expr->astOperand2(), pm); - return unknown; - } else if (expr->str() == "," && expr->astOperand1() && expr->astOperand2()) { - execute(expr->astOperand1(), pm); - return execute(expr->astOperand2(), pm); - } else if (expr->tokType() == Token::eIncDecOp && expr->astOperand1() && expr->astOperand1()->exprId() != 0) { - if (!pm.hasValue(expr->astOperand1()->exprId())) - return unknown; - ValueFlow::Value& lhs = pm.at(expr->astOperand1()->exprId()); - if (!lhs.isIntValue()) - return unknown; - // overflow - if (!lhs.isImpossible() && lhs.intvalue == 0 && expr->str() == "--" && astIsUnsigned(expr->astOperand1())) + } else if (expr->str() == "||" && expr->astOperand1() && expr->astOperand2()) { + ValueFlow::Value lhs = execute(expr->astOperand1()); + if (!lhs.isIntValue() || lhs.isImpossible()) + return unknown; + if (isTrue(lhs)) + return lhs; + if (isFalse(lhs)) + return execute(expr->astOperand2()); return unknown; + } else if (expr->str() == "," && expr->astOperand1() && expr->astOperand2()) { + execute(expr->astOperand1()); + return execute(expr->astOperand2()); + } else if (expr->tokType() == Token::eIncDecOp && expr->astOperand1() && expr->astOperand1()->exprId() != 0) { + if (!pm->hasValue(expr->astOperand1()->exprId())) + return unknown; + ValueFlow::Value& lhs = pm->at(expr->astOperand1()->exprId()); + if (!lhs.isIntValue()) + return unknown; + // overflow + if (!lhs.isImpossible() && lhs.intvalue == 0 && expr->str() == "--" && astIsUnsigned(expr->astOperand1())) + return unknown; - if (expr->str() == "++") - lhs.intvalue++; - else - lhs.intvalue--; - return lhs; - } else if (expr->str() == "[" && expr->astOperand1() && expr->astOperand2()) { - const Token *tokvalue = nullptr; - if (!pm.getTokValue(expr->astOperand1()->exprId(), &tokvalue)) { - auto tokvalue_it = std::find_if(expr->astOperand1()->values().cbegin(), - expr->astOperand1()->values().cend(), - std::mem_fn(&ValueFlow::Value::isTokValue)); - if (tokvalue_it == expr->astOperand1()->values().cend() || !tokvalue_it->isKnown()) { + if (expr->str() == "++") + lhs.intvalue++; + else + lhs.intvalue--; + return lhs; + } else if (expr->str() == "[" && expr->astOperand1() && expr->astOperand2()) { + const Token* tokvalue = nullptr; + if (!pm->getTokValue(expr->astOperand1()->exprId(), &tokvalue)) { + auto tokvalue_it = std::find_if(expr->astOperand1()->values().cbegin(), + expr->astOperand1()->values().cend(), + std::mem_fn(&ValueFlow::Value::isTokValue)); + if (tokvalue_it == expr->astOperand1()->values().cend() || !tokvalue_it->isKnown()) { + return unknown; + } + tokvalue = tokvalue_it->tokvalue; + } + if (!tokvalue || !tokvalue->isLiteral()) { return unknown; } - tokvalue = tokvalue_it->tokvalue; + const std::string strValue = tokvalue->strValue(); + ValueFlow::Value rhs = execute(expr->astOperand2()); + if (!rhs.isIntValue()) + return unknown; + const MathLib::bigint index = rhs.intvalue; + if (index >= 0 && index < strValue.size()) + return ValueFlow::Value{strValue[index]}; + if (index == strValue.size()) + return ValueFlow::Value{}; + } else if (Token::Match(expr, "%cop%") && expr->astOperand1() && expr->astOperand2()) { + ValueFlow::Value lhs = execute(expr->astOperand1()); + ValueFlow::Value rhs = execute(expr->astOperand2()); + ValueFlow::Value r = unknown; + if (!lhs.isUninitValue() && !rhs.isUninitValue()) + r = evaluate(expr->str(), lhs, rhs); + if (expr->isComparisonOp() && (r.isUninitValue() || r.isImpossible())) { + if (rhs.isIntValue()) { + std::vector result = + infer(ValueFlow::makeIntegralInferModel(), expr->str(), expr->astOperand1()->values(), {rhs}); + if (!result.empty() && result.front().isKnown()) + return result.front(); + } + if (lhs.isIntValue()) { + std::vector result = + infer(ValueFlow::makeIntegralInferModel(), expr->str(), {lhs}, expr->astOperand2()->values()); + if (!result.empty() && result.front().isKnown()) + return result.front(); + } + return unknown; + } + return r; } - if (!tokvalue || !tokvalue->isLiteral()) { + // Unary ops + else if (Token::Match(expr, "!|+|-") && expr->astOperand1() && !expr->astOperand2()) { + ValueFlow::Value lhs = execute(expr->astOperand1()); + if (!lhs.isIntValue()) + return unknown; + if (expr->str() == "!") { + if (isTrue(lhs)) { + lhs.intvalue = 0; + } else if (isFalse(lhs)) { + lhs.intvalue = 1; + } else { + return unknown; + } + lhs.setPossible(); + lhs.bound = ValueFlow::Value::Bound::Point; + } + if (expr->str() == "-") + lhs.intvalue = -lhs.intvalue; + return lhs; + } else if (expr->str() == "?" && expr->astOperand1() && expr->astOperand2()) { + ValueFlow::Value cond = execute(expr->astOperand1()); + if (!cond.isIntValue()) + return unknown; + const Token* child = expr->astOperand2(); + if (isFalse(cond)) + return execute(child->astOperand2()); + if (isTrue(cond)) + return execute(child->astOperand1()); + return unknown; + } else if (expr->str() == "(" && expr->isCast()) { + if (Token::simpleMatch(expr->previous(), ">") && expr->previous()->link()) + return execute(expr->astOperand2()); + return execute(expr->astOperand1()); } - const std::string strValue = tokvalue->strValue(); - ValueFlow::Value rhs = execute(expr->astOperand2(), pm); - if (!rhs.isIntValue()) - return unknown; - const MathLib::bigint index = rhs.intvalue; - if (index >= 0 && index < strValue.size()) - return ValueFlow::Value{strValue[index]}; - if (index == strValue.size()) - return ValueFlow::Value{}; - } else if (Token::Match(expr, "%cop%") && expr->astOperand1() && expr->astOperand2()) { - ValueFlow::Value lhs = execute(expr->astOperand1(), pm); - ValueFlow::Value rhs = execute(expr->astOperand2(), pm); - ValueFlow::Value r = unknown; - if (!lhs.isUninitValue() && !rhs.isUninitValue()) - r = evaluate(expr->str(), lhs, rhs); - if (expr->isComparisonOp() && (r.isUninitValue() || r.isImpossible())) { - if (rhs.isIntValue()) { - std::vector result = - infer(ValueFlow::makeIntegralInferModel(), expr->str(), expr->astOperand1()->values(), {rhs}); - if (!result.empty() && result.front().isKnown()) - return result.front(); + if (expr->exprId() > 0 && pm->hasValue(expr->exprId())) { + ValueFlow::Value result = pm->at(expr->exprId()); + if (result.isImpossible() && result.isIntValue() && result.intvalue == 0 && isUsedAsBool(expr)) { + result.intvalue = !result.intvalue; + result.setKnown(); } - if (lhs.isIntValue()) { - std::vector result = - infer(ValueFlow::makeIntegralInferModel(), expr->str(), {lhs}, expr->astOperand2()->values()); - if (!result.empty() && result.front().isKnown()) - return result.front(); - } - return unknown; + return result; } - return r; - } - // Unary ops - else if (Token::Match(expr, "!|+|-") && expr->astOperand1() && !expr->astOperand2()) { - ValueFlow::Value lhs = execute(expr->astOperand1(), pm); - if (!lhs.isIntValue()) - return unknown; - if (expr->str() == "!") { - if (isTrue(lhs)) { - lhs.intvalue = 0; - } else if (isFalse(lhs)) { - lhs.intvalue = 1; - } else { - return unknown; + + if (Token::Match(expr->previous(), ">|%name% {|(")) { + const Token* ftok = expr->previous(); + const Function* f = ftok->function(); + ValueFlow::Value result = unknown; + if (settings && expr->str() == "(") { + std::vector tokArgs = getArguments(expr); + std::vector args(tokArgs.size()); + std::transform(tokArgs.cbegin(), tokArgs.cend(), args.begin(), [&](const Token* tok) { + return execute(tok); + }); + if (f) { + if (fdepth >= 0 && !f->isImplicitlyVirtual()) { + ProgramMemory functionState; + for (std::size_t i = 0; i < args.size(); ++i) { + const Variable* const arg = f->getArgumentVar(i); + if (!arg) + return unknown; + functionState.setValue(arg->nameToken(), args[i]); + } + Executor ex = *this; + ex.pm = &functionState; + ex.fdepth--; + auto r = ex.execute(f->functionScope); + if (!r.empty()) + result = r.front(); + // TODO: Track values changed by reference + } + } else { + BuiltinLibraryFunction lf = getBuiltinLibraryFunction(ftok->str()); + if (lf) + return lf(args); + const std::string& returnValue = settings->library.returnValue(ftok); + if (!returnValue.empty()) { + std::unordered_map arg_map; + int argn = 0; + for (const ValueFlow::Value& v : args) { + if (!v.isUninitValue()) + arg_map[argn] = v; + argn++; + } + return evaluateLibraryFunction(arg_map, returnValue, settings); + } + } } - lhs.setPossible(); - lhs.bound = ValueFlow::Value::Bound::Point; + // Check if function modifies argument + visitAstNodes(expr->astOperand2(), [&](const Token* child) { + if (child->exprId() > 0 && pm->hasValue(child->exprId())) { + ValueFlow::Value& v = pm->at(child->exprId()); + if (v.valueType == ValueFlow::Value::ValueType::CONTAINER_SIZE) { + if (ValueFlow::isContainerSizeChanged(child, v.indirect, settings)) + v = unknown; + } else if (v.valueType != ValueFlow::Value::ValueType::UNINIT) { + if (isVariableChanged(child, v.indirect, settings, true)) + v = unknown; + } + } + return ChildrenToVisit::op1_and_op2; + }); + return result; } - if (expr->str() == "-") - lhs.intvalue = -lhs.intvalue; - return lhs; - } else if (expr->str() == "?" && expr->astOperand1() && expr->astOperand2()) { - ValueFlow::Value cond = execute(expr->astOperand1(), pm); - if (!cond.isIntValue()) - return unknown; - const Token* child = expr->astOperand2(); - if (isFalse(cond)) - return execute(child->astOperand2(), pm); - if (isTrue(cond)) - return execute(child->astOperand1(), pm); return unknown; - } else if (expr->str() == "(" && expr->isCast()) { - if (Token::simpleMatch(expr->previous(), ">") && expr->previous()->link()) - return execute(expr->astOperand2(), pm); - return execute(expr->astOperand1(), pm); - } - if (expr->exprId() > 0 && pm.hasValue(expr->exprId())) { - ValueFlow::Value result = pm.at(expr->exprId()); - if (result.isImpossible() && result.isIntValue() && result.intvalue == 0 && isUsedAsBool(expr)) { - result.intvalue = !result.intvalue; - result.setKnown(); - } - return result; } - - if (Token::Match(expr->previous(), ">|%name% {|(")) { - const Token* ftok = expr->previous(); - const Function* f = ftok->function(); - // TODO: Evaluate inline functions as well - if (!f && settings && expr->str() == "(") { - std::vector tokArgs = getArguments(expr); - std::vector args(tokArgs.size()); - std::transform(tokArgs.cbegin(), tokArgs.cend(), args.begin(), [&](const Token* tok) { - return execute(tok, pm, settings); - }); - BuiltinLibraryFunction lf = getBuiltinLibraryFunction(ftok->str()); - if (lf) - return lf(args); - const std::string& returnValue = settings->library.returnValue(ftok); - if (!returnValue.empty()) { - std::unordered_map arg_map; - int argn = 0; - for (const ValueFlow::Value& result : args) { - if (!result.isUninitValue()) - arg_map[argn] = result; - argn++; - } - return evaluateLibraryFunction(arg_map, returnValue, settings); + static const ValueFlow::Value* getImpossibleValue(const Token* tok) + { + if (!tok) + return nullptr; + std::vector values; + for (const ValueFlow::Value& v : tok->values()) { + if (!v.isImpossible()) + continue; + if (v.isContainerSizeValue() || v.isIntValue()) { + values.push_back(std::addressof(v)); } } - // Check if function modifies argument - visitAstNodes(expr->astOperand2(), [&](const Token* child) { - if (child->exprId() > 0 && pm.hasValue(child->exprId())) { - ValueFlow::Value& v = pm.at(child->exprId()); - if (v.valueType == ValueFlow::Value::ValueType::CONTAINER_SIZE) { - if (ValueFlow::isContainerSizeChanged(child, v.indirect, settings)) - v = unknown; - } else if (v.valueType != ValueFlow::Value::ValueType::UNINIT) { - if (isVariableChanged(child, v.indirect, settings, true)) - v = unknown; - } - } - return ChildrenToVisit::op1_and_op2; + auto it = + std::max_element(values.begin(), values.end(), [](const ValueFlow::Value* x, const ValueFlow::Value* y) { + return x->intvalue < y->intvalue; }); + if (it == values.end()) + return nullptr; + return *it; } - return unknown; -} + ValueFlow::Value execute(const Token* expr) + { + ValueFlow::Value v = executeImpl(expr); + if (!v.isUninitValue()) + return v; + if (!expr) + return v; + if (expr->exprId() > 0 && pm->hasValue(expr->exprId())) + return pm->at(expr->exprId()); + if (const ValueFlow::Value* value = getImpossibleValue(expr)) + return *value; + return v; + } -static const ValueFlow::Value* getImpossibleValue(const Token* tok) -{ - if (!tok) - return nullptr; - std::vector values; - for (const ValueFlow::Value& v : tok->values()) { - if (!v.isImpossible()) - continue; - if (v.isContainerSizeValue() || v.isIntValue()) { - values.push_back(std::addressof(v)); + std::vector execute(const Scope* scope) + { + static const std::vector unknown = {ValueFlow::Value::unknown()}; + if (!scope) + return unknown; + if (!scope->bodyStart) + return unknown; + for (const Token* tok = scope->bodyStart->next(); precedes(tok, scope->bodyEnd); tok = tok->next()) { + const Token* top = tok->astTop(); + if (!top) + return unknown; + + if (Token::simpleMatch(top, "return") && top->astOperand1()) + return {execute(top->astOperand1())}; + + if (Token::Match(top, "%op%")) { + if (execute(top).isUninitValue()) + return unknown; + const Token* next = nextAfterAstRightmostLeaf(top); + if (!next) + return unknown; + tok = next; + } else if (Token::simpleMatch(top->previous(), "if (")) { + const Token* condTok = top->astOperand2(); + ValueFlow::Value v = execute(condTok); + if (!v.isIntValue()) + return unknown; + const Token* thenStart = top->link()->next(); + const Token* next = thenStart->link(); + const Token* elseStart = nullptr; + if (Token::simpleMatch(thenStart->link(), "} else {")) { + elseStart = thenStart->link()->tokAt(2); + next = elseStart->link(); + } + std::vector result; + if (isTrue(v)) { + result = execute(thenStart->scope()); + } else if (isFalse(v)) { + if (elseStart) + result = execute(elseStart->scope()); + } else { + return unknown; + } + if (!result.empty()) + return result; + tok = next; + } else { + return unknown; + } } + return {}; } - auto it = std::max_element(values.begin(), values.end(), [](const ValueFlow::Value* x, const ValueFlow::Value* y) { - return x->intvalue < y->intvalue; - }); - if (it == values.end()) - return nullptr; - return *it; -} +}; static ValueFlow::Value execute(const Token* expr, ProgramMemory& pm, const Settings* settings) { - ValueFlow::Value v = executeImpl(expr, pm, settings); - if (!v.isUninitValue()) - return v; - if (!expr) - return v; - if (expr->exprId() > 0 && pm.hasValue(expr->exprId())) - return pm.at(expr->exprId()); - if (const ValueFlow::Value* value = getImpossibleValue(expr)) - return *value; - return v; + Executor ex{&pm, settings}; + return ex.execute(expr); } std::vector execute(const Scope* scope, ProgramMemory& pm, const Settings* settings) { - static const std::vector unknown = {ValueFlow::Value::unknown()}; - if (!scope) - return unknown; - if (!scope->bodyStart) - return unknown; - for (const Token* tok = scope->bodyStart->next(); precedes(tok, scope->bodyEnd); tok = tok->next()) { - const Token* top = tok->astTop(); - if (!top) - return unknown; - - if (Token::simpleMatch(top, "return") && top->astOperand1()) - return {execute(top->astOperand1(), pm, settings)}; - - if (Token::Match(top, "%op%")) { - if (execute(top, pm, settings).isUninitValue()) - return unknown; - const Token* next = nextAfterAstRightmostLeaf(top); - if (!next) - return unknown; - tok = next; - } else if (Token::simpleMatch(top->previous(), "if (")) { - const Token* condTok = top->astOperand2(); - ValueFlow::Value v = execute(condTok, pm, settings); - if (!v.isIntValue()) - return unknown; - const Token* thenStart = top->link()->next(); - const Token* next = thenStart->link(); - const Token* elseStart = nullptr; - if (Token::simpleMatch(thenStart->link(), "} else {")) { - elseStart = thenStart->link()->tokAt(2); - next = elseStart->link(); - } - std::vector result; - if (isTrue(v)) { - result = execute(thenStart->scope(), pm, settings); - } else if (isFalse(v)) { - if (elseStart) - result = execute(elseStart->scope(), pm, settings); - } else { - return unknown; - } - if (!result.empty()) - return result; - tok = next; - } else { - return unknown; - } - } - return {}; + Executor ex{&pm, settings}; + return ex.execute(scope); } ValueFlow::Value evaluateLibraryFunction(const std::unordered_map& args, diff --git a/lib/reverseanalyzer.cpp b/lib/reverseanalyzer.cpp index d85366a7134..a243cc7eaa1 100644 --- a/lib/reverseanalyzer.cpp +++ b/lib/reverseanalyzer.cpp @@ -225,7 +225,7 @@ struct ReverseTraversal { continue; } // Simple assign - if (assignTok->astParent() == assignTop || assignTok == assignTop) { + if (assignTok->str() == "=" && (assignTok->astParent() == assignTop || assignTok == assignTop)) { Analyzer::Action rhsAction = analyzer->analyze(assignTok->astOperand2(), Analyzer::Direction::Reverse); Analyzer::Action lhsAction = diff --git a/lib/settings.cpp b/lib/settings.cpp index be5ef271ee3..1efc55c9015 100644 --- a/lib/settings.cpp +++ b/lib/settings.cpp @@ -23,7 +23,6 @@ #include "vfvalue.h" #include -#include #define PICOJSON_USE_INT64 #include diff --git a/lib/settings.h b/lib/settings.h index a1728761d27..38c7111fbfb 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -128,7 +128,7 @@ class CPPCHECKLIB Settings { bool checkLibrary{}; /** @brief The maximum time in seconds for the checks of a single file */ - std::size_t checksMaxTime{}; + int checksMaxTime{}; /** @brief check unknown function return values */ std::set checkUnknownFunctionReturn; diff --git a/lib/suppressions.cpp b/lib/suppressions.cpp index 7403a3ec448..8b0095edc6b 100644 --- a/lib/suppressions.cpp +++ b/lib/suppressions.cpp @@ -28,7 +28,6 @@ #include #include // std::isdigit, std::isalnum, etc -#include #include #include // std::bind, std::placeholders #include // IWYU pragma: keep diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 2713635c7b7..0135c0cb0d6 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include // IWYU pragma: keep #include @@ -1107,7 +1108,7 @@ void SymbolDatabase::createSymbolDatabaseSetFunctionPointers(bool firstPass) continue; bool isTemplateArg = false; - if (tok->next()->str() != "(") { + if (!Token::Match(tok->next(), "(|{")) { const Token *start = tok; while (Token::Match(start->tokAt(-2), "%name% ::")) start = start->tokAt(-2); @@ -2431,7 +2432,7 @@ Function::Function(const Tokenizer *mTokenizer, const Token *tok1 = setFlags(tok, scope); // find the return type - if (!isConstructor() && !isDestructor() && !isLambda()) { + if (!isConstructor() && !isDestructor()) { // @todo auto type deduction should be checked // @todo attributes and exception specification can also precede trailing return type if (Token::Match(argDef->link()->next(), "const|volatile| &|&&| .")) { // Trailing return type @@ -2442,7 +2443,7 @@ Function::Function(const Tokenizer *mTokenizer, retDef = argDef->link()->tokAt(3); else if (argDef->link()->strAt(3) == ".") retDef = argDef->link()->tokAt(4); - } else { + } else if (!isLambda()) { if (tok1->str() == ">") tok1 = tok1->next(); while (Token::Match(tok1, "extern|virtual|static|friend|struct|union|enum")) @@ -3459,16 +3460,28 @@ const Token *Type::initBaseInfo(const Token *tok, const Token *tok1) return tok2; } -const std::string& Type::name() const +std::string Type::name() const { - const Token* next = classDef->next(); + const Token* start = classDef->next(); if (classScope && classScope->enumClass && isEnumType()) - return next->strAt(1); - if (next->str() == "class") - return next->strAt(1); - if (next->isName()) - return next->str(); - return emptyString; + start = start->tokAt(1); + else if (start->str() == "class") + start = start->tokAt(1); + else if (!start->isName()) + return emptyString; + const Token* next = start; + while (Token::Match(next, "::|<|>|(|)|[|]|*|&|&&|%name%")) { + if (Token::Match(next, "<|(|[") && next->link()) + next = next->link(); + next = next->next(); + } + std::string result; + for (const Token* tok = start; tok != next; tok = tok->next()) { + if (!result.empty()) + result += ' '; + result += tok->str(); + } + return result; } void SymbolDatabase::debugMessage(const Token *tok, const std::string &type, const std::string &msg) const @@ -4357,16 +4370,15 @@ const Function * Function::getOverriddenFunctionRecursive(const ::Type* baseType auto range = parent->functionMap.equal_range(tokenDef->str()); for (std::multimap::const_iterator it = range.first; it != range.second; ++it) { const Function * func = it->second; - if (func->hasVirtualSpecifier()) { // Base is virtual and of same name + if (func->isImplicitlyVirtual()) { // Base is virtual and of same name const Token *temp1 = func->tokenDef->previous(); const Token *temp2 = tokenDef->previous(); bool match = true; // check for matching return parameters - while (temp1->str() != "virtual") { + while (!Token::Match(temp1, "virtual|public:|private:|protected:|{|}|;")) { if (temp1->str() != temp2->str() && - !(temp1->str() == derivedFromType->name() && - temp2->str() == baseType->name())) { + !(temp1->type() && temp2->type() && temp2->type()->isDerivedFrom(temp1->type()->name()))) { match = false; break; } @@ -6848,6 +6860,23 @@ static const Function *getOperatorFunction(const Token * const tok) return nullptr; } +static const Function* getFunction(const Token* tok) { + if (!tok) + return nullptr; + if (tok->function() && tok->function()->retDef) + return tok->function(); + if (const Variable* lvar = tok->variable()) { // lambda + const Function* lambda{}; + if (Token::Match(lvar->nameToken()->next(), "; %varid% = [", lvar->declarationId())) + lambda = lvar->nameToken()->tokAt(4)->function(); + else if (Token::simpleMatch(lvar->nameToken()->next(), "{ [")) + lambda = lvar->nameToken()->tokAt(2)->function(); + if (lambda && lambda->retDef) + return lambda; + } + return nullptr; +} + void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *tokens) { if (!tokens) @@ -6965,10 +6994,10 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to } - // function - else if (tok->previous() && tok->previous()->function() && tok->previous()->function()->retDef) { + // function or lambda + else if (const Function* f = getFunction(tok->previous())) { ValueType valuetype; - if (parsedecl(tok->previous()->function()->retDef, &valuetype, mDefaultSignedness, mSettings, mIsCpp)) + if (parsedecl(f->retDef, &valuetype, mDefaultSignedness, mSettings, mIsCpp)) setValueType(tok, valuetype); } diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index bcb92c101ec..d7c50ee7a2a 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -118,7 +118,7 @@ class CPPCHECKLIB Type { } } - const std::string& name() const; + std::string name() const; const std::string& type() const { return classDef ? classDef->str() : emptyString; diff --git a/lib/token.cpp b/lib/token.cpp index f2caf859573..0e9184626f3 100644 --- a/lib/token.cpp +++ b/lib/token.cpp @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include diff --git a/lib/token.h b/lib/token.h index a5566756f50..d04d365d445 100644 --- a/lib/token.h +++ b/lib/token.h @@ -27,6 +27,7 @@ #include "utils.h" #include "vfvalue.h" +#include #include #include #include @@ -639,6 +640,13 @@ class CPPCHECKLIB Token { setFlag(fIsInline, b); } + bool isAtomic() const { + return getFlag(fIsAtomic); + } + void isAtomic(bool b) { + setFlag(fIsAtomic, b); + } + bool isRestrict() const { return getFlag(fIsRestrict); } @@ -1315,8 +1323,9 @@ class CPPCHECKLIB Token { fIsRemovedVoidParameter = (1ULL << 36), // A void function parameter has been removed fIsIncompleteConstant = (1ULL << 37), fIsRestrict = (1ULL << 38), // Is this a restrict pointer type - fIsSimplifiedTypedef = (1ULL << 39), - fIsFinalType = (1ULL << 40), // Is this a type with final specifier + fIsAtomic = (1ULL << 39), // Is this a _Atomic declaration + fIsSimplifiedTypedef = (1ULL << 40), + fIsFinalType = (1ULL << 41), // Is this a type with final specifier }; enum : uint64_t { diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 39454b2b938..d66f38eb1de 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -34,6 +34,7 @@ #include "token.h" #include "utils.h" #include "valueflow.h" +#include "vfvalue.h" #include #include @@ -3244,7 +3245,7 @@ bool Tokenizer::simplifyUsing() skip = true; simplifyUsingError(usingStart, usingEnd); } - tok1 = after; + tok1 = after->previous(); } if (!skip) @@ -3420,8 +3421,10 @@ void Tokenizer::fillTypeSizes() mTypeSize["short"] = mSettings->platform.sizeof_short; mTypeSize["int"] = mSettings->platform.sizeof_int; mTypeSize["long"] = mSettings->platform.sizeof_long; + mTypeSize["long long"] = mSettings->platform.sizeof_long_long; mTypeSize["float"] = mSettings->platform.sizeof_float; mTypeSize["double"] = mSettings->platform.sizeof_double; + mTypeSize["long double"] = mSettings->platform.sizeof_long_double; mTypeSize["wchar_t"] = mSettings->platform.sizeof_wchar_t; mTypeSize["size_t"] = mSettings->platform.sizeof_size_t; mTypeSize["*"] = mSettings->platform.sizeof_pointer; @@ -4489,7 +4492,7 @@ void Tokenizer::setVarIdPass1() // parse anonymous namespaces as part of the current scope if (!Token::Match(startToken->previous(), "union|struct|enum|namespace {") && - !(initlist && Token::Match(startToken->previous(), "%name%|>|>>") && Token::Match(startToken->link(), "} ,|{"))) { + !(initlist && Token::Match(startToken->previous(), "%name%|>|>>|(") && Token::Match(startToken->link(), "} ,|{|)"))) { if (tok->str() == "{") { bool isExecutable; @@ -4750,7 +4753,8 @@ void Tokenizer::setVarIdPass1() continue; } - if (!scopeStack.top().isEnum || !(Token::Match(tok->previous(), "{|,") && Token::Match(tok->next(), ",|=|}"))) { + if ((!scopeStack.top().isEnum || !(Token::Match(tok->previous(), "{|,") && Token::Match(tok->next(), ",|=|}"))) && + !Token::simpleMatch(tok->next(), ": ;")) { const std::map::const_iterator it = variableMap.map(globalNamespace).find(tok->str()); if (it != variableMap.map(globalNamespace).end()) { tok->varId(it->second); @@ -5242,7 +5246,8 @@ void Tokenizer::createLinks2() } else if (token->str() == "<" && ((token->previous() && (token->previous()->isTemplate() || (token->previous()->isName() && !token->previous()->varId()) || - (token->strAt(-1) == "]" && !Token::Match(token->linkAt(-1)->previous(), "%name%|)")))) || + (token->strAt(-1) == "]" && (!Token::Match(token->linkAt(-1)->previous(), "%name%|)") || token->linkAt(-1)->previous()->isKeyword())) || + (token->strAt(-1) == ")" && token->linkAt(-1)->strAt(-1) == "operator"))) || Token::Match(token->next(), ">|>>"))) { type.push(token); if (token->previous()->str() == "template") @@ -5833,6 +5838,8 @@ void Tokenizer::dump(std::ostream &out) const out << " externLang=\"C\""; if (tok->isExpandedMacro()) out << " isExpandedMacro=\"true\""; + if (tok->isTemplateArg()) + out << " isTemplateArg=\"true\""; if (tok->isRemovedVoidParameter()) out << " isRemovedVoidParameter=\"true\""; if (tok->isSplittedVarDeclComma()) @@ -5845,6 +5852,8 @@ void Tokenizer::dump(std::ostream &out) const out << " isComplex=\"true\""; if (tok->isRestrict()) out << " isRestrict=\"true\""; + if (tok->isAtomic()) + out << " isAtomic=\"true\""; if (tok->isAttributeExport()) out << " isAttributeExport=\"true\""; if (tok->link()) @@ -5905,7 +5914,7 @@ void Tokenizer::dump(std::ostream &out) const for (const TypedefInfo &typedefInfo: mTypedefInfo) { out << " link(); - else if (!isC() && tok2->str() == "<" && tok2->previous()->isName() && !tok2->previous()->varId()) + else if (!isC() && tok2->str() == "<" && ((tok2->previous()->isName() && !tok2->previous()->varId()) || tok2->strAt(-1) == "]")) tok2 = tok2->findClosingBracket(); else if (std::strchr(";,", tok2->str()[0])) { @@ -8045,6 +8054,19 @@ void Tokenizer::reportUnknownMacros() const } } + // Report unknown macros before } "{ .. if (x) MACRO }" + for (const Token *tok = tokens(); tok; tok = tok->next()) { + if (Token::Match(tok, ")|; %name% } !!)")) { + if (tok->link() && !Token::simpleMatch(tok->link()->tokAt(-1), "if")) + continue; + const Token* prev = tok->linkAt(2); + while (Token::simpleMatch(prev, "{")) + prev = prev->previous(); + if (Token::Match(prev, ";|)")) + unknownMacroError(tok->next()); + } + } + // Report unknown macros that contain several statements "MACRO(a;b;c)" for (const Token *tok = tokens(); tok; tok = tok->next()) { if (!Token::Match(tok, "%name% (")) @@ -9039,16 +9061,31 @@ void Tokenizer::simplifyKeyword() tok->deleteNext(); if (c99) { - if (tok->str() == "restrict") { + auto getTypeTokens = [tok]() { + std::vector ret; + for (Token *temp = tok; Token::Match(temp, "%name%"); temp = temp->previous()) { + if (!temp->isKeyword()) + ret.emplace_back(temp); + } for (Token *temp = tok->next(); Token::Match(temp, "%name%"); temp = temp->next()) { - temp->isRestrict(true); + if (!temp->isKeyword()) + ret.emplace_back(temp); } + return ret; + }; + + if (tok->str() == "restrict") { + for (Token* temp: getTypeTokens()) + temp->isRestrict(true); tok->deleteThis(); } if (mSettings->standards.c >= Standards::C11) { - while (tok->str() == "_Atomic") + while (tok->str() == "_Atomic") { + for (Token* temp: getTypeTokens()) + temp->isAtomic(true); tok->deleteThis(); + } } } @@ -9741,7 +9778,7 @@ void Tokenizer::simplifyOperatorName() if (par->str() == "," && !op.empty()) break; if (!(Token::Match(par, "<|>") && !op.empty())) { - op += par->str(); + op += par->str() == "." ? par->originalName() : par->str(); par = par->next(); done = false; } diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp index 809e4f439f9..2e023d54841 100644 --- a/lib/tokenlist.cpp +++ b/lib/tokenlist.cpp @@ -732,11 +732,6 @@ static void compileTerm(Token *&tok, AST_state& state) tok = tok->next(); else throw InternalError(tok, "Syntax error. Unexpected tokens in designated initializer.", InternalError::AST); - } else if (Token::simpleMatch(tok, "{ }")) { - tok->astOperand1(state.op.top()); - state.op.pop(); - state.op.push(tok); - tok = tok->tokAt(2); } } else if (!state.cpp || !Token::Match(tok, "new|delete %name%|*|&|::|(|[")) { std::vector inner; @@ -911,7 +906,7 @@ static void compilePrecedence2(Token *&tok, AST_state& state) } compileBinOp(tok, state, compileScope); } else if (tok->str() == "[") { - if (state.cpp && isPrefixUnary(tok, state.cpp) && Token::Match(tok->link(), "] (|{")) { // Lambda + if (state.cpp && isPrefixUnary(tok, state.cpp) && Token::Match(tok->link(), "] (|{|<")) { // Lambda // What we do here: // - Nest the round bracket under the square bracket. // - Nest what follows the lambda (if anything) with the lambda opening [ @@ -928,8 +923,10 @@ static void compilePrecedence2(Token *&tok, AST_state& state) } } - if (Token::simpleMatch(squareBracket->link(), "] (")) { - Token* const roundBracket = squareBracket->link()->next(); + const bool hasTemplateArg = Token::simpleMatch(squareBracket->link(), "] <") && + Token::simpleMatch(squareBracket->link()->next()->link(), "> ("); + if (Token::simpleMatch(squareBracket->link(), "] (") || hasTemplateArg) { + Token* const roundBracket = hasTemplateArg ? squareBracket->link()->next()->link()->next() : squareBracket->link()->next(); Token* curlyBracket = roundBracket->link()->next(); while (Token::Match(curlyBracket, "mutable|const|constexpr|consteval")) curlyBracket = curlyBracket->next(); diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index e94f87d4634..bbde61c2669 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -109,10 +109,8 @@ #include #include #include -#include #include #include -#include #include #include #include @@ -1093,29 +1091,15 @@ static void setTokenValueCast(Token *parent, const ValueType &valueType, const V static nonneg int getSizeOfType(const Token *typeTok, const Settings *settings) { const ValueType &valueType = ValueType::parseDecl(typeTok, *settings, true); // TODO: set isCpp - if (valueType.pointer > 0) - return settings->platform.sizeof_pointer; - if (valueType.type == ValueType::Type::BOOL || valueType.type == ValueType::Type::CHAR) - return 1; - if (valueType.type == ValueType::Type::SHORT) - return settings->platform.sizeof_short; - if (valueType.type == ValueType::Type::INT) - return settings->platform.sizeof_int; - if (valueType.type == ValueType::Type::LONG) - return settings->platform.sizeof_long; - if (valueType.type == ValueType::Type::LONGLONG) - return settings->platform.sizeof_long_long; - if (valueType.type == ValueType::Type::WCHAR_T) - return settings->platform.sizeof_wchar_t; - return 0; + return ValueFlow::getSizeOf(valueType, settings); } size_t ValueFlow::getSizeOf(const ValueType &vt, const Settings *settings) { if (vt.pointer) return settings->platform.sizeof_pointer; - if (vt.type == ValueType::Type::CHAR) + if (vt.type == ValueType::Type::BOOL || vt.type == ValueType::Type::CHAR) return 1; if (vt.type == ValueType::Type::SHORT) return settings->platform.sizeof_short; @@ -3836,6 +3820,13 @@ const Token* ValueFlow::getEndOfExprScope(const Token* tok, const Scope* default const Token* varEnd = getEndOfVarScope(var); if (!end || (smallest ? precedes(varEnd, end) : succeeds(varEnd, end))) end = varEnd; + + const Token* top = var->nameToken()->astTop(); + if (top && Token::simpleMatch(top->tokAt(-1), "if (")) { // variable declared in if (...) + const Token* elseTok = top->link()->linkAt(1); + if (Token::simpleMatch(elseTok, "} else {") && tok->scope()->isNestedIn(elseTok->tokAt(2)->scope())) + end = tok->scope()->bodyEnd; + } } } return ChildrenToVisit::op1_and_op2; @@ -7218,10 +7209,6 @@ struct MultiValueFlowAnalyzer : ValueFlowAnalyzer { return false; } - bool isGlobal() const override { - return false; - } - bool lowerToPossible() override { for (auto&& p:values) { if (p.second.isImpossible()) @@ -8309,9 +8296,6 @@ bool ValueFlow::isContainerSizeChanged(const Token* tok, int indirect, const Set if (astIsLHS(tok) && Token::Match(tok->astParent(), "%assign%|<<")) return true; const Library::Container* container = tok->valueType()->container; - // Views cannot change container size - if (container->view) - return false; if (astIsLHS(tok) && Token::simpleMatch(tok->astParent(), "[")) return container->stdAssociativeLike; const Library::Container::Action action = astContainerAction(tok); @@ -8780,7 +8764,7 @@ static void valueFlowContainerSize(TokenList* tokenlist, value.setKnown(); valueFlowForward(containerTok->next(), containerTok, value, tokenlist, settings); } - } else if (Token::Match(tok->previous(), ">|return (|{") && astIsContainer(tok)) { + } else if (Token::Match(tok->previous(), ">|return (|{") && astIsContainer(tok) && getLibraryContainer(tok)->size_templateArgNo < 0) { std::vector values; if (Token::simpleMatch(tok, "{")) { values = getInitListSize(tok, tok->valueType(), settings, true); diff --git a/lib/version.h b/lib/version.h index 4151eb136ef..f0d87079eba 100644 --- a/lib/version.h +++ b/lib/version.h @@ -1,17 +1,18 @@ -// For a release version x.y the MAJOR should be x and both MINOR and DEVMINOR should be y. +// For a release version x.y.z the MAJOR should be x and both MINOR and DEVMINOR should be y. // After a release the DEVMINOR is incremented. MAJOR=x MINOR=y, DEVMINOR=y+1 -#define CPPCHECK_MAJOR 2 -#define CPPCHECK_MINOR 11 -#define CPPCHECK_DEVMINOR 12 +#define CPPCHECK_MAJOR_VERSION 2 +#define CPPCHECK_MINOR_VERSION 11 +#define CPPCHECK_DEVMINOR_VERSION 12 +#define CPPCHECK_FIX_VERSION 0 #define STRINGIFY(x) STRING(x) #define STRING(VER) #VER -#if CPPCHECK_MINOR == CPPCHECK_DEVMINOR -#define CPPCHECK_VERSION_STRING STRINGIFY(CPPCHECK_MAJOR) "." STRINGIFY(CPPCHECK_DEVMINOR) -#define CPPCHECK_VERSION CPPCHECK_MAJOR,CPPCHECK_MINOR,0,0 +#if CPPCHECK_MINOR_VERSION == CPPCHECK_DEVMINOR_VERSION +#define CPPCHECK_VERSION_STRING STRINGIFY(CPPCHECK_MAJOR_VERSION) "." STRINGIFY(CPPCHECK_MINOR_VERSION) "." STRINGIFY(CPPCHECK_FIX_VERSION) +#define CPPCHECK_VERSION CPPCHECK_MAJOR_VERSION,CPPCHECK_MINOR_VERSION,CPPCHECK_FIX_VERSION,0 #else -#define CPPCHECK_VERSION_STRING STRINGIFY(CPPCHECK_MAJOR) "." STRINGIFY(CPPCHECK_DEVMINOR) " dev" -#define CPPCHECK_VERSION CPPCHECK_MAJOR,CPPCHECK_MINOR,99,0 +#define CPPCHECK_VERSION_STRING STRINGIFY(CPPCHECK_MAJOR_VERSION) "." STRINGIFY(CPPCHECK_DEVMINOR_VERSION) " dev" +#define CPPCHECK_VERSION CPPCHECK_MAJOR_VERSION,CPPCHECK_MINOR_VERSION,99,0 #endif #define LEGALCOPYRIGHT L"Copyright (C) 2007-2023 Cppcheck team." diff --git a/test/cfg/posix.c b/test/cfg/posix.c index 3da4959536a..0621e983618 100644 --- a/test/cfg/posix.c +++ b/test/cfg/posix.c @@ -330,6 +330,13 @@ double nullPointer_erand48(unsigned short xsubi[3]) return erand48(xsubi); } +struct non_const_parameter_erand48_struct { unsigned short xsubi[3]; }; +// No warning is expected that dat can be const +double non_const_parameter_erand48(struct non_const_parameter_erand48_struct *dat) +{ + return erand48(dat->xsubi); +} + unsigned short *nullPointer_seed48(unsigned short seed16v[3]) { // cppcheck-suppress nullPointer diff --git a/test/fixture.cpp b/test/fixture.cpp index 71fea0f2ff0..64b1471c4f9 100644 --- a/test/fixture.cpp +++ b/test/fixture.cpp @@ -20,13 +20,13 @@ #include "errortypes.h" #include "options.h" -#include "path.h" #include "redirect.h" #include #include #include #include +#include #include #include diff --git a/test/fixture.h b/test/fixture.h index 28d9ececb62..2a3c26eca87 100644 --- a/test/fixture.h +++ b/test/fixture.h @@ -24,17 +24,20 @@ #include "color.h" #include "config.h" #include "errorlogger.h" +#include "errortypes.h" +#include "library.h" +#include "platform.h" #include "settings.h" +#include "standards.h" #include #include -#include #include #include #include +#include class options; -class Settings; class Tokenizer; class TestFixture : public ErrorLogger { diff --git a/test/helpers.cpp b/test/helpers.cpp index 9c86d7320b6..0d33758cc3f 100644 --- a/test/helpers.cpp +++ b/test/helpers.cpp @@ -21,9 +21,11 @@ #include "path.h" #include "preprocessor.h" +#include #include #include -#include +#include // IWYU pragma: keep +#include #include #include #include diff --git a/test/testassert.cpp b/test/testassert.cpp index 8c18720ed04..491d9a183ca 100644 --- a/test/testassert.cpp +++ b/test/testassert.cpp @@ -23,7 +23,6 @@ #include "fixture.h" #include "tokenize.h" -#include #include // IWYU pragma: keep diff --git a/test/testautovariables.cpp b/test/testautovariables.cpp index c4f8aa108f5..0ee1d84132a 100644 --- a/test/testautovariables.cpp +++ b/test/testautovariables.cpp @@ -23,9 +23,7 @@ #include "fixture.h" #include "tokenize.h" -#include #include // IWYU pragma: keep -#include class TestAutoVariables : public TestFixture { public: @@ -847,6 +845,12 @@ class TestAutoVariables : public TestFixture { " return 0;\n" "}\n"); ASSERT_EQUALS("", errout.str()); + + check("void f() {\n" + " S* p = &g();\n" + " delete p;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void testinvaliddealloc_input() { diff --git a/test/testbool.cpp b/test/testbool.cpp index 2e3b2106754..3f311183d2d 100644 --- a/test/testbool.cpp +++ b/test/testbool.cpp @@ -23,7 +23,6 @@ #include "fixture.h" #include "tokenize.h" -#include #include // IWYU pragma: keep class TestBool : public TestFixture { diff --git a/test/testboost.cpp b/test/testboost.cpp index e82feb4f638..39dfeff4100 100644 --- a/test/testboost.cpp +++ b/test/testboost.cpp @@ -23,7 +23,6 @@ #include "fixture.h" #include "tokenize.h" -#include #include // IWYU pragma: keep class TestBoost : public TestFixture { diff --git a/test/testbufferoverrun.cpp b/test/testbufferoverrun.cpp index 45af74ac848..97841f45a7c 100644 --- a/test/testbufferoverrun.cpp +++ b/test/testbufferoverrun.cpp @@ -22,7 +22,6 @@ #include "ctu.h" #include "errortypes.h" #include "standards.h" -#include "library.h" #include "platform.h" #include "settings.h" #include "fixture.h" @@ -174,6 +173,9 @@ class TestBufferOverrun : public TestFixture { TEST_CASE(array_index_69); // #6370 TEST_CASE(array_index_70); // #11355 TEST_CASE(array_index_71); // #11461 + TEST_CASE(array_index_72); // #11784 + TEST_CASE(array_index_73); // #11530 + TEST_CASE(array_index_74); // #11088 TEST_CASE(array_index_multidim); TEST_CASE(array_index_switch_in_for); TEST_CASE(array_index_for_in_for); // FP: #2634 @@ -1924,6 +1926,47 @@ class TestBufferOverrun : public TestFixture { ASSERT_EQUALS("", errout.str()); } + // #11784 + void array_index_72() + { + check("char f(int i) {\n" + " char d[4] = {};\n" + " for (; i < 3; i++) {}\n" + " for (i++; i > 0;) {\n" + " d[--i] = 1;\n" + " break;\n" + " }\n" + " return d[3];\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } + + // #11530 + void array_index_73() + { + check("void f() {\n" + " int k = 0;\n" + " std::function a[1] = {};\n" + " a[k++](0);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } + + // #11088 + void array_index_74() + { + check("void foo(const char *keys) {\n" + " const char *prefix = \"') {}\n" + "}\n" + "void bar() {\n" + " foo(\"q\");\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } + void array_index_multidim() { check("void f()\n" "{\n" diff --git a/test/testclass.cpp b/test/testclass.cpp index 9ed7823ba3a..430fb6c0984 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -19,17 +19,19 @@ #include "check.h" #include "checkclass.h" #include "errortypes.h" -#include "library.h" #include "preprocessor.h" #include "settings.h" #include "fixture.h" #include "tokenize.h" #include +#include #include // IWYU pragma: keep #include +#include #include +#include class TestClass : public TestFixture { public: @@ -180,6 +182,7 @@ class TestClass : public TestFixture { TEST_CASE(const88); TEST_CASE(const89); TEST_CASE(const90); + TEST_CASE(const91); TEST_CASE(const_handleDefaultParameters); TEST_CASE(const_passThisToMemberOfOtherClass); @@ -229,6 +232,8 @@ class TestClass : public TestFixture { TEST_CASE(override1); TEST_CASE(overrideCVRefQualifiers); + TEST_CASE(uselessOverride); + TEST_CASE(thisUseAfterFree); TEST_CASE(unsafeClassRefMember); @@ -660,6 +665,46 @@ class TestClass : public TestFixture { "struct B { bool a; };\n" "template<> struct A<1> : B {};\n"); ASSERT_EQUALS("", errout.str()); + + checkDuplInheritedMembers("struct B {\n" + " int g() const;\n" + " virtual int f() const { return g(); }\n" + "};\n" + "struct D : B {\n" + " int g() const;\n" + " int f() const override { return g(); }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:6]: (warning) The struct 'D' defines member function with name 'g' also defined in its parent struct 'B'.\n", + errout.str()); + + checkDuplInheritedMembers("struct B {\n" + " int g() const;\n" + "};\n" + "struct D : B {\n" + " int g(int) const;\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); + + checkDuplInheritedMembers("struct S {\n" + " struct T {\n" + " T() {}\n" + " };\n" + "};\n" + "struct T : S::T {\n" + " T() : S::T() {}\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); + + checkDuplInheritedMembers("struct S {};\n" // #11827 + "struct SPtr {\n" + " virtual S* operator->() const { return p; }\n" + " S* p = nullptr;\n" + "};\n" + "struct T : public S {};\n" + "struct TPtr : public SPtr {\n" + " T* operator->() const { return (T*)p; }\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); } #define checkCopyConstructor(code) checkCopyConstructor_(code, __FILE__, __LINE__) @@ -6018,7 +6063,7 @@ class TestClass : public TestFixture { " int i{};\n" " S f() { return S{ &i }; }\n" "};\n"); - TODO_ASSERT_EQUALS("[test.cpp:7]: (style, inconclusive) Technically the member function 'C::f' can be const.\n", "", errout.str()); + ASSERT_EQUALS("[test.cpp:7]: (style, inconclusive) Technically the member function 'C::f' can be const.\n", errout.str()); checkConst("struct S {\n" " explicit S(const int* p) : mp(p) {}\n" @@ -6595,6 +6640,20 @@ class TestClass : public TestFixture { errout.str()); } + void const91() { // #11790 + checkConst("struct S {\n" + " char* p;\n" + " template \n" + " T* get() {\n" + " return reinterpret_cast(p);\n" + " }\n" + "};\n" + "const int* f(S& s) {\n" + " return s.get();\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } + void const_handleDefaultParameters() { checkConst("struct Foo {\n" " void foo1(int i, int j = 0) {\n" @@ -8303,6 +8362,18 @@ class TestClass : public TestFixture { " friend T f();\n" "};\n"); ASSERT_EQUALS("", errout.str()); + + checkOverride("struct S {};\n" // #11827 + "struct SPtr {\n" + " virtual S* operator->() const { return p; }\n" + " S* p = nullptr;\n" + "};\n" + "struct T : public S {};\n" + "struct TPtr : public SPtr {\n" + " T* operator->() const { return (T*)p; }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:8]: (style) The function 'operator->' overrides a function in a base class but is not marked with a 'override' specifier.\n", + errout.str()); } void overrideCVRefQualifiers() { @@ -8323,6 +8394,187 @@ class TestClass : public TestFixture { ASSERT_EQUALS("", errout.str()); } + #define checkUselessOverride(code) checkUselessOverride_(code, __FILE__, __LINE__) + void checkUselessOverride_(const char code[], const char* file, int line) { + // Clear the error log + errout.str(""); + + const Settings settings = settingsBuilder().severity(Severity::style).build(); + + // Raw tokens.. + std::vector files(1, "test.cpp"); + std::istringstream istr(code); + const simplecpp::TokenList tokens1(istr, files, files[0]); + + // Preprocess.. + simplecpp::TokenList tokens2(files); + std::map filedata; + simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI()); + + // Tokenize.. + Tokenizer tokenizer(&settings, this); + tokenizer.createTokens(std::move(tokens2)); + ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line); + + // Check.. + CheckClass checkClass(&tokenizer, &settings, this); + (checkClass.checkUselessOverride)(); + } + + void uselessOverride() { + checkUselessOverride("struct B { virtual int f() { return 5; } };\n" // #11757 + "struct D : B {\n" + " int f() override { return B::f(); }\n" + "};"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:3]: (style) The function 'f' overrides a function in a base class but just delegates back to the base class.\n", errout.str()); + + checkUselessOverride("struct B { virtual void f(); };\n" + "struct D : B {\n" + " void f() override { B::f(); }\n" + "};"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:3]: (style) The function 'f' overrides a function in a base class but just delegates back to the base class.\n", errout.str()); + + checkUselessOverride("struct B { virtual int f() = 0; };\n" + "int B::f() { return 5; }\n" + "struct D : B {\n" + " int f() override { return B::f(); }\n" + "};"); + ASSERT_EQUALS("", errout.str()); + + checkUselessOverride("struct B { virtual int f(int i); };\n" + "struct D : B {\n" + " int f(int i) override { return B::f(i); }\n" + "};"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:3]: (style) The function 'f' overrides a function in a base class but just delegates back to the base class.\n", errout.str()); + + checkUselessOverride("struct B { virtual int f(int i); };\n" + "struct D : B {\n" + " int f(int i) override { return B::f(i + 1); }\n" + "};"); + ASSERT_EQUALS("", errout.str()); + + checkUselessOverride("struct B { virtual int f(int i, int j); };\n" + "struct D : B {\n" + " int f(int i, int j) override { return B::f(j, i); }\n" + "};"); + ASSERT_EQUALS("", errout.str()); + + checkUselessOverride("struct B { virtual int f(); };\n" + "struct I { virtual int f() = 0; };\n" + "struct D : B, I {\n" + " int f() override { return B::f(); }\n" + "};"); + ASSERT_EQUALS("", errout.str()); + + checkUselessOverride("struct S { virtual void f(); };\n" + "struct D : S {\n" + " void f() final { S::f(); }\n" + "};"); + ASSERT_EQUALS("", errout.str()); + + checkUselessOverride("struct S {\n" + "protected:\n" + " virtual void f();\n" + "};\n" + "struct D : S {\n" + "public:\n" + " void f() override { S::f(); }\n" + "};"); + ASSERT_EQUALS("", errout.str()); + + checkUselessOverride("struct B { virtual void f(int, int, int) const; };\n" // #11799 + "struct D : B {\n" + " int m = 42;\n" + " void f(int a, int b, int c) const override;\n" + "};\n" + "void D::f(int a, int b, int c) const {\n" + " B::f(a, b, m);\n" + "};"); + ASSERT_EQUALS("", errout.str()); + + checkUselessOverride("struct B {\n" // #11803 + " virtual void f();\n" + " virtual void f(int i);\n" + "};\n" + "struct D : B {\n" + " void f() override { B::f(); }\n" + " void f(int i) override;\n" + " void g() { f(); }\n" + "};"); + ASSERT_EQUALS("", errout.str()); + + checkUselessOverride("struct B { virtual void f(); };\n" // #11808 + "struct D : B { void f() override {} };\n" + "struct D2 : D {\n" + " void f() override {\n" + " B::f();\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout.str()); + + checkUselessOverride("struct B {\n" + " virtual int f() { return 1; }\n" + " virtual int g() { return 7; }\n" + " virtual int h(int i, int j) { return i + j; }\n" + " virtual int j(int i, int j) { return i + j; }\n" + "};\n" + "struct D : B {\n" + " int f() override { return 2; }\n" + " int g() override { return 7; }\n" + " int h(int j, int i) override { return i + j; }\n" + " int j(int i, int j) override { return i + j; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:9]: (style) The function 'g' overrides a function in a base class but is identical to the overridden function\n" + "[test.cpp:5] -> [test.cpp:11]: (style) The function 'j' overrides a function in a base class but is identical to the overridden function\n", + errout.str()); + + checkUselessOverride("struct B : std::exception {\n" + " virtual void f() { throw *this; }\n" + "};\n" + "struct D : B {\n" + " void f() override { throw *this; }\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); + + checkUselessOverride("#define MACRO virtual void f() {}\n" + "struct B {\n" + " MACRO\n" + "};\n" + "struct D : B {\n" + " MACRO\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); + + checkUselessOverride("struct B {\n" + " B() = default;\n" + " explicit B(int i) : m(i) {}\n" + " int m{};\n" + " virtual int f() const { return m; }\n" + "};\n" + "struct D : B {\n" + " explicit D(int i) : m(i) {}\n" + " int m{};\n" + " int f() const override { return m; }\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); + + checkUselessOverride("struct B {\n" + " int g() const;\n" + " virtual int f() const { return g(); }\n" + "};\n" + "struct D : B {\n" + " int g() const;\n" + " int f() const override { return g(); }\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); + + checkUselessOverride("#define MACRO 1\n" + "struct B { virtual int f() { return 1; } };\n" + "struct D : B {\n" + " int f() override { return MACRO; }\n" + "};\n"); + ASSERT_EQUALS("", errout.str()); + } #define checkUnsafeClassRefMember(code) checkUnsafeClassRefMember_(code, __FILE__, __LINE__) void checkUnsafeClassRefMember_(const char code[], const char* file, int line) { diff --git a/test/testcmdlineparser.cpp b/test/testcmdlineparser.cpp index 69e6026cae0..fffa19f5ff7 100644 --- a/test/testcmdlineparser.cpp +++ b/test/testcmdlineparser.cpp @@ -1684,7 +1684,7 @@ class TestCmdlineParser : public TestFixture { void checksMaxTime() { REDIRECT; const char * const argv[] = {"cppcheck", "--checks-max-time=12", "file.cpp"}; - settings.checksMaxTime = SIZE_MAX; + settings.checksMaxTime = 0; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(12, settings.checksMaxTime); ASSERT_EQUALS("", GET_REDIRECT_OUTPUT); @@ -1694,7 +1694,7 @@ class TestCmdlineParser : public TestFixture { REDIRECT; const char * const argv[] = {"cppcheck", "--checks-max-time=-1", "file.cpp"}; ASSERT(!defParser.parseFromArgs(3, argv)); - ASSERT_EQUALS("cppcheck: error: argument to '--checks-max-time=' is not valid - needs to be positive.\n", GET_REDIRECT_OUTPUT); + ASSERT_EQUALS("cppcheck: error: argument to '--checks-max-time=' needs to be a positive integer.\n", GET_REDIRECT_OUTPUT); } void checksMaxTimeInvalid() { diff --git a/test/testcondition.cpp b/test/testcondition.cpp index 3fc84e532e2..b9a7d6e5c34 100644 --- a/test/testcondition.cpp +++ b/test/testcondition.cpp @@ -25,7 +25,6 @@ #include "fixture.h" #include "tokenize.h" -#include #include #include // IWYU pragma: keep #include @@ -912,6 +911,11 @@ class TestCondition : public TestFixture { "#endif\n" " 0;"); ASSERT_EQUALS("", errout.str()); + + check("enum precedence { PC0, UNARY };\n" + "int x = PC0 | UNARY;\n" + "int y = UNARY | PC0;\n"); + ASSERT_EQUALS("", errout.str()); } @@ -4895,6 +4899,16 @@ class TestCondition : public TestFixture { " return p != NULL && q != NULL && p == NULL;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (style) Return value 'p==NULL' is always false\n", errout.str()); + + check("struct S {\n" // #11789 + " std::vector v;\n" + " void f(int i) const;\n" + "};\n" + "void S::f(int i) const {\n" + " int j = i - v.size();\n" + " if (j >= 0) {}\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void alwaysTrueContainer() { diff --git a/test/testerrorlogger.cpp b/test/testerrorlogger.cpp index 1f48090e802..481f67bb7a7 100644 --- a/test/testerrorlogger.cpp +++ b/test/testerrorlogger.cpp @@ -23,7 +23,6 @@ #include "suppressions.h" #include "fixture.h" -#include #include #include diff --git a/test/testexceptionsafety.cpp b/test/testexceptionsafety.cpp index 997c672ee62..274a895a6cb 100644 --- a/test/testexceptionsafety.cpp +++ b/test/testexceptionsafety.cpp @@ -23,7 +23,6 @@ #include "fixture.h" #include "tokenize.h" -#include #include // IWYU pragma: keep class TestExceptionSafety : public TestFixture { diff --git a/test/testfunctions.cpp b/test/testfunctions.cpp index 20254275d43..418f3613078 100644 --- a/test/testfunctions.cpp +++ b/test/testfunctions.cpp @@ -18,13 +18,11 @@ #include "checkfunctions.h" #include "errortypes.h" -#include "library.h" #include "settings.h" #include "standards.h" #include "fixture.h" #include "tokenize.h" -#include #include // IWYU pragma: keep #include @@ -119,6 +117,16 @@ class TestFunctions : public TestFixture { std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, filename), file, line); + // filter out ValueFlow messages.. + const std::string debugwarnings = errout.str(); + errout.str(""); + std::istringstream istr2(debugwarnings); + std::string errline; + while (std::getline(istr2, errline)) { + if (errline.find("valueflow.cpp") == std::string::npos) + errout << errline << "\n"; + } + runChecks(&tokenizer, settings_, this); } @@ -1819,6 +1827,7 @@ class TestFunctions : public TestFixture { void checkLibraryMatchFunctions() { Settings s = settingsBuilder(settings).checkLibrary().build(); s.daca = true; + s.debugwarnings = true; check("void f() {\n" " lib_func();" @@ -1934,6 +1943,8 @@ class TestFunctions : public TestFixture { " q->push_back(1);\n" "}\n", "test.cpp", &s); TODO_ASSERT_EQUALS("", + "[test.cpp:2]: (debug) auto token with no type.\n" + "[test.cpp:4]: (debug) auto token with no type.\n" "[test.cpp:3]: (information) --check-library: There is no matching configuration for function auto::push_back()\n" "[test.cpp:5]: (information) --check-library: There is no matching configuration for function auto::push_back()\n", errout.str()); @@ -1949,7 +1960,9 @@ class TestFunctions : public TestFixture { check("auto f() {\n" " return std::runtime_error(\"abc\");\n" "}\n", "test.cpp", &s); - ASSERT_EQUALS("", errout.str()); + TODO_ASSERT_EQUALS("", + "[test.cpp:1]: (debug) auto token with no type.\n", + errout.str()); check("struct S {\n" // #11543 " S() {}\n" @@ -2007,6 +2020,16 @@ class TestFunctions : public TestFixture { " void f(int i) { push_back(i); }\n" "};\n", "test.cpp", &s); ASSERT_EQUALS("", errout.str()); + + check("void f() {\n" + " auto g = []() -> std::string { return \"abc\"; };\n" + " auto s = g();\n" + " if (s.at(0)) {}\n" + " auto h{ []() -> std::string { return \"xyz\"; } };\n" + " auto t = h();\n" + " if (t.at(0)) {}\n" + "};\n", "test.cpp", &s); + ASSERT_EQUALS("", errout.str()); } void checkUseStandardLibrary1() { diff --git a/test/testinternal.cpp b/test/testinternal.cpp index 0904b62e014..4092e45e9c6 100644 --- a/test/testinternal.cpp +++ b/test/testinternal.cpp @@ -23,7 +23,6 @@ #include "fixture.h" #include "settings.h" -#include #include class TestInternal : public TestFixture { diff --git a/test/testio.cpp b/test/testio.cpp index 81fb3707620..4db7e72e423 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -25,7 +25,6 @@ #include "fixture.h" #include "tokenize.h" -#include #include // IWYU pragma: keep #include diff --git a/test/testleakautovar.cpp b/test/testleakautovar.cpp index 9d61b7cd7d1..11eca62d325 100644 --- a/test/testleakautovar.cpp +++ b/test/testleakautovar.cpp @@ -24,7 +24,6 @@ #include "fixture.h" #include "tokenize.h" -#include #include #include // IWYU pragma: keep #include @@ -2080,6 +2079,22 @@ class TestLeakAutoVar : public TestFixture { " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); + + check("void f(node **p) {\n" + " node* n = *p;\n" + " if (n->left == NULL) {\n" + " *p = n->right;\n" + " free(n);\n" + " }\n" + " else if (n->right == NULL) {\n" + " *p = n->left;\n" + " free(n);\n" + " }\n" + " else {\n" + " for (int i = 0; i < 4; ++i) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void mismatchAllocDealloc() { diff --git a/test/testlibrary.cpp b/test/testlibrary.cpp index fe3bd036c4e..16caaa4f69c 100644 --- a/test/testlibrary.cpp +++ b/test/testlibrary.cpp @@ -26,6 +26,7 @@ #include "tokenize.h" #include "tokenlist.h" +#include #include #include // IWYU pragma: keep #include diff --git a/test/testmemleak.cpp b/test/testmemleak.cpp index 2684e5b04cd..beedd57b232 100644 --- a/test/testmemleak.cpp +++ b/test/testmemleak.cpp @@ -26,7 +26,6 @@ #include #include // IWYU pragma: keep -#include class TestMemleakInClass; class TestMemleakNoVar; diff --git a/test/testoptions.cpp b/test/testoptions.cpp index c97d59402ec..f03f183db9e 100644 --- a/test/testoptions.cpp +++ b/test/testoptions.cpp @@ -1,22 +1,25 @@ -// Cppcheck - A tool for static C/C++ code analysis -// Copyright (C) 2007-2023 Cppcheck team. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "options.h" #include "fixture.h" +#include #include #include diff --git a/test/testother.cpp b/test/testother.cpp index 0fa1c8c26f8..8c54d541feb 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -26,7 +26,6 @@ #include "fixture.h" #include "tokenize.h" -#include #include #include // IWYU pragma: keep #include @@ -106,6 +105,7 @@ class TestOther : public TestFixture { TEST_CASE(varScope32); // #11441 TEST_CASE(varScope33); TEST_CASE(varScope34); + TEST_CASE(varScope35); TEST_CASE(oldStylePointerCast); TEST_CASE(invalidPointerCast); @@ -820,6 +820,14 @@ class TestOther : public TestFixture { " STATIC_ASSERT(sizeof(int) == sizeof(FOO));\n" "}\n"); ASSERT_EQUALS("", errout.str()); + + // #11505 + check("void f(uint16_t num, uint8_t radix) {\n" + " int c = num % radix;\n" + " num /= radix;\n" + " if (!num) {}\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void nanInArithmeticExpression() { @@ -1628,6 +1636,37 @@ class TestOther : public TestFixture { ASSERT_EQUALS("", errout.str()); } + void varScope35() { // #11845 + check("void f(int err, const char* src) {\n" + " const char* msg = \"Success\";\n" + " char buf[42];\n" + " if (err != 0)\n" + " msg = strcpy(buf, src);\n" + " printf(\"%d: %s\\n\", err, msg);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("char* g(char* dst, const char* src);\n" + "void f(int err, const char* src) {\n" + " const char* msg = \"Success\";\n" + " char buf[42];\n" + " if (err != 0)\n" + " msg = g(buf, src);\n" + " printf(\"%d: %s\\n\", err, msg);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("char* g(char* dst, const char* src);\n" + "void f(int err, const char* src) {\n" + " const char* msg = \"Success\";\n" + " char buf[42];\n" + " if (err != 0)\n" + " g(buf, src);\n" + " printf(\"%d: %s\\n\", err, msg);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style) The scope of the variable 'buf' can be reduced.\n", errout.str()); + } + #define checkOldStylePointerCast(code) checkOldStylePointerCast_(code, __FILE__, __LINE__) void checkOldStylePointerCast_(const char code[], const char* file, int line) { // Clear the error buffer.. @@ -3262,6 +3301,12 @@ class TestOther : public TestFixture { " const int* p = s.g();\n" "}\n"); ASSERT_EQUALS("", errout.str()); + + check("struct S { int x; };\n" // #11818 + "std::istream& f(std::istream& is, S& s) {\n" + " return is >> s.x;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void constParameterCallback() { @@ -3740,6 +3785,13 @@ class TestOther : public TestFixture { " p = q;\n" "}\n"); ASSERT_EQUALS("", errout.str()); + + check("struct S { int a[1]; };\n" + "void f(S* s) {\n" + " if (s->a[0]) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 's' can be declared as pointer to const\n", + errout.str()); } void switchRedundantAssignmentTest() { @@ -6556,6 +6608,15 @@ class TestOther : public TestFixture { " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) The comparison 'p == 0' is always true.\n", errout.str()); + + // #11820 + check("unsigned f(unsigned x) {\n" + " return x - !!x;\n" + "}\n" + "unsigned g(unsigned x) {\n" + " return !!x - x;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void duplicateExpression8() { diff --git a/test/testsimplifyusing.cpp b/test/testsimplifyusing.cpp index 1a6e2fde1d6..fb6c92bb9ce 100644 --- a/test/testsimplifyusing.cpp +++ b/test/testsimplifyusing.cpp @@ -72,6 +72,7 @@ class TestSimplifyUsing : public TestFixture { TEST_CASE(simplifyUsing25); TEST_CASE(simplifyUsing26); // #11090 TEST_CASE(simplifyUsing27); + TEST_CASE(simplifyUsing28); TEST_CASE(simplifyUsing8970); TEST_CASE(simplifyUsing8971); @@ -675,6 +676,16 @@ class TestSimplifyUsing : public TestFixture { ASSERT_EQUALS(expected, tok(code)); } + void simplifyUsing28() { // #11795 + const char code[] = "void f() {\n" + " using T = int;\n" + " T* p{ new T };\n" + "}\n"; + const char expected[] = "void f ( ) { int * p { new int } ; }"; + ASSERT_EQUALS(expected, tok(code, cppcheck::Platform::Type::Native, /*debugwarnings*/ true)); + ASSERT_EQUALS("", errout.str()); + } + void simplifyUsing8970() { const char code[] = "using V = std::vector;\n" "struct A {\n" diff --git a/test/testsingleexecutor.cpp b/test/testsingleexecutor.cpp index 1ebf9a83c58..2bdf94c8608 100644 --- a/test/testsingleexecutor.cpp +++ b/test/testsingleexecutor.cpp @@ -19,6 +19,7 @@ #include "cppcheck.h" #include "fixture.h" #include "helpers.h" +#include "importproject.h" #include "redirect.h" #include "library.h" #include "settings.h" @@ -28,10 +29,10 @@ #include #include #include +#include #include #include #include -#include #include #include #include diff --git a/test/testsizeof.cpp b/test/testsizeof.cpp index fafc845267c..e16e4db6c72 100644 --- a/test/testsizeof.cpp +++ b/test/testsizeof.cpp @@ -22,7 +22,6 @@ #include "fixture.h" #include "tokenize.h" -#include #include #include // IWYU pragma: keep #include diff --git a/test/teststl.cpp b/test/teststl.cpp index c4319124868..b25a4443108 100644 --- a/test/teststl.cpp +++ b/test/teststl.cpp @@ -25,7 +25,6 @@ #include "utils.h" #include -#include #include // IWYU pragma: keep #include @@ -888,6 +887,41 @@ class TestStl : public TestFixture { " auto x = v->back();\n" "}\n"); ASSERT_EQUALS("", errout.str()); + + checkNormal("template \n" + "struct Foo {\n" + " std::array items = {0};\n" + " T maxCount = count;\n" + " explicit Foo(const T& maxValue = (std::numeric_limits::max)()) : maxCount(maxValue) {}\n" + " bool Set(const uint8_t idx) {\n" + " if (CheckBounds(idx) && items[idx] < maxCount) {\n" + " items[idx] += 1;\n" + " return true;\n" + " }\n" + " return false;\n" + " }\n" + " static bool CheckBounds(const uint8_t idx) { return idx < count; }\n" + "};\n" + "void f() {\n" + " Foo x;\n" + " if (x.Set(42U)) {}\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkNormal("struct S { void g(std::span& r) const; };\n" // #11828 + "int f(const S& s) {\n" + " std::span t;\n" + " s.g(t);\n" + " return t[0];\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkNormal("char h() {\n" + " std::string s;\n" + " std::string_view sv(s);\n" + " return s[2];\n" + "}\n"); + TODO_ASSERT_EQUALS("test.cpp:4:error:Out of bounds access in expression 's[2]' because 's' is empty.\n", "", errout.str()); } void outOfBoundsSymbolic() @@ -2314,6 +2348,19 @@ class TestStl : public TestFixture { "}\n"); ASSERT_EQUALS("", errout.str()); + check("struct B { virtual int g() { return 0; } };\n" // #11831 + "struct C {\n" + " int h() const { return b->g(); }\n" + " B* b;\n" + "};\n" + "struct O {\n" + " int f() const;\n" + " std::vector v;\n" + " C c;\n" + "};\n" + "int O::f() const { return v[c.h() - 1]; }\n"); + ASSERT_EQUALS("", errout.str()); + const auto oldSettings = settings; settings.daca = true; @@ -4236,6 +4283,20 @@ class TestStl : public TestFixture { ASSERT_EQUALS("[test.cpp:2]: (performance) Assigning the result of c_str() to a std::string_view is slow and redundant.\n" "[test.cpp:6]: (performance) Constructing a std::string_view from the result of c_str() is slow and redundant.\n", errout.str()); + + check("void f(const std::string& s) {\n" // #11819 + " std::string_view sv(s.data(), 13);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("struct S { std::string x; };\n" // #11802 + "std::vector> global;\n" + "const char* f() {\n" + " auto s = std::make_shared();\n" + " global.push_back(s);\n" + " return s->x.c_str();\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void uselessCalls() { diff --git a/test/teststring.cpp b/test/teststring.cpp index 61bc1e5f055..24a4f1c3a54 100644 --- a/test/teststring.cpp +++ b/test/teststring.cpp @@ -23,7 +23,6 @@ #include "fixture.h" #include "tokenize.h" -#include #include // IWYU pragma: keep diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index a9d0af457fa..8c2bec6bd8f 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -249,6 +249,7 @@ class TestSymbolDatabase : public TestFixture { TEST_CASE(functionArgs20); TEST_CASE(functionImplicitlyVirtual); + TEST_CASE(functionGetOverridden); TEST_CASE(functionIsInlineKeyword); @@ -472,6 +473,7 @@ class TestSymbolDatabase : public TestFixture { TEST_CASE(lambda2); // #7473 TEST_CASE(lambda3); TEST_CASE(lambda4); + TEST_CASE(lambda5); TEST_CASE(circularDependencies); // #6298 @@ -2729,6 +2731,24 @@ class TestSymbolDatabase : public TestFixture { ASSERT_EQUALS(true, function && function->isImplicitlyVirtual(false)); } + void functionGetOverridden() { + GET_SYMBOL_DB("struct B { virtual void f(); };\n" + "struct D : B {\n" + "public:\n" + " void f() override;\n" + "};\n" + "struct D2 : D { void f() override {} };\n"); + ASSERT(db != nullptr); + ASSERT_EQUALS(5, db->scopeList.size()); + const Function *func = db->scopeList.back().function; + ASSERT(func && func->nestedIn); + ASSERT_EQUALS("D2", func->nestedIn->className); + bool foundAllBaseClasses{}; + const Function* baseFunc = func->getOverriddenFunction(&foundAllBaseClasses); + ASSERT(baseFunc && baseFunc->nestedIn && foundAllBaseClasses); + ASSERT_EQUALS("D", baseFunc->nestedIn->className); + } + void functionIsInlineKeyword() { GET_SYMBOL_DB("inline void fs() {}"); (void)db; @@ -7775,6 +7795,26 @@ class TestSymbolDatabase : public TestFixture { ASSERT_EQUALS(s.type()->classScope, &*scope); } + void lambda5() { // #11275 + GET_SYMBOL_DB("int* f() {\n" + " auto g = []() {\n" + " return true;\n" + " };\n" + " return nullptr;\n" + "}\n"); + + ASSERT(db && db->scopeList.size() == 3); + std::list::const_iterator scope = db->scopeList.cbegin(); + ASSERT_EQUALS(Scope::eGlobal, scope->type); + ++scope; + ASSERT_EQUALS(Scope::eFunction, scope->type); + ++scope; + ASSERT_EQUALS(Scope::eLambda, scope->type); + const Token* ret = Token::findsimplematch(tokenizer.tokens(), "return true"); + ASSERT(ret && ret->scope()); + ASSERT_EQUALS(ret->scope()->type, Scope::eLambda); + } + // #6298 "stack overflow in Scope::findFunctionInBase (endless recursion)" void circularDependencies() { check("template class E,class D> class C : E {\n" diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index 33da2ca4108..2e0972d07b8 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -3501,6 +3501,55 @@ class TestTokenizer : public TestFixture { ASSERT_EQUALS(true, tok1->link() == tok2); ASSERT_EQUALS(true, tok2->link() == tok1); } + + { // #11810 + const char code[] = "void f() {\n" + " auto g = [] (A a, B&& b) { return a < b; };\n" + "}\n"; + errout.str(""); + Tokenizer tokenizer(&settings0, this); + std::istringstream istr(code); + ASSERT(tokenizer.tokenize(istr, "test.cpp")); + const Token* tok1 = Token::findsimplematch(tokenizer.tokens(), "< A"); + const Token* tok2 = Token::findsimplematch(tok1, "> ("); + ASSERT_EQUALS(true, tok1->link() == tok2); + ASSERT_EQUALS(true, tok2->link() == tok1); + } + + { + const char code[] = "void f() {\n" + " auto g = [] () {\n" + " return [] () {};\n" + " };\n" + "}\n"; + errout.str(""); + Tokenizer tokenizer(&settings0, this); + std::istringstream istr(code); + ASSERT(tokenizer.tokenize(istr, "test.cpp")); + const Token* tok1 = Token::findsimplematch(tokenizer.tokens(), "< T"); + const Token* tok2 = Token::findsimplematch(tok1, "> ("); + ASSERT_EQUALS(true, tok1->link() == tok2); + ASSERT_EQUALS(true, tok2->link() == tok1); + } + + { + const char code[] = "struct S {\n" // #11840 + " template\n" + " void operator() (int);\n" + "};\n" + "void f() {\n" + " S s;\n" + " s.operator()(1);\n" + "}\n"; + errout.str(""); + Tokenizer tokenizer(&settings0, this); + std::istringstream istr(code); + ASSERT(tokenizer.tokenize(istr, "test.cpp")); + const Token* tok1 = Token::findsimplematch(tokenizer.tokens(), "< int"); + const Token* tok2 = Token::findsimplematch(tok1, "> ("); + ASSERT_EQUALS(true, tok1->link() == tok2); + ASSERT_EQUALS(true, tok2->link() == tok1); + } } void simplifyString() { @@ -4920,7 +4969,7 @@ class TestTokenizer : public TestFixture { const char code[] = "void f() {" "static_cast(xResult.operator->())->GetMatrix();" "}"; - const char result[] = "void f ( ) { static_cast < ScToken * > ( xResult . operator. ( ) ) . GetMatrix ( ) ; }"; + const char result[] = "void f ( ) { static_cast < ScToken * > ( xResult . operator-> ( ) ) . GetMatrix ( ) ; }"; ASSERT_EQUALS(result, tokenizeAndStringify(code)); } @@ -6336,6 +6385,7 @@ class TestTokenizer : public TestFixture { ASSERT_EQUALS("var{{,{{,{", testAst("auto var{ {{},{}}, {} };")); ASSERT_EQUALS("fXYabcfalse==CD:?,{,{(", testAst("f({X, {Y, abc == false ? C : D}});")); ASSERT_EQUALS("stdvector::p0[{(return", testAst("return std::vector({ p[0] });")); + ASSERT_EQUALS("vstdvector::{=", testAst("auto v = std::vector{ };")); // Initialization with decltype(expr) instead of a type ASSERT_EQUALS("decltypex((", testAst("decltype(x)();")); @@ -6842,6 +6892,18 @@ class TestTokenizer : public TestFixture { ASSERT_NO_THROW(tokenizeAndStringify(code11)); ASSERT_NO_THROW(tokenizeAndStringify("alignas(8) alignas(16) int x;")); // alignas is not unknown macro + + ASSERT_THROW(tokenizeAndStringify("void foo() { if(x) SYSTEM_ERROR }"), InternalError); + ASSERT_THROW(tokenizeAndStringify("void foo() { dostuff(); SYSTEM_ERROR }"), InternalError); + + ASSERT_NO_THROW(tokenizeAndStringify("void f(void* q) {\n" + " g(&(S) { .p = (int*)q });\n" + "}\n", /*expand*/ true, cppcheck::Platform::Type::Native, "test.c")); + + ASSERT_NO_THROW(tokenizeAndStringify("typedef struct { int i; } S;\n" + "void f(float a) {\n" + "S s = (S){ .i = (int)a };\n" + "}\n", /*expand*/ true, cppcheck::Platform::Type::Native, "test.c")); } void findGarbageCode() { // Test Tokenizer::findGarbageCode() diff --git a/test/testtype.cpp b/test/testtype.cpp index b8a60e4c813..c49ef7aae31 100644 --- a/test/testtype.cpp +++ b/test/testtype.cpp @@ -24,7 +24,6 @@ #include "fixture.h" #include "tokenize.h" -#include #include // IWYU pragma: keep #include @@ -326,9 +325,19 @@ class TestType : public TestFixture { void longCastAssign() { const Settings settings = settingsBuilder().severity(Severity::style).platform(cppcheck::Platform::Type::Unix64).build(); + const Settings settingsWin = settingsBuilder().severity(Severity::style).platform(cppcheck::Platform::Type::Win64).build(); + + const char code[] = "long f(int x, int y) {\n" + " const long ret = x * y;\n" + " return ret;\n" + "}\n"; + check(code, settings); + ASSERT_EQUALS("[test.cpp:2]: (style) int result is assigned to long variable. If the variable is long to avoid loss of information, then you have loss of information.\n", errout.str()); + check(code, settingsWin); + ASSERT_EQUALS("", errout.str()); check("long f(int x, int y) {\n" - " const long ret = x * y;\n" + " long ret = x * y;\n" " return ret;\n" "}\n", settings); ASSERT_EQUALS("[test.cpp:2]: (style) int result is assigned to long variable. If the variable is long to avoid loss of information, then you have loss of information.\n", errout.str()); @@ -352,21 +361,39 @@ class TestType : public TestFixture { " return ret;\n" "}\n", settings); ASSERT_EQUALS("", errout.str()); + + check("double g(float f) {\n" + " return f * f;\n" + "}\n", settings); + ASSERT_EQUALS("[test.cpp:2]: (style) float result is returned as double value. If the return value is double to avoid loss of information, then you have loss of information.\n", + errout.str()); } void longCastReturn() { - const Settings settings = settingsBuilder().severity(Severity::style).build(); + const Settings settings = settingsBuilder().severity(Severity::style).platform(cppcheck::Platform::Type::Unix64).build(); + const Settings settingsWin = settingsBuilder().severity(Severity::style).platform(cppcheck::Platform::Type::Win64).build(); - check("long f(int x, int y) {\n" - " return x * y;\n" - "}\n", settings); + const char code[] = "long f(int x, int y) {\n" + " return x * y;\n" + "}\n"; + check(code, settings); ASSERT_EQUALS("[test.cpp:2]: (style) int result is returned as long value. If the return value is long to avoid loss of information, then you have loss of information.\n", errout.str()); + check(code, settingsWin); + ASSERT_EQUALS("", errout.str()); + + const char code2[] = "long long f(int x, int y) {\n" + " return x * y;\n" + "}\n"; + check(code2, settings); + ASSERT_EQUALS("[test.cpp:2]: (style) int result is returned as long long value. If the return value is long long to avoid loss of information, then you have loss of information.\n", errout.str()); + check(code2, settingsWin); + ASSERT_EQUALS("[test.cpp:2]: (style) int result is returned as long long value. If the return value is long long to avoid loss of information, then you have loss of information.\n", errout.str()); // typedef check("size_t f(int x, int y) {\n" " return x * y;\n" "}\n", settings); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS("[test.cpp:2]: (style) int result is returned as long value. If the return value is long to avoid loss of information, then you have loss of information.\n", errout.str()); } // This function ensure that test works with different compilers. Floats can diff --git a/test/testuninitvar.cpp b/test/testuninitvar.cpp index 520336e3899..924ebcaa67b 100644 --- a/test/testuninitvar.cpp +++ b/test/testuninitvar.cpp @@ -20,7 +20,6 @@ #include "checkuninitvar.h" #include "ctu.h" #include "errortypes.h" -#include "library.h" #include "settings.h" #include "fixture.h" #include "tokenize.h" @@ -75,6 +74,7 @@ class TestUninitVar : public TestFixture { TEST_CASE(uninitvar11); // ticket #9123 TEST_CASE(uninitvar12); // #10218 - stream read TEST_CASE(uninitvar13); // #9772 + TEST_CASE(uninitvar14); TEST_CASE(uninitvar_unconditionalTry); TEST_CASE(uninitvar_funcptr); // #6404 TEST_CASE(uninitvar_operator); // #6680 @@ -3126,6 +3126,15 @@ class TestUninitVar : public TestFixture { ASSERT_EQUALS("", errout.str()); } + void uninitvar14() { // #11832 + const char code[] = "void f() {\n" + " int b;\n" + " *(&b) = 0;\n" + "}"; + checkUninitVar(code); + ASSERT_EQUALS("", errout.str()); + } + void uninitvar_unconditionalTry() { // Unconditional scopes and try{} scopes checkUninitVar("int f() {\n" @@ -3784,7 +3793,7 @@ class TestUninitVar : public TestFixture { " int a;\n" " int *p = ptr ? ptr : &a;\n" "}"); - TODO_ASSERT_EQUALS("", "[test.cpp:3]: (error) Uninitialized variable: &a\n", errout.str()); + ASSERT_EQUALS("", errout.str()); valueFlowUninit("int f(int a) {\n" " int x;\n" @@ -6106,6 +6115,18 @@ class TestUninitVar : public TestFixture { " (void)a[i];\n" "}\n"); ASSERT_EQUALS("", errout.str()); + + valueFlowUninit("void f() {\n" + " int x;\n" + " int *p = 0 ? 0 : &x;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + valueFlowUninit("void g() {\n" + " int y;\n" + " int *q = 1 ? &y : 0;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void valueFlowUninitBreak() { // Do not show duplicate warnings about the same uninitialized value diff --git a/test/testunusedfunctions.cpp b/test/testunusedfunctions.cpp index 0eed7a68fd6..0cccc44ccbc 100644 --- a/test/testunusedfunctions.cpp +++ b/test/testunusedfunctions.cpp @@ -24,7 +24,6 @@ #include "tokenize.h" #include -#include class TestUnusedFunctions : public TestFixture { public: diff --git a/test/testunusedvar.cpp b/test/testunusedvar.cpp index 51562221b56..527fb5c1916 100644 --- a/test/testunusedvar.cpp +++ b/test/testunusedvar.cpp @@ -165,6 +165,8 @@ class TestUnusedVar : public TestFixture { TEST_CASE(localvaralias19); // ticket #9828 TEST_CASE(localvaralias20); // ticket #10966 TEST_CASE(localvaralias21); + TEST_CASE(localvaralias22); + TEST_CASE(localvaralias23); TEST_CASE(localvarasm); TEST_CASE(localvarstatic); TEST_CASE(localvarextern); @@ -1933,6 +1935,31 @@ class TestUnusedVar : public TestFixture { " return v.begin()->b;\n" "}\n"); ASSERT_EQUALS("", errout.str()); + + checkStructMemberUsage("int f(int s) {\n" // #10587 + " const struct S { int a, b; } Map[] = { { 0, 1 }, { 2, 3 } };\n" + " auto it = std::find_if(std::begin(Map), std::end(Map), [&](const auto& m) { return s == m.a; });\n" + " if (it != std::end(Map))\n" + " return it->b;\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkStructMemberUsage("int f(int s) {\n" + " const struct S { int a, b; } Map[] = { { 0, 1 }, { 2, 3 } };\n" + " for (auto&& m : Map)\n" + " if (m.a == s)\n" + " return m.b;\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + checkStructMemberUsage("struct R { bool b{ false }; };\n" // #11539 + "void f(std::optional r) {\n" + " if (r.has_value())\n" + " std::cout << r->b;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void structmember_macro() { @@ -2613,6 +2640,12 @@ class TestUnusedVar : public TestFixture { " *(b+i) = 0;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (style) Variable '*(b+i)' is assigned a value that is never used.\n", "", errout.str()); + + functionVariableUsage("void f() {\n" // #11832 + " int b;\n" + " *(&b) = 0;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void localvar8() { @@ -5012,6 +5045,36 @@ class TestUnusedVar : public TestFixture { ASSERT_EQUALS("", errout.str()); } + void localvaralias22() { // #11139 + functionVariableUsage("int f() {\n" + " int x[1], *p = x;\n" + " x[0] = 42;\n" + " return *p;\n" + "}\n" + "int g() {\n" + " int x[1], *p{ x };\n" + " x[0] = 42;\n" + " return *p;\n" + "}\n" + "int h() {\n" + " int x[1], *p(x);\n" + " x[0] = 42;\n" + " return *p;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } + + void localvaralias23() { // #11817 + functionVariableUsage("void f(int& r, bool a, bool b) {\n" + " int& e = r;\n" + " if (a)\n" + " e = 42;\n" + " else if (b)\n" + " e = 1;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } + void localvarasm() { functionVariableUsage("void foo(int &b)\n" @@ -5383,6 +5446,37 @@ class TestUnusedVar : public TestFixture { " return y;\n" "}"); ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("int f(int i) {\n" // #11788 + " if (int x = i) {\n" + " return x;\n" + " }\n" + " else {\n" + " x = 12;\n" + " return x;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("void f(int i) {\n" + " if (int x = i) {\n" + " while (x < 100) {\n" + " if (x % 2 == 0) {\n" + " x += 3;\n" + " }\n" + " else if (x % 3 == 0) {\n" + " x += 5;\n" + " }\n" + " else {\n" + " x += 7;\n" + " }\n" + " x += 6;\n" + " }\n" + " return x;\n" + " }\n" + " return i;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void localvarOpAssign() { @@ -6172,6 +6266,15 @@ class TestUnusedVar : public TestFixture { " s[0] = 0;\n" "}\n"); ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("struct S {\n" + " std::mutex m;\n" + " void f();\n" + "};\n" + "void S::f() {\n" + " const ::std::lock_guard g(m);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void localVarClass() { @@ -6453,6 +6556,11 @@ class TestUnusedVar : public TestFixture { " std::list>::value_type a{ 1, 2, 3, 4 };\n" "}\n"); TODO_ASSERT_EQUALS("", "[test.cpp:2]: (information) --check-library: Provide configuration for std::list::value_type\n", errout.str()); + + functionVariableUsage("void f(int* p) {\n" + " int* q{ p };\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'q' is assigned a value that is never used.\n", errout.str()); } void localvarRangeBasedFor() { diff --git a/test/testutils.cpp b/test/testutils.cpp index 6b61634822a..09f92d3815a 100644 --- a/test/testutils.cpp +++ b/test/testutils.cpp @@ -16,14 +16,13 @@ * along with this program. If not, see . */ -#include "helpers.h" #include "fixture.h" -#include "settings.h" #include "utils.h" #include #include #include +#include #include class TestUtils : public TestFixture { diff --git a/test/testvaarg.cpp b/test/testvaarg.cpp index b4307c32d6e..d160336f300 100644 --- a/test/testvaarg.cpp +++ b/test/testvaarg.cpp @@ -23,7 +23,6 @@ #include "fixture.h" #include "tokenize.h" -#include #include // IWYU pragma: keep class TestVaarg : public TestFixture { diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index 38c9f39afb5..4e322596175 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -5565,6 +5565,17 @@ class TestValueFlow : public TestFixture { "}"; values = tokenValues(code, "x + 1", ValueFlow::Value::ValueType::UNINIT); ASSERT_EQUALS(0, values.size()); + + code = "void g() {\n" + " int y;\n" + " int *q = 1 ? &y : 0;\n" + "}\n"; + values = tokenValues(code, "y :", ValueFlow::Value::ValueType::UNINIT); + ASSERT_EQUALS(1, values.size()); + ASSERT_EQUALS(true, values.front().isUninitValue()); + values = tokenValues(code, "& y :", ValueFlow::Value::ValueType::UNINIT); + ASSERT_EQUALS(1, values.size()); + ASSERT_EQUALS(true, values.front().isUninitValue()); } void valueFlowConditionExpressions() { @@ -6611,10 +6622,13 @@ class TestValueFlow : public TestFixture { ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "( ) ;"), 0)); code = "std::vector f() { return std::vector{}; }"; - TODO_ASSERT_EQUALS("", "values.size():0", isKnownContainerSizeValue(tokenValues(code, "{ } ;"), 0)); + ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "{ } ;"), 0)); code = "std::vector f() { return {}; }"; ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "{ } ;"), 0)); + + code = "int f() { auto a = std::array{}; return a[1]; }"; + ASSERT_EQUALS("values.size():0", isKnownContainerSizeValue(tokenValues(code, "a ["), 0)); } void valueFlowContainerElement() @@ -6798,6 +6812,14 @@ class TestValueFlow : public TestFixture { " dummy_resource::log.clear();\n" "}\n"; valueOfTok(code, "log"); + + code = "struct D : B {\n" + " D(int i, const std::string& s) : B(i, s) {}\n" + "};\n" + "template<> struct B::S {\n" + " int j;\n" + "};\n"; + valueOfTok(code, "B"); } void valueFlowCrash() { diff --git a/test/testvarid.cpp b/test/testvarid.cpp index 1fdd495e782..90dfeafe604 100644 --- a/test/testvarid.cpp +++ b/test/testvarid.cpp @@ -2785,6 +2785,17 @@ class TestVarID : public TestFixture { " break;\n" " }\n" "}", "test.c")); + + ASSERT_EQUALS("1: int * f ( ) {\n" // #11838 + "2: int * label@1 ; label@1 = 0 ;\n" + "3: label : ;\n" + "4: return label@1 ;\n" + "5: }\n", + tokenize("int* f() {\n" + " int* label = 0;\n" + "label:\n" + " return label;\n" + "}")); } void varid_structinit() { // #6406 @@ -3460,6 +3471,18 @@ class TestVarID : public TestFixture { "5: }\n" "6: } ;\n"; ASSERT_EQUALS(expected, tokenize(code)); + + const char code2[] = "struct S {\n" // #11411 + " std::vector v;\n" + " int i;\n" + " S(int i) : v({ 0 }), i(i) {}\n" + "};"; + const char expected2[] = "1: struct S {\n" + "2: std :: vector < int > v@1 ;\n" + "3: int i@2 ;\n" + "4: S ( int i@3 ) : v@1 ( { 0 } ) , i@2 ( i@3 ) { }\n" + "5: } ;\n"; + ASSERT_EQUALS(expected2, tokenize(code2)); } void varidclass18() { diff --git a/tools/donate-cpu-server.py b/tools/donate-cpu-server.py index 21ca925a2f0..40fc527a942 100755 --- a/tools/donate-cpu-server.py +++ b/tools/donate-cpu-server.py @@ -26,7 +26,7 @@ # Version scheme (MAJOR.MINOR.PATCH) should orientate on "Semantic Versioning" https://semver.org/ # Every change in this script should result in increasing the version number accordingly (exceptions may be cosmetic # changes) -SERVER_VERSION = "1.3.40" +SERVER_VERSION = "1.3.41" OLD_VERSION = '2.11' @@ -85,7 +85,7 @@ def overviewReport() -> str: html += 'Stale report
\n' html += 'Diff report
\n' html += 'HEAD report
\n' - html += 'HEAD (info) report
\n' + html += 'HEAD (information) report
\n' html += 'Latest results
\n' html += 'Time report (improved)
\n' html += 'Time report (regressed) - packages.txt
\n' @@ -115,6 +115,7 @@ def overviewReport() -> str: html += '
\n' html += 'Important errors:
\n' html += 'cppcheckError
\n' + html += 'internalError
\n' html += 'internalAstError
\n' html += 'syntaxError
\n' html += 'DacaWrongData
\n' @@ -624,7 +625,7 @@ def summaryReport(resultsPath: str, name: str, prefix: str, marker: str) -> str: outToday[messageId] += 1 html = '\n' - html += 'HEAD report\n' + html += '{} report\n'.format(name) html += '

HEAD report

\n' html += '

Uploaded today

' html += summaryReportFromDict(outToday, prefix, 'today') @@ -639,7 +640,7 @@ def headReport(resultsPath: str) -> str: def infoReport(resultsPath: str) -> str: - return summaryReport(resultsPath, 'HEAD (info)', 'headinfo', INFO_MARKER) + return summaryReport(resultsPath, 'HEAD (information)', 'headinfo', INFO_MARKER) def messageIdReport(resultPath: str, marker: str, messageId: str, query_params: dict) -> str: diff --git a/tools/donate_cpu_lib.py b/tools/donate_cpu_lib.py index cb6fe07909a..278118160f0 100644 --- a/tools/donate_cpu_lib.py +++ b/tools/donate_cpu_lib.py @@ -15,7 +15,7 @@ # Version scheme (MAJOR.MINOR.PATCH) should orientate on "Semantic Versioning" https://semver.org/ # Every change in this script should result in increasing the version number accordingly (exceptions may be cosmetic # changes) -CLIENT_VERSION = "1.3.45" +CLIENT_VERSION = "1.3.46" # Timeout for analysis with Cppcheck in seconds CPPCHECK_TIMEOUT = 30 * 60 @@ -399,9 +399,9 @@ def __run_command(cmd, print_cmd=True): time_start = time.time() comm = None if sys.platform == 'win32': - p = subprocess.Popen(shlex.split(cmd, comments=False, posix=False), stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + p = subprocess.Popen(shlex.split(cmd, comments=False, posix=False), stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, errors='surrogateescape') else: - p = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, preexec_fn=os.setsid) + p = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, errors='surrogateescape', preexec_fn=os.setsid) try: comm = p.communicate(timeout=CPPCHECK_TIMEOUT) return_code = p.returncode @@ -606,6 +606,25 @@ def diff_results(ver1, results1, ver2, results2): ret += ver2 + ' ' + r2[i2] + '\n' i2 += 1 + # if there are syntaxError/unknownMacro/etc then analysis stops. + # diffing normal checker warnings will not make much sense + bailout_ids = ('[syntaxError]', '[unknownMacro]') + has_bailout_id = False + for id in bailout_ids: + if (id in results1) or (id in results1): + has_bailout_id = True + if has_bailout_id: + def check_bailout(line): + for id in bailout_ids: + if line.endswith(id): + return True + return False + out = '' + for line in ret.split('\n'): + if check_bailout(line): + out += line + '\n' + ret = out + return ret diff --git a/tools/test-my-pr.py b/tools/test-my-pr.py index 1f8a76bcfda..715df3297bb 100755 --- a/tools/test-my-pr.py +++ b/tools/test-my-pr.py @@ -138,6 +138,7 @@ def format_float(a, b=1): print("No package downloaded") continue else: + print('Package: ' + package) tgz = package source_path, source_found = lib.unpack_package(work_path, tgz, c_only=args.c_only, cpp_only=args.cpp_only) diff --git a/tools/triage/CMakeLists.txt b/tools/triage/CMakeLists.txt index 73cf887ba7f..c244144f83c 100644 --- a/tools/triage/CMakeLists.txt +++ b/tools/triage/CMakeLists.txt @@ -8,10 +8,10 @@ CheckOptions: - { key: HeaderFileExtensions, value: 'x' } ") - add_compile_definitions($<$>:-DQT_NO_DEBUG>) - add_compile_definitions($<$>:-DQT_NO_DEBUG_OUTPUT>) - add_compile_definitions($<$>:-DQT_NO_WARNING_OUTPUT>) - add_compile_definitions($<$:-DQT_DEBUG>) + add_compile_definitions($<$>:QT_NO_DEBUG>) + add_compile_definitions($<$>:QT_NO_DEBUG_OUTPUT>) + add_compile_definitions($<$>:QT_NO_WARNING_OUTPUT>) + add_compile_definitions($<$:QT_DEBUG>) file(GLOB hdrs "*.h") file(GLOB srcs "*.cpp")