diff --git a/.clang-tidy b/.clang-tidy index 96c8908f0c0..7617f792e03 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -66,8 +66,7 @@ Checks: > -readability-magic-numbers, -readability-redundant-access-specifiers, -readability-suspicious-call-argument, - -readability-uppercase-literal-suffix, - -readability-use-anyofallof + -readability-uppercase-literal-suffix WarningsAsErrors: '*' HeaderFilterRegex: '(cli|gui|lib|oss-fuzz|test|triage)\/[a-z]+\.h' CheckOptions: diff --git a/.github/workflows/CI-cygwin.yml b/.github/workflows/CI-cygwin.yml index 31557ecbcc7..b9ea30470da 100644 --- a/.github/workflows/CI-cygwin.yml +++ b/.github/workflows/CI-cygwin.yml @@ -4,7 +4,14 @@ name: CI-cygwin -on: [push,pull_request] +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: permissions: contents: read diff --git a/.github/workflows/CI-mingw.yml b/.github/workflows/CI-mingw.yml index 152e84902c2..cf64096f549 100644 --- a/.github/workflows/CI-mingw.yml +++ b/.github/workflows/CI-mingw.yml @@ -4,7 +4,14 @@ name: CI-mingw -on: [push,pull_request] +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: permissions: contents: read diff --git a/.github/workflows/CI-unixish-docker.yml b/.github/workflows/CI-unixish-docker.yml index 3558e6a5721..9bfc5a017b5 100644 --- a/.github/workflows/CI-unixish-docker.yml +++ b/.github/workflows/CI-unixish-docker.yml @@ -2,7 +2,14 @@ # Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners name: CI-unixish-docker -on: [push, pull_request] +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: permissions: contents: read diff --git a/.github/workflows/CI-unixish.yml b/.github/workflows/CI-unixish.yml index 10cbf319d3d..44535cd077d 100644 --- a/.github/workflows/CI-unixish.yml +++ b/.github/workflows/CI-unixish.yml @@ -2,7 +2,14 @@ # Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners name: CI-unixish -on: [push, pull_request] +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: permissions: contents: read @@ -334,6 +341,7 @@ jobs: run: | python3 -m pip install pip --upgrade python3 -m pip install pytest + python3 -m pip install pytest-timeout - name: Build cppcheck run: | diff --git a/.github/workflows/CI-windows.yml b/.github/workflows/CI-windows.yml index 98df86f8558..a822cccc533 100644 --- a/.github/workflows/CI-windows.yml +++ b/.github/workflows/CI-windows.yml @@ -4,7 +4,14 @@ name: CI-windows -on: [push,pull_request] +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: permissions: contents: read @@ -70,6 +77,7 @@ jobs: matrix: os: [windows-2019, windows-2022] arch: [x64, x86] + config: [debug, release] fail-fast: false runs-on: ${{ matrix.os }} @@ -82,6 +90,7 @@ jobs: - uses: actions/checkout@v3 - name: Set up Python 3.11 + if: matrix.config == 'release' uses: actions/setup-python@v4 with: python-version: '3.11' @@ -124,10 +133,12 @@ jobs: CL: /MP - name: Install missing Python packages + if: matrix.config == 'release' run: | python -m pip install pip --upgrade || exit /b !errorlevel! python -m pip install pytest || exit /b !errorlevel! python -m pip install pytest-custom_exit_code || exit /b !errorlevel! + python -m pip install pytest-timeout || exit /b !errorlevel! - name: Run CMake if: false # TODO: enable @@ -139,6 +150,7 @@ jobs: cmake -S . -B build -DBUILD_TESTS=On || exit /b !errorlevel! - name: Build CLI debug configuration using MSBuild + if: matrix.config == 'debug' run: | set ARCH=${{ matrix.arch }} if "${{ matrix.arch }}" == "x86" ( @@ -148,9 +160,11 @@ jobs: msbuild -m cppcheck.sln /p:Configuration=Debug-PCRE;Platform=%ARCH% -maxcpucount || exit /b !errorlevel! - name: Run Debug test + if: matrix.config == 'debug' run: .\bin\debug\testrunner.exe || exit /b !errorlevel! - name: Build CLI release configuration using MSBuild + if: matrix.config == 'release' run: | set ARCH=${{ matrix.arch }} if "${{ matrix.arch }}" == "x86" ( @@ -160,9 +174,11 @@ jobs: msbuild -m cppcheck.sln /p:Configuration=Release-PCRE;Platform=%ARCH% -maxcpucount || exit /b !errorlevel! - name: Run Release test + if: matrix.config == 'release' run: .\bin\testrunner.exe || exit /b !errorlevel! - name: Run test/cli + if: matrix.config == 'release' run: | :: since FILESDIR is not set copy the binary to the root so the addons are found :: copy .\build\bin\Release\cppcheck.exe .\cppcheck.exe || exit /b !errorlevel! @@ -178,6 +194,7 @@ jobs: python -m pytest test-suppress-syntaxError.py || exit /b !errorlevel! - name: Test addons + if: matrix.config == 'release' run: | .\cppcheck --addon=threadsafety addons\test\threadsafety || exit /b !errorlevel! .\cppcheck --addon=threadsafety --std=c++03 addons\test\threadsafety || exit /b !errorlevel! diff --git a/.github/workflows/asan.yml b/.github/workflows/asan.yml index c29ecde8a4a..89dc81d7cfe 100644 --- a/.github/workflows/asan.yml +++ b/.github/workflows/asan.yml @@ -2,7 +2,14 @@ # Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners name: address sanitizer -on: [push, pull_request] +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: permissions: contents: read diff --git a/.github/workflows/buildman.yml b/.github/workflows/buildman.yml index 4bbdf779661..30cc5b16ad5 100644 --- a/.github/workflows/buildman.yml +++ b/.github/workflows/buildman.yml @@ -1,6 +1,13 @@ name: Build manual -on: [push, pull_request] +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: permissions: contents: read diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml index 7bcd8bd8e31..3eda1954994 100644 --- a/.github/workflows/clang-tidy.yml +++ b/.github/workflows/clang-tidy.yml @@ -2,7 +2,14 @@ # Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners name: clang-tidy -on: [push, pull_request] +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: permissions: contents: read diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 12d15855527..f486cf9e24f 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,6 +1,13 @@ name: "CodeQL" -on: [push, pull_request] +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: permissions: contents: read diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index b07c15811cd..5018f2c3b8a 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -2,7 +2,14 @@ # Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners name: Coverage -on: [push, pull_request] +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: permissions: contents: read diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index ff3134b7ed5..3ae9ceb200f 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -2,7 +2,14 @@ # Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners name: format -on: [push, pull_request] +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: permissions: contents: read diff --git a/.github/workflows/scriptcheck.yml b/.github/workflows/scriptcheck.yml index 6ef6d90ee4c..d6223caafa0 100644 --- a/.github/workflows/scriptcheck.yml +++ b/.github/workflows/scriptcheck.yml @@ -2,7 +2,14 @@ # Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners name: scriptcheck -on: [push, pull_request] +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: permissions: contents: read diff --git a/.github/workflows/selfcheck.yml b/.github/workflows/selfcheck.yml index 8cd028f9af9..70cc147c40f 100644 --- a/.github/workflows/selfcheck.yml +++ b/.github/workflows/selfcheck.yml @@ -2,7 +2,14 @@ # Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners name: selfcheck -on: [push, pull_request] +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: permissions: contents: read diff --git a/.github/workflows/tsan.yml b/.github/workflows/tsan.yml index 876e1976141..850453aa105 100644 --- a/.github/workflows/tsan.yml +++ b/.github/workflows/tsan.yml @@ -2,7 +2,14 @@ # Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners name: thread sanitizer -on: [push, pull_request] +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: permissions: contents: read diff --git a/.github/workflows/ubsan.yml b/.github/workflows/ubsan.yml index 2a422710b74..2fc169dcc23 100644 --- a/.github/workflows/ubsan.yml +++ b/.github/workflows/ubsan.yml @@ -2,7 +2,14 @@ # Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners name: undefined behaviour sanitizers -on: [push, pull_request] +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: permissions: contents: read diff --git a/.github/workflows/valgrind.yml b/.github/workflows/valgrind.yml index 1753e6f0acd..cf03ff89fa3 100644 --- a/.github/workflows/valgrind.yml +++ b/.github/workflows/valgrind.yml @@ -2,7 +2,14 @@ # Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners name: valgrind -on: [push, pull_request] +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: permissions: contents: read diff --git a/AUTHORS b/AUTHORS index 73ff5644dd9..da05d9d1764 100644 --- a/AUTHORS +++ b/AUTHORS @@ -356,6 +356,7 @@ Thomas Otto Thomas P. K. Healy Thomas Sondergaard Thorsten Sick +Tim Blume Tim Gerundt tititiou36 Tobias Weibel diff --git a/Makefile b/Makefile index 8cc4ead63c8..d1aa9cc093c 100644 --- a/Makefile +++ b/Makefile @@ -200,6 +200,7 @@ LIBOBJ = $(libcppdir)/analyzerinfo.o \ $(libcppdir)/checkbufferoverrun.o \ $(libcppdir)/checkclass.o \ $(libcppdir)/checkcondition.o \ + $(libcppdir)/checkersreport.o \ $(libcppdir)/checkexceptionsafety.o \ $(libcppdir)/checkfunctions.o \ $(libcppdir)/checkinternal.o \ @@ -488,6 +489,9 @@ $(libcppdir)/checkclass.o: lib/checkclass.cpp externals/tinyxml2/tinyxml2.h lib/ $(libcppdir)/checkcondition.o: lib/checkcondition.cpp lib/astutils.h lib/check.h lib/checkcondition.h lib/checkother.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkcondition.cpp +$(libcppdir)/checkersreport.o: lib/checkersreport.cpp lib/checkers.h lib/checkersreport.h lib/config.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_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkersreport.cpp + $(libcppdir)/checkexceptionsafety.o: lib/checkexceptionsafety.cpp lib/check.h lib/checkexceptionsafety.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkexceptionsafety.cpp @@ -638,7 +642,7 @@ $(libcppdir)/vfvalue.o: lib/vfvalue.cpp lib/config.h lib/errortypes.h lib/mathli cli/cmdlineparser.o: cli/cmdlineparser.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/filelister.h 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/timer.h lib/utils.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/cmdlineparser.cpp -cli/cppcheckexecutor.o: cli/cppcheckexecutor.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/cppcheckexecutorseh.h cli/cppcheckexecutorsig.h cli/executor.h cli/filelister.h cli/processexecutor.h cli/singleexecutor.h cli/threadexecutor.h lib/analyzerinfo.h lib/check.h lib/checkers.h lib/checkunusedfunctions.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/path.h lib/pathmatch.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h +cli/cppcheckexecutor.o: cli/cppcheckexecutor.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/cppcheckexecutorseh.h cli/cppcheckexecutorsig.h cli/executor.h cli/filelister.h cli/processexecutor.h cli/singleexecutor.h cli/threadexecutor.h lib/analyzerinfo.h lib/check.h lib/checkers.h lib/checkersreport.h lib/checkunusedfunctions.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/path.h lib/pathmatch.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/cppcheckexecutor.cpp cli/cppcheckexecutorseh.o: cli/cppcheckexecutorseh.cpp cli/cppcheckexecutor.h cli/cppcheckexecutorseh.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/utils.h diff --git a/addons/misra.py b/addons/misra.py index 9c22efe218f..55de43d4887 100755 --- a/addons/misra.py +++ b/addons/misra.py @@ -1457,6 +1457,15 @@ def _save_ctu_summary_usage(self, dumpfile, cfg): cppcheckdata.reportSummary(dumpfile, 'MisraUsage', names) + def misra_1_2(self, cfg): + # gcc language extensions: https://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html + for token in cfg.tokenlist: + if simpleMatch(token, '? :'): + self.reportError(token, 1, 2) + elif simpleMatch(token, '( {') and simpleMatch(token.next.link.previous, '; } )'): + self.reportError(token, 1, 2) + + def misra_1_4(self, cfg): for token in cfg.tokenlist: if token.str in ('_Atomic', '_Noreturn', '_Generic', '_Thread_local', '_Alignas', '_Alignof'): @@ -4311,6 +4320,7 @@ def fillVerifyExpected(verify_expected, tok): if not self.settings.quiet: self.printStatus('Checking %s, config %s...' % (dumpfile, cfg.name)) + self.executeCheck(102, self.misra_1_2, cfg) if not path_premium_addon: self.executeCheck(104, self.misra_1_4, cfg) self.executeCheck(202, self.misra_2_2, cfg) diff --git a/addons/misra_9.py b/addons/misra_9.py index f0f73371906..89002f30dfe 100644 --- a/addons/misra_9.py +++ b/addons/misra_9.py @@ -293,7 +293,7 @@ def parseInitializer(self, root, token): # Fake dummy as nextChild (of current root) nextChild = dummyRoot - if self.token.astOperand1: + if nextChild and self.token.astOperand1: self.root = nextChild self.token = self.token.astOperand1 isFirstElement = True @@ -328,7 +328,7 @@ def parseInitializer(self, root, token): self.ed.parent.initializeChildren() else: - if self.ed.parent != self.root: + if self.root is not None and self.ed.parent != self.root: # Check if token is correct value type for self.root.children[?] child = self.root.getChildByValueElement(self.ed) if self.token.valueType: @@ -458,7 +458,6 @@ def misra_9_x(self, data, rule, rawTokens = None): # without it. if ed.valueType is None and not variable.isArray: continue - parser.parseInitializer(ed, eq.astOperand2) # print(rule, nameToken.str + '=', ed.getInitDump()) if rule == 902 and not ed.isMisra92Compliant(): diff --git a/addons/test/misra/crash_misra9_parseInitializer.c b/addons/test/misra/crash_misra9_parseInitializer.c new file mode 100644 index 00000000000..8e3dcfd5757 --- /dev/null +++ b/addons/test/misra/crash_misra9_parseInitializer.c @@ -0,0 +1,18 @@ +union { + struct { + uint8_t a; + uint8_t b; + } a; +} bar; + +struct foo { + uint8_t a; + union bar w; + uint8_t b; +}; + +struct foo asdf = { + 0, + {{0,0}}, + 1 +}; diff --git a/addons/test/misra/misra-test.c b/addons/test/misra/misra-test.c index 29fadd938d6..2d1a523ffc4 100644 --- a/addons/test/misra/misra-test.c +++ b/addons/test/misra/misra-test.c @@ -58,6 +58,12 @@ typedef unsigned int u32; typedef signed int s32; typedef unsigned long long u64; +static void misra_1_2(void) +{ + (void)(condition ? : 0); // 1.2 + a = 1 + ({if (!expr) {code;} 1;}); // 1.2 +} + static _Atomic int misra_1_4_var; // 1.4 static _Noreturn void misra_1_4_func(void) // 1.4 { diff --git a/addons/test/test-misra.py b/addons/test/test-misra.py index ba1dbaae74a..3701dc2f460 100644 --- a/addons/test/test-misra.py +++ b/addons/test/test-misra.py @@ -120,7 +120,6 @@ def test_rules_suppression(checker, capsys): assert found is None, 'Unexptected output:\n' + captured dump_remove(src) - def test_arguments_regression(): args_ok = ["-generate-table", "--rule-texts=./addons/test/assets/misra_rules_multiple_lines.txt", diff --git a/clang-tidy.md b/clang-tidy.md index b85031ac25c..ba7c7d6113d 100644 --- a/clang-tidy.md +++ b/clang-tidy.md @@ -87,9 +87,6 @@ We intentionally use this. Leads to lots of "false positives". This seem to enforce a coding guidelines of certain codebases. -`readability-use-anyofallof`
- -We currently don't even apply our own `useStlAlgorithm` findings. `bugprone-easily-swappable-parameters`
@@ -146,6 +143,15 @@ To be evaluated (need to remove exclusion). `cppcoreguidelines-missing-std-forward`
`cppcoreguidelines-avoid-const-or-ref-data-members`
+`cppcoreguidelines-macro-usage`
+`cppcoreguidelines-pro-type-member-init`
+`cppcoreguidelines-pro-type-static-cast-downcast`
+`cppcoreguidelines-prefer-member-initializer`
+`cppcoreguidelines-misleading-capture-default-by-value`
+`bugprone-argument-comment.CommentBoolLiterals`
+`cert-err33-c`
+`google-readability-namespace-comments`
+`cppcoreguidelines-special-member-functions`
To be evaluated (need to enable explicitly). diff --git a/cli/cli.vcxproj.filters b/cli/cli.vcxproj.filters index e8385a6c84d..2b107b060f4 100644 --- a/cli/cli.vcxproj.filters +++ b/cli/cli.vcxproj.filters @@ -14,9 +14,6 @@ - - Header Files - Header Files @@ -44,6 +41,9 @@ Header Files + + Header Files + @@ -76,6 +76,9 @@ Source Files + + Source Files + diff --git a/cli/cppcheckexecutor.cpp b/cli/cppcheckexecutor.cpp index 041c051dc76..747d9cb50d5 100644 --- a/cli/cppcheckexecutor.cpp +++ b/cli/cppcheckexecutor.cpp @@ -20,6 +20,7 @@ #include "analyzerinfo.h" #include "checkers.h" +#include "checkersreport.h" #include "cmdlineparser.h" #include "color.h" #include "config.h" @@ -337,60 +338,13 @@ int CppCheckExecutor::check_internal(CppCheck& cppcheck) return 0; } -static bool isCppcheckPremium(const Settings& settings) { - return (settings.cppcheckCfgProductName.compare(0, 16, "Cppcheck Premium") == 0); -} - -static std::string getMisraRuleSeverity(const std::string& rule) { - if (checkers::misraRuleSeverity.count(rule) > 0) - return checkers::misraRuleSeverity.at(rule); - return "style"; -} - -static bool isMisraRuleInconclusive(const std::string& rule) { - return rule == "8.3"; -} - -static bool isMisraRuleActive(const std::string& rule, int amendment, const std::string& severity, const Settings& settings) { - if (!isCppcheckPremium(settings) && amendment >= 3) - return false; - const bool inconclusive = isMisraRuleInconclusive(rule); - if (inconclusive && !settings.certainty.isEnabled(Certainty::inconclusive)) - return false; - if (severity == "warning") - return settings.severity.isEnabled(Severity::warning); - if (severity == "style") - return settings.severity.isEnabled(Severity::style); - return true; // error severity -} - void CppCheckExecutor::writeCheckersReport(const Settings& settings) const { + CheckersReport checkersReport(settings, mActiveCheckers); + if (!settings.quiet) { - int activeCheckers = 0; - int totalCheckers = 0; - for (const auto& checkReq: checkers::allCheckers) { - if (mActiveCheckers.count(checkReq.first) > 0) - ++activeCheckers; - ++totalCheckers; - } - if (isCppcheckPremium(settings)) { - for (const auto& checkReq: checkers::premiumCheckers) { - if (mActiveCheckers.count(checkReq.first) > 0) - ++activeCheckers; - ++totalCheckers; - } - } - if (mSettings->premiumArgs.find("misra-c-") != std::string::npos || mSettings->addons.count("misra")) { - for (const checkers::MisraInfo& info: checkers::misraC2012Rules) { - const std::string rule = std::to_string(info.a) + "." + std::to_string(info.b); - const std::string severity = getMisraRuleSeverity(rule); - const bool active = isMisraRuleActive(rule, info.amendment, severity, settings); - if (active) - ++activeCheckers; - ++totalCheckers; - } - } + const int activeCheckers = checkersReport.getActiveCheckersCount(); + const int totalCheckers = checkersReport.getAllCheckersCount(); const std::string extra = settings.verbose ? " (use --checkers-report= to see details)" : ""; if (mCriticalErrors.empty()) @@ -403,111 +357,9 @@ void CppCheckExecutor::writeCheckersReport(const Settings& settings) const return; std::ofstream fout(settings.checkersReportFilename); - if (!fout.is_open()) - return; - - fout << "Critical errors" << std::endl; - fout << "---------------" << std::endl; - if (!mCriticalErrors.empty()) { - fout << "There was critical errors (" << mCriticalErrors << ")" << std::endl; - fout << "All checking is skipped for a file with such error" << std::endl; - } else { - fout << "No critical errors, all files were checked." << std::endl; - fout << "Important: Analysis is still not guaranteed to be 'complete' it is possible there are false negatives." << std::endl; - } - - fout << std::endl << std::endl; - fout << "Open source checkers" << std::endl; - fout << "--------------------" << std::endl; + if (fout.is_open()) + fout << checkersReport.getReport(mCriticalErrors); - int maxCheckerSize = 0; - for (const auto& checkReq: checkers::allCheckers) { - const std::string& checker = checkReq.first; - if (checker.size() > maxCheckerSize) - maxCheckerSize = checker.size(); - } - for (const auto& checkReq: checkers::allCheckers) { - const std::string& checker = checkReq.first; - const bool active = mActiveCheckers.count(checkReq.first) > 0; - const std::string& req = checkReq.second; - fout << (active ? "Yes " : "No ") << checker; - if (!active && !req.empty()) - fout << std::string(maxCheckerSize + 4 - checker.size(), ' ') << "require:" + req; - fout << std::endl; - } - - const bool cppcheckPremium = isCppcheckPremium(settings); - - if (cppcheckPremium) { - fout << std::endl << std::endl; - fout << "Premium checkers" << std::endl; - fout << "----------------" << std::endl; - - maxCheckerSize = 0; - for (const auto& checkReq: checkers::premiumCheckers) { - const std::string& checker = checkReq.first; - if (checker.size() > maxCheckerSize) - maxCheckerSize = checker.size(); - } - for (const auto& checkReq: checkers::premiumCheckers) { - const std::string& checker = checkReq.first; - std::string req = checkReq.second; - bool active = cppcheckPremium; - if (req == "warning") - active &= mSettings->severity.isEnabled(Severity::warning); - else if (req == "style") - active &= mSettings->severity.isEnabled(Severity::style); - fout << (active ? "Yes " : "No ") << checker; - if (!req.empty()) - req = "premium," + req; - else - req = "premium"; - if (!active) - fout << std::string(maxCheckerSize + 4 - checker.size(), ' ') << "require:" + req; - fout << std::endl; - } - } - - int misra = 0; - if (mSettings->premiumArgs.find("misra-c-2012") != std::string::npos) - misra = 2012; - else if (mSettings->premiumArgs.find("misra-c-2023") != std::string::npos) - misra = 2023; - else if (mSettings->addons.count("misra")) - misra = 2012; - - if (misra == 0) { - fout << std::endl << std::endl; - fout << "Misra C" << std::endl; - fout << "-------" << std::endl; - fout << "Misra is not enabled" << std::endl; - } else { - fout << std::endl << std::endl; - fout << "Misra C " << misra << std::endl; - fout << "------------" << std::endl; - for (const checkers::MisraInfo& info: checkers::misraC2012Rules) { - const std::string rule = std::to_string(info.a) + "." + std::to_string(info.b); - const std::string severity = getMisraRuleSeverity(rule); - const bool active = isMisraRuleActive(rule, info.amendment, severity, settings); - const bool inconclusive = isMisraRuleInconclusive(rule); - fout << (active ? "Yes " : "No ") << rule; - std::string extra; - if (misra == 2012 && info.amendment >= 1) - extra = " amendment:" + std::to_string(info.amendment); - std::string reqs; - if (info.amendment >= 3) - reqs += ",premium"; - if (severity != "error") - reqs += "," + severity; - if (inconclusive) - reqs += ",inconclusive"; - if (!active && !reqs.empty()) - extra += " require:" + reqs.substr(1); - if (!extra.empty()) - fout << std::string(7 - rule.size(), ' ') << extra; - fout << '\n'; - } - } } bool CppCheckExecutor::loadLibraries(Settings& settings) @@ -607,7 +459,7 @@ void CppCheckExecutor::reportErr(const ErrorMessage &msg) { assert(mSettings != nullptr); - if (msg.severity == Severity::none && msg.id == "logChecker") { + if (msg.severity == Severity::none && (msg.id == "logChecker" || endsWith(msg.id, "-logChecker"))) { const std::string& checker = msg.shortMessage(); mActiveCheckers.emplace(checker); return; @@ -692,6 +544,12 @@ bool CppCheckExecutor::executeCommand(std::string exe, std::vector { output_.clear(); +#ifdef _WIN32 + // Extra quoutes are needed in windows if filename has space + if (exe.find(" ") != std::string::npos) + exe = "\"" + exe + "\""; +#endif + std::string joinedArgs; for (const std::string &arg : args) { if (!joinedArgs.empty()) @@ -702,21 +560,34 @@ bool CppCheckExecutor::executeCommand(std::string exe, std::vector joinedArgs += arg; } -#ifdef _WIN32 - // Extra quoutes are needed in windows if filename has space - if (exe.find(" ") != std::string::npos) - exe = "\"" + exe + "\""; const std::string cmd = exe + " " + joinedArgs + " " + redirect; - std::unique_ptr pipe(_popen(cmd.c_str(), "r"), _pclose); + +#ifdef _WIN32 + FILE* p = _popen(cmd.c_str(), "r"); #else - const std::string cmd = exe + " " + joinedArgs + " " + redirect; - std::unique_ptr pipe(popen(cmd.c_str(), "r"), pclose); + FILE *p = popen(cmd.c_str(), "r"); #endif - if (!pipe) + if (!p) { + // TODO: read errno return false; + } char buffer[1024]; - while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr) + while (fgets(buffer, sizeof(buffer), p) != nullptr) output_ += buffer; + +#ifdef _WIN32 + const int res = _pclose(p); +#else + const int res = pclose(p); +#endif + if (res == -1) { // error occured + // TODO: read errno + return false; + } + if (res != 0) { // process failed + // TODO: need to get error details + return false; + } return true; } diff --git a/createrelease b/createrelease index 0a98f850266..1373f44aee6 100755 --- a/createrelease +++ b/createrelease @@ -45,6 +45,7 @@ # # Update the Makefile: # make dmake && ./dmake --release +# Uppdatera CI så att dmake körs med --release # git commit -a -m "2.8: Updated Makefile" # # Tag: diff --git a/gui/checkstatistics.cpp b/gui/checkstatistics.cpp index a8f75c9a8f1..4f812b8ee43 100644 --- a/gui/checkstatistics.cpp +++ b/gui/checkstatistics.cpp @@ -65,6 +65,11 @@ void CheckStatistics::addItem(const QString &tool, ShowTypes::ShowType type) } } +void CheckStatistics::addChecker(const QString &checker) +{ + mActiveCheckers.insert(checker.toStdString()); +} + void CheckStatistics::clear() { mStyle.clear(); @@ -73,6 +78,8 @@ void CheckStatistics::clear() mPortability.clear(); mInformation.clear(); mError.clear(); + mActiveCheckers.clear(); + mCheckersReport.clear(); } unsigned CheckStatistics::getCount(const QString &tool, ShowTypes::ShowType type) const diff --git a/gui/checkstatistics.h b/gui/checkstatistics.h index f487c39c854..d53be128834 100644 --- a/gui/checkstatistics.h +++ b/gui/checkstatistics.h @@ -23,9 +23,13 @@ #include #include +#include #include #include +#include +#include + /// @addtogroup GUI /// @{ @@ -44,6 +48,11 @@ class CheckStatistics : public QObject { */ void addItem(const QString &tool, ShowTypes::ShowType type); + /** + * @brief Add checker to statistics + */ + void addChecker(const QString& checker); + /** * @brief Clear the statistics. * @@ -59,9 +68,24 @@ class CheckStatistics : public QObject { */ unsigned getCount(const QString &tool, ShowTypes::ShowType type) const; + std::set getActiveCheckers() const { + return mActiveCheckers; + } + + int getNumberOfActiveCheckers() const { + return mActiveCheckers.size(); + } + /** Get tools with results */ QStringList getTools() const; + void setCheckersReport(QString report) { + mCheckersReport = std::move(report); + } + QString getCheckersReport() const { + return mCheckersReport; + } + private: QMap mStyle; QMap mWarning; @@ -69,6 +93,8 @@ class CheckStatistics : public QObject { QMap mPortability; QMap mInformation; QMap mError; + std::set mActiveCheckers; + QString mCheckersReport; }; /// @} diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 5f28d8b98cf..fb3a3081f49 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -540,6 +540,7 @@ void MainWindow::doAnalyzeProject(ImportProject p, const bool checkLibrary, cons } mThread->setProject(p); mThread->check(checkSettings); + mUI->mResults->setCheckSettings(checkSettings); } void MainWindow::doAnalyzeFiles(const QStringList &files, const bool checkLibrary, const bool checkConfiguration) @@ -603,6 +604,7 @@ void MainWindow::doAnalyzeFiles(const QStringList &files, const bool checkLibrar mThread->setCheckFiles(true); mThread->check(checkSettings); + mUI->mResults->setCheckSettings(checkSettings); } void MainWindow::analyzeCode(const QString& code, const QString& filename) @@ -1208,7 +1210,9 @@ void MainWindow::reAnalyzeSelected(const QStringList& files) // considered in "Modified Files Check" performed after "Selected Files Check" // TODO: Should we store per file CheckStartTime? QDateTime saveCheckStartTime = mThread->getCheckStartTime(); - mThread->check(getCppcheckSettings()); + const Settings& checkSettings = getCppcheckSettings(); + mThread->check(checkSettings); + mUI->mResults->setCheckSettings(checkSettings); mThread->setCheckStartTime(saveCheckStartTime); } @@ -1232,7 +1236,9 @@ void MainWindow::reAnalyze(bool all) qDebug() << "Rechecking project file" << mProjectFile->getFilename(); mThread->setCheckFiles(all); - mThread->check(getCppcheckSettings()); + const Settings& checkSettings = getCppcheckSettings(); + mThread->check(checkSettings); + mUI->mResults->setCheckSettings(checkSettings); } void MainWindow::clearResults() diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 78782b22bdd..969e1c28e9d 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -193,7 +193,7 @@ public slots: /** @brief Slot for showing the library editor */ void showLibraryEditor(); -protected slots: +private slots: /** @brief Slot for checkthread's done signal */ void analysisDone(); diff --git a/gui/resultsview.cpp b/gui/resultsview.cpp index e1cea88330b..891d80a4973 100644 --- a/gui/resultsview.cpp +++ b/gui/resultsview.cpp @@ -19,6 +19,7 @@ #include "resultsview.h" #include "checkstatistics.h" +#include "checkersreport.h" #include "codeeditor.h" #include "codeeditorstyle.h" #include "common.h" @@ -105,6 +106,7 @@ void ResultsView::initialize(QSettings *settings, ApplicationList *list, ThreadH ResultsView::~ResultsView() { delete mUI; + delete mCheckSettings; } void ResultsView::clear(bool results) @@ -116,6 +118,8 @@ void ResultsView::clear(bool results) mUI->mDetails->setText(QString()); mStatistics->clear(); + delete mCheckSettings; + mCheckSettings = nullptr; //Clear the progressbar mUI->mProgress->setMaximum(PROGRESS_MAX); @@ -150,6 +154,11 @@ void ResultsView::progress(int value, const QString& description) void ResultsView::error(const ErrorItem &item) { + if (item.severity == Severity::none && (item.errorId == "logChecker" || item.errorId.endsWith("-logChecker"))) { + mStatistics->addChecker(item.message); + return; + } + handleCriticalError(item); if (mUI->mTree->addErrorItem(item)) { @@ -282,6 +291,13 @@ QString ResultsView::getCheckDirectory() return mUI->mTree->getCheckDirectory(); } +void ResultsView::setCheckSettings(const Settings &settings) +{ + delete mCheckSettings; + mCheckSettings = new Settings; + *mCheckSettings = settings; +} + void ResultsView::checkingStarted(int count) { mSuccess = true; @@ -296,6 +312,13 @@ void ResultsView::checkingFinished() mUI->mProgress->setVisible(false); mUI->mProgress->setFormat("%p%"); + { + Settings checkSettings; + const std::set activeCheckers = mStatistics->getActiveCheckers(); + CheckersReport checkersReport(mCheckSettings ? *mCheckSettings : checkSettings, activeCheckers); + mStatistics->setCheckersReport(QString::fromStdString(checkersReport.getReport(mCriticalErrors.toStdString()))); + } + // TODO: Items can be mysteriously hidden when checking is finished, this function // call should be redundant but it "unhides" the wrongly hidden items. mUI->mTree->refreshTree(); @@ -528,6 +551,11 @@ void ResultsView::stopAnalysis() void ResultsView::handleCriticalError(const ErrorItem &item) { if (ErrorLogger::isCriticalErrorId(item.errorId.toStdString())) { + if (!mCriticalErrors.contains(item.errorId)) { + if (!mCriticalErrors.isEmpty()) + mCriticalErrors += ","; + mCriticalErrors += item.errorId; + } QString msg = tr("There was a critical error with id '%1'").arg(item.errorId); if (!item.file0.isEmpty()) msg += ", " + tr("when checking %1").arg(item.file0); diff --git a/gui/resultsview.h b/gui/resultsview.h index 70c8ddfa2b9..d978afab936 100644 --- a/gui/resultsview.h +++ b/gui/resultsview.h @@ -29,6 +29,7 @@ #include class ErrorItem; +class Settings; class ApplicationList; class ThreadHandler; class QModelIndex; @@ -136,6 +137,11 @@ class ResultsView : public QWidget { QString getCheckDirectory(); + /** + * Set settings used in checking + */ + void setCheckSettings(const Settings& settings); + /** * @brief Inform the view that checking has started * @@ -368,11 +374,16 @@ public slots: CheckStatistics *mStatistics; + Settings* mCheckSettings = nullptr; + /** * Set to true when checking finish successfully. Set to false whenever analysis starts. */ bool mSuccess = false; + /** Critical error ids */ + QString mCriticalErrors; + private slots: /** * @brief Custom context menu for Analysis Log diff --git a/gui/statsdialog.cpp b/gui/statsdialog.cpp index 4f5ce9a85bf..5dfd8a589e0 100644 --- a/gui/statsdialog.cpp +++ b/gui/statsdialog.cpp @@ -75,6 +75,10 @@ StatsDialog::StatsDialog(QWidget *parent) { mUI->setupUi(this); + QFont font("courier"); + font.setStyleHint(QFont::Monospace); + mUI->mCheckersReport->setFont(font); + setWindowFlags(Qt::Window); connect(mUI->mCopyToClipboard, &QPushButton::pressed, this, &StatsDialog::copyToClipboard); @@ -366,12 +370,14 @@ void StatsDialog::copyToClipboard() void StatsDialog::setStatistics(const CheckStatistics *stats) { mStatistics = stats; - mUI->mLblErrors->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowErrors))); - mUI->mLblWarnings->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowWarnings))); - mUI->mLblStyle->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowStyle))); - mUI->mLblPortability->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowPortability))); - mUI->mLblPerformance->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowPerformance))); - mUI->mLblInformation->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowInformation))); + mUI->mLblErrors->setText(QString::number(stats->getCount(CPPCHECK,ShowTypes::ShowErrors))); + mUI->mLblWarnings->setText(QString::number(stats->getCount(CPPCHECK,ShowTypes::ShowWarnings))); + mUI->mLblStyle->setText(QString::number(stats->getCount(CPPCHECK,ShowTypes::ShowStyle))); + mUI->mLblPortability->setText(QString::number(stats->getCount(CPPCHECK,ShowTypes::ShowPortability))); + mUI->mLblPerformance->setText(QString::number(stats->getCount(CPPCHECK,ShowTypes::ShowPerformance))); + mUI->mLblInformation->setText(QString::number(stats->getCount(CPPCHECK,ShowTypes::ShowInformation))); + mUI->mLblActiveCheckers->setText(QString::number(stats->getNumberOfActiveCheckers())); + mUI->mCheckersReport->setPlainText(stats->getCheckersReport()); } #ifdef QT_CHARTS_LIB diff --git a/gui/statsdialog.ui b/gui/statsdialog.ui index 8147b8e7ec6..a749c51e676 100644 --- a/gui/statsdialog.ui +++ b/gui/statsdialog.ui @@ -6,8 +6,8 @@ 0 0 - 502 - 274 + 500 + 414 @@ -247,114 +247,150 @@ Statistics - + - - - - - Errors: - - - - - - - TextLabel - - - - + + + Errors: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + TextLabel + + - - - - - Warnings: - - - - - - - TextLabel - - - - + + + Warnings: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + TextLabel + + - - - - - Stylistic warnings: - - - - - - - TextLabel - - - - + + + Stylistic warnings: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + TextLabel + + - - - - - Portability warnings: - - - - - - - TextLabel - - - - + + + Portability warnings: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + TextLabel + + - - - - - Performance issues: - - - - - - - TextLabel - - - - + + + Performance issues: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + TextLabel + + - - - - - Information messages: - - - - - - - TextLabel - - - - + + + Information messages: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + TextLabel + + + + + + + Active checkers: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + TextLabel + + + + + + + + Checkers + + + + + + true + + + + Courier 10 Pitch + + + + true + + + true + + diff --git a/lib/astutils.cpp b/lib/astutils.cpp index 53de61dcc26..b238827dad1 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -984,6 +984,7 @@ bool isAliasOf(const Token *tok, nonneg int varid, bool* inconclusive) { if (tok->varId() == varid) return false; + // NOLINTNEXTLINE(readability-use-anyofallof) - TODO: fix this / also Cppcheck false negative for (const ValueFlow::Value &val : tok->values()) { if (!val.isLocalLifetimeValue()) continue; @@ -1000,29 +1001,43 @@ bool isAliasOf(const Token *tok, nonneg int varid, bool* inconclusive) return false; } -bool isAliasOf(const Token* tok, const Token* expr, bool* inconclusive) +bool isAliasOf(const Token* tok, const Token* expr, int* indirect, bool* inconclusive) { - const bool pointer = astIsPointer(tok); const ValueFlow::Value* value = nullptr; - const Token* r = findAstNode(expr, [&](const Token* childTok) { - for (const ValueFlow::Value& val : tok->values()) { - if (val.isImpossible()) - continue; - if (val.isLocalLifetimeValue() || (pointer && val.isSymbolicValue() && val.intvalue == 0)) { - if (findAstNode(val.tokvalue, - [&](const Token* aliasTok) { - return aliasTok->exprId() == childTok->exprId(); - })) { - if (val.isInconclusive() && inconclusive != nullptr) { - value = &val; - } else { - return true; + const Token* r = nullptr; + if (indirect) + *indirect = 1; + for (const ReferenceToken& ref : followAllReferences(tok)) { + const bool pointer = astIsPointer(ref.token); + r = findAstNode(expr, [&](const Token* childTok) { + if (childTok->exprId() == 0) + return false; + if (ref.token != tok && expr->exprId() == childTok->exprId()) { + if (indirect) + *indirect = 0; + return true; + } + for (const ValueFlow::Value& val : ref.token->values()) { + if (val.isImpossible()) + continue; + if (val.isLocalLifetimeValue() || (pointer && val.isSymbolicValue() && val.intvalue == 0)) { + if (findAstNode(val.tokvalue, + [&](const Token* aliasTok) { + return aliasTok->exprId() == childTok->exprId(); + })) { + if (val.isInconclusive() && inconclusive != nullptr) { + value = &val; + } else { + return true; + } } } } - } - return false; - }); + return false; + }); + if (r) + break; + } if (!r && value && inconclusive) *inconclusive = true; return r || value; @@ -1465,6 +1480,8 @@ bool isUsedAsBool(const Token* const tok, const Settings* settings) if (isForLoopCondition(tok)) return true; if (!Token::Match(parent, "%cop%")) { + if (parent->str() == "," && parent->isInitComma()) + return false; std::vector vtParents = getParentValueTypes(tok, settings); return std::any_of(vtParents.cbegin(), vtParents.cend(), [&](const ValueType& vt) { return vt.pointer == 0 && vt.type == ValueType::BOOL; @@ -2725,16 +2742,17 @@ static bool isExpressionChangedAt(const F& getExprTok, if (globalvar && !tok->isKeyword() && Token::Match(tok, "%name% (") && !(tok->function() && tok->function()->isAttributePure())) // TODO: Is global variable really changed by function call? return true; + int i = 1; bool aliased = false; // If we can't find the expression then assume it is an alias auto expr = getExprTok(); if (!expr) aliased = true; if (!aliased) - aliased = isAliasOf(tok, expr); + aliased = isAliasOf(tok, expr, &i); if (!aliased) return false; - if (isVariableChanged(tok, indirect + 1, settings, cpp, depth)) + if (isVariableChanged(tok, indirect + i, settings, cpp, depth)) return true; // TODO: Try to traverse the lambda function if (Token::Match(tok, "%var% (")) diff --git a/lib/astutils.h b/lib/astutils.h index fdcc713d767..95a8896a6de 100644 --- a/lib/astutils.h +++ b/lib/astutils.h @@ -356,7 +356,7 @@ bool isExpressionChangedAt(const Token* expr, /// If token is an alias if another variable bool isAliasOf(const Token *tok, nonneg int varid, bool* inconclusive = nullptr); -bool isAliasOf(const Token* tok, const Token* expr, bool* inconclusive = nullptr); +bool isAliasOf(const Token* tok, const Token* expr, int* indirect = nullptr, bool* inconclusive = nullptr); bool isAliased(const Variable *var); diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index dd15ca06641..49613c06914 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -988,6 +988,14 @@ void CheckClass::initializeVarList(const Function &func, std::listisConst() && !member->isStatic()) { assignAllVar(usage); } + + // const method, assume it assigns all mutable members + else if (member->isConst()) { + for (Usage& i: usage) { + if (i.var->isMutable()) + i.assign = true; + } + } } // not member function diff --git a/lib/checkers.h b/lib/checkers.h index 5cc61a518a3..5acdba8f4f0 100644 --- a/lib/checkers.h +++ b/lib/checkers.h @@ -206,22 +206,395 @@ namespace checkers { static std::map premiumCheckers{ - {"CheckBufferOverrun::addressOfPointerArithmetic","warning"}, - {"CheckBufferOverrun::negativeBufferSizeCheckedNonZero","warning"}, - {"CheckBufferOverrun::negativeBufferSizeCheckedNonZero","warning"}, - {"CheckHang::infiniteLoop",""}, - {"CheckHang::infiniteLoopContinue",""}, - {"CheckOther::arrayPointerComparison","style"}, - {"CheckOther::knownResult","style"}, - {"CheckOther::lossOfPrecision","style"}, - {"CheckOther::pointerCast","style"}, - {"CheckOther::reassignInLoop","style"}, - {"CheckOther::unreachableCode","style"}, - {"CheckStrictAlias::strictAliasCondition","warning"}, - {"CheckUninitVar::uninitvar",""}, - {"CheckUninitVar::uninitmember",""}, - {"CheckUnusedVar::unreadVariable","style"}, - {"CheckUnusedVar::unusedPrivateMember","style"}, + {"Autosar: A0-1-3","style"}, + {"Autosar: A0-4-2","style"}, + {"Autosar: A0-4-4","style"}, + {"Autosar: A10-1-1","style"}, + {"Autosar: A11-0-2","style"}, + {"Autosar: A11-3-1","style"}, + {"Autosar: A13-2-1","style"}, + {"Autosar: A13-2-3","style"}, + {"Autosar: A13-5-2","style"}, + {"Autosar: A13-5-5","style"}, + {"Autosar: A15-1-2","style"}, + {"Autosar: A15-3-5","style"}, + {"Autosar: A16-6-1","style"}, + {"Autosar: A16-7-1","style"}, + {"Autosar: A18-0-3","style"}, + {"Autosar: A18-1-1","style"}, + {"Autosar: A18-1-2","style"}, + {"Autosar: A18-1-3","style"}, + {"Autosar: A18-5-1","style"}, + {"Autosar: A18-9-1","style"}, + {"Autosar: A2-11-1","style"}, + {"Autosar: A2-13-1","style"}, + {"Autosar: A2-13-3","style"}, + {"Autosar: A2-13-5","style"}, + {"Autosar: A2-13-6","style"}, + {"Autosar: A2-5-2","style"}, + {"Autosar: A3-1-3","style"}, + {"Autosar: A3-1-4","style"}, + {"Autosar: A3-3-1","style"}, + {"Autosar: A4-10-1","style"}, + {"Autosar: A4-7-1","style"}, + {"Autosar: A5-0-2","style"}, + {"Autosar: A5-0-3","style"}, + {"Autosar: A5-0-4","style"}, + {"Autosar: A5-1-1","style"}, + {"Autosar: A5-1-2","style"}, + {"Autosar: A5-1-3","style"}, + {"Autosar: A5-16-1","style"}, + {"Autosar: A5-1-6","style"}, + {"Autosar: A5-1-7","style"}, + {"Autosar: A5-2-1","style"}, + {"Autosar: A5-2-4","style"}, + {"Autosar: A6-5-3","style"}, + {"Autosar: A7-1-4","style"}, + {"Autosar: A7-1-6","style"}, + {"Autosar: A7-1-7","style"}, + {"Autosar: A8-4-1","style"}, + {"Autosar: A8-5-3","style"}, + {"Autosar: A9-3-1","style"}, + {"Cert C: ARR30-C","warning"}, + {"Cert C: ARR32-C","warning"}, + {"Cert C: ARR37-C","warning"}, + {"Cert C: ARR38-C",""}, + {"Cert C: ARR39-C","warning"}, + {"Cert C: CON30-C","style"}, + {"Cert C: CON31-C","style"}, + {"Cert C: CON32-C","style"}, + {"Cert C: CON33-C","style"}, + {"Cert C: CON34-C","warning"}, + {"Cert C: CON35-C","warning"}, + {"Cert C: CON36-C","style"}, + {"Cert C: CON37-C","style"}, + {"Cert C: CON38-C","warning"}, + {"Cert C: CON39-C","warning"}, + {"Cert C: CON40-C","warning"}, + {"Cert C: CON41-C","style"}, + {"Cert C++: CON51-CPP",""}, + {"Cert C++: CON52-CPP","style"}, + {"Cert C++: CON53-CPP","style"}, + {"Cert C++: CON54-CPP","style"}, + {"Cert C++: CON55-CPP","style"}, + {"Cert C++: CON56-CPP",""}, + {"Cert C++: CTR50-CPP",""}, + {"Cert C++: CTR52-CPP",""}, + {"Cert C++: CTR53-CPP",""}, + {"Cert C++: CTR56-CPP","style"}, + {"Cert C++: CTR57-CPP",""}, + {"Cert C++: CTR58-CPP",""}, + {"Cert C: DCL31-C","style"}, + {"Cert C: DCL36-C","style"}, + {"Cert C: DCL37-C","style"}, + {"Cert C: DCL38-C","style"}, + {"Cert C: DCL39-C","style"}, + {"Cert C: DCL40-C","style"}, + {"Cert C: DCL41-C","style"}, + {"Cert C++: DCL50-CPP","style"}, + {"Cert C++: DCL51-CPP","style"}, + {"Cert C++: DCL52-CPP","style"}, + {"Cert C++: DCL53-CPP","style"}, + {"Cert C++: DCL54-CPP",""}, + {"Cert C++: DCL56-CPP",""}, + {"Cert C++: DCL58-CPP","style"}, + {"Cert C++: DCL59-CPP","style"}, + {"Cert C: ENV30-C","style"}, + {"Cert C: ENV31-C","style"}, + {"Cert C: ENV32-C","style"}, + {"Cert C: ENV33-C","style"}, + {"Cert C: ENV34-C","style"}, + {"Cert C: ERR30-C","warning"}, + {"Cert C: ERR32-C","warning"}, + {"Cert C: ERR33-C","warning"}, + {"Cert C++: ERR50-CPP",""}, + {"Cert C++: ERR51-CPP","style"}, + {"Cert C++: ERR52-CPP","style"}, + {"Cert C++: ERR53-CPP",""}, + {"Cert C++: ERR54-CPP",""}, + {"Cert C++: ERR55-CPP",""}, + {"Cert C++: ERR56-CPP",""}, + {"Cert C++: ERR58-CPP",""}, + {"Cert C++: ERR59-CPP","warning"}, + {"Cert C++: ERR60-CPP","warning"}, + {"Cert C++: ERR61-CPP","style"}, + {"Cert C++: ERR62-CPP","style"}, + {"Cert C: EXP32-C","warning"}, + {"Cert C: EXP35-C",""}, + {"Cert C: EXP36-C","style"}, + {"Cert C: EXP37-C","style"}, + {"Cert C: EXP39-C","style"}, + {"Cert C: EXP40-C","style"}, + {"Cert C: EXP42-C","style"}, + {"Cert C: EXP43-C","style"}, + {"Cert C: EXP45-C","warning"}, + {"Cert C++: EXP50-CPP",""}, + {"Cert C++: EXP51-CPP",""}, + {"Cert C++: EXP55-CPP",""}, + {"Cert C++: EXP56-CPP",""}, + {"Cert C++: EXP57-CPP","style"}, + {"Cert C++: EXP58-CPP","style"}, + {"Cert C++: EXP59-CPP",""}, + {"Cert C: FIO30-C","warning"}, + {"Cert C: FIO32-C","style"}, + {"Cert C: FIO34-C","style"}, + {"Cert C: FIO37-C",""}, + {"Cert C: FIO38-C","style"}, + {"Cert C: FIO40-C","style"}, + {"Cert C: FIO41-C","style"}, + {"Cert C: FIO44-C","warning"}, + {"Cert C: FIO45-C","warning"}, + {"Cert C++: FIO51-CPP","style"}, + {"Cert C: FLP30-C","warning"}, + {"Cert C: FLP36-C","portability"}, + {"Cert C: FLP37-C","style"}, + {"Cert C: INT30-C","warning"}, + {"Cert C: INT31-C","warning"}, + {"Cert C: INT32-C","warning"}, + {"Cert C: INT33-C","warning"}, + {"Cert C: INT34-C","warning"}, + {"Cert C: INT35-C","warning"}, + {"Cert C: INT36-C","warning"}, + {"Cert C++: INT50-CPP","style"}, + {"Cert C: MEM33-C","style"}, + {"Cert C: MEM35-C","warning"}, + {"Cert C: MEM36-C","warning"}, + {"Cert C++: MEM52-CPP",""}, + {"Cert C++: MEM53-CPP",""}, + {"Cert C++: MEM54-CPP",""}, + {"Cert C++: MEM55-CPP",""}, + {"Cert C++: MEM57-CPP","style"}, + {"Cert C: MSC30-C","style"}, + {"Cert C: MSC32-C","style"}, + {"Cert C: MSC33-C","style"}, + {"Cert C: MSC38-C","warning"}, + {"Cert C: MSC39-C","warning"}, + {"Cert C: MSC40-C","warning"}, + {"Cert C++: MSC50-CPP","style"}, + {"Cert C++: MSC51-CPP","style"}, + {"Cert C++: MSC53-CPP",""}, + {"Cert C++: MSC54-CPP","style"}, + {"Cert C++: OOP51-CPP",""}, + {"Cert C++: OOP55-CPP",""}, + {"Cert C++: OOP56-CPP",""}, + {"Cert C++: OOP57-CPP",""}, + {"Cert C++: OOP58-CPP","style"}, + {"Cert C: PRE31-C","style"}, + {"Cert C: SIG30-C","style"}, + {"Cert C: SIG31-C","warning"}, + {"Cert C: SIG34-C","style"}, + {"Cert C: SIG35-C","warning"}, + {"Cert C: STR31-C","warning"}, + {"Cert C: STR32-C","warning"}, + {"Cert C: STR34-C","warning"}, + {"Cert C: STR38-C","style"}, + {"Cert C++: STR50-CPP",""}, + {"Cert C++: STR53-CPP",""}, + {"Misra C: 10.1","style"}, + {"Misra C: 10.2","style"}, + {"Misra C: 10.3","style"}, + {"Misra C: 10.4","style"}, + {"Misra C: 10.5","style"}, + {"Misra C: 10.6","style"}, + {"Misra C: 10.7","style"}, + {"Misra C: 10.8","style"}, + {"Misra C: 11.10","style"}, + {"Misra C: 12.6",""}, + {"Misra C: 1.5","style"}, + {"Misra C: 17.10","style"}, + {"Misra C: 17.11","style"}, + {"Misra C: 17.12","style"}, + {"Misra C: 17.9","style"}, + {"Misra C: 18.10","style"}, + {"Misra C: 18.9","style"}, + {"Misra C: 21.12","style"}, + {"Misra C: 21.22","style"}, + {"Misra C: 21.23","style"}, + {"Misra C: 21.24","style"}, + {"Misra C: 21.25","warning"}, + {"Misra C: 21.26","warning"}, + {"Misra C: 22.11",""}, + {"Misra C: 22.12","style"}, + {"Misra C: 22.13","style"}, + {"Misra C: 22.14","style"}, + {"Misra C: 22.15","style"}, + {"Misra C: 22.16","warning"}, + {"Misra C: 22.17","warning"}, + {"Misra C: 22.18","warning"}, + {"Misra C: 22.19","warning"}, + {"Misra C: 22.20","style"}, + {"Misra C: 23.1","style"}, + {"Misra C: 23.2","style"}, + {"Misra C: 23.3","style"}, + {"Misra C: 23.4","style"}, + {"Misra C: 23.5","style"}, + {"Misra C: 23.6","style"}, + {"Misra C: 23.7","style"}, + {"Misra C: 23.8","style"}, + {"Misra C: 6.3","style"}, + {"Misra C: 7.5","style"}, + {"Misra C: 7.6","style"}, + {"Misra C: 8.10","style"}, + {"Misra C: 8.15","style"}, + {"Misra C: 8.16","style"}, + {"Misra C: 8.17","style"}, + {"Misra C: 9.6","style"}, + {"Misra C: 9.7",""}, + {"Misra C++: M0-1-11","style"}, + {"Misra C++: M0-1-12","style"}, + {"Misra C++: M0-1-4","style"}, + {"Misra C++: M0-1-5","style"}, + {"Misra C++: M0-1-7","style"}, + {"Misra C++: M0-1-8","style"}, + {"Misra C++: M10-1-1","style"}, + {"Misra C++: M10-1-2","style"}, + {"Misra C++: M10-1-3","style"}, + {"Misra C++: M10-2-1","style"}, + {"Misra C++: M10-3-3","style"}, + {"Misra C++: M11-0-1","style"}, + {"Misra C++: M12-8-2","style"}, + {"Misra C++: M14-5-1","warning"}, + {"Misra C++: M14-5-2","warning"}, + {"Misra C++: M14-5-3","warning"}, + {"Misra C++: M14-6-1","warning"}, + {"Misra C++: M14-7-1","style"}, + {"Misra C++: M14-7-2","style"}, + {"Misra C++: M15-0-3",""}, + {"Misra C++: M15-1-1",""}, + {"Misra C++: M15-1-2","style"}, + {"Misra C++: M15-1-3","style"}, + {"Misra C++: M15-3-2","warning"}, + {"Misra C++: M15-3-3",""}, + {"Misra C++: M15-4-1","style"}, + {"Misra C++: M16-0-1","style"}, + {"Misra C++: M16-0-2","style"}, + {"Misra C++: M16-0-3","style"}, + {"Misra C++: M16-0-4","style"}, + {"Misra C++: M16-1-1","style"}, + {"Misra C++: M16-2-1","style"}, + {"Misra C++: M16-2-2","style"}, + {"Misra C++: M16-2-3","style"}, + {"Misra C++: M16-2-4","style"}, + {"Misra C++: M16-2-5","style"}, + {"Misra C++: M16-2-6","style"}, + {"Misra C++: M16-3-1","style"}, + {"Misra C++: M16-3-2","style"}, + {"Misra C++: M17-0-1","style"}, + {"Misra C++: M17-0-2","style"}, + {"Misra C++: M17-0-3","style"}, + {"Misra C++: M17-0-5","style"}, + {"Misra C++: M18-0-1","style"}, + {"Misra C++: M18-0-2","style"}, + {"Misra C++: M18-0-3","style"}, + {"Misra C++: M18-0-4","style"}, + {"Misra C++: M18-0-5","style"}, + {"Misra C++: M18-2-1","style"}, + {"Misra C++: M18-4-1","style"}, + {"Misra C++: M18-7-1","style"}, + {"Misra C++: M19-3-1","style"}, + {"Misra C++: M2-10-1","style"}, + {"Misra C++: M2-10-3","style"}, + {"Misra C++: M2-10-4","style"}, + {"Misra C++: M2-10-5","style"}, + {"Misra C++: M2-10-6","style"}, + {"Misra C++: M2-13-4","style"}, + {"Misra C++: M2-13-5","style"}, + {"Misra C++: M27-0-1","style"}, + {"Misra C++: M2-7-1","style"}, + {"Misra C++: M2-7-2","style"}, + {"Misra C++: M2-7-3","style"}, + {"Misra C++: M3-1-1","style"}, + {"Misra C++: M3-1-2","style"}, + {"Misra C++: M3-1-3","style"}, + {"Misra C++: M3-2-1",""}, + {"Misra C++: M3-3-1","style"}, + {"Misra C++: M3-3-2","style"}, + {"Misra C++: M3-9-1","style"}, + {"Misra C++: M3-9-2","style"}, + {"Misra C++: M3-9-3","style"}, + {"Misra C++: M4-10-1","style"}, + {"Misra C++: M4-10-2","style"}, + {"Misra C++: M4-5-1","style"}, + {"Misra C++: M4-5-2","style"}, + {"Misra C++: M4-5-3","style"}, + {"Misra C++: M5-0-10","style"}, + {"Misra C++: M5-0-11","style"}, + {"Misra C++: M5-0-12","style"}, + {"Misra C++: M5-0-14","style"}, + {"Misra C++: M5-0-15","style"}, + {"Misra C++: M5-0-20","style"}, + {"Misra C++: M5-0-21","style"}, + {"Misra C++: M5-0-2","style"}, + {"Misra C++: M5-0-3","style"}, + {"Misra C++: M5-0-4","style"}, + {"Misra C++: M5-0-5","style"}, + {"Misra C++: M5-0-6","style"}, + {"Misra C++: M5-0-7","style"}, + {"Misra C++: M5-0-8","style"}, + {"Misra C++: M5-0-9","style"}, + {"Misra C++: M5-2-10","style"}, + {"Misra C++: M5-2-11","style"}, + {"Misra C++: M5-2-12","style"}, + {"Misra C++: M5-2-1","style"}, + {"Misra C++: M5-2-2","style"}, + {"Misra C++: M5-2-3","style"}, + {"Misra C++: M5-2-5","style"}, + {"Misra C++: M5-2-6","style"}, + {"Misra C++: M5-2-7","style"}, + {"Misra C++: M5-2-8","style"}, + {"Misra C++: M5-2-9","style"}, + {"Misra C++: M5-3-1","style"}, + {"Misra C++: M5-3-2","style"}, + {"Misra C++: M5-3-3","style"}, + {"Misra C++: M6-2-3","style"}, + {"Misra C++: M6-4-4","style"}, + {"Misra C++: M6-4-6","style"}, + {"Misra C++: M6-4-7","style"}, + {"Misra C++: M6-4-8","style"}, + {"Misra C++: M6-5-1","style"}, + {"Misra C++: M6-5-2","style"}, + {"Misra C++: M6-5-3","style"}, + {"Misra C++: M6-5-4","style"}, + {"Misra C++: M6-5-5","style"}, + {"Misra C++: M6-5-6","style"}, + {"Misra C++: M6-6-1","style"}, + {"Misra C++: M6-6-3","style"}, + {"Misra C++: M6-6-4","style"}, + {"Misra C++: M6-6-5","style"}, + {"Misra C++: M7-2-1","style"}, + {"Misra C++: M7-3-1","style"}, + {"Misra C++: M7-3-2","style"}, + {"Misra C++: M7-3-3","style"}, + {"Misra C++: M7-3-4","style"}, + {"Misra C++: M7-3-5","style"}, + {"Misra C++: M7-3-6","style"}, + {"Misra C++: M7-4-2","style"}, + {"Misra C++: M7-4-3","style"}, + {"Misra C++: M7-5-3","style"}, + {"Misra C++: M8-0-1","style"}, + {"Misra C++: M8-3-1","style"}, + {"Misra C++: M8-4-4","style"}, + {"Misra C++: M9-3-1","style"}, + {"Misra C++: M9-5-1","style"}, + {"Misra C++: M9-6-2","style"}, + {"Misra C++: M9-6-3","style"}, + {"Misra C++: M9-6-4","style"}, + {"PremiumCheckBufferOverrun::addressOfPointerArithmetic","warning"}, + {"PremiumCheckBufferOverrun::negativeBufferSizeCheckedNonZero","warning"}, + {"PremiumCheckBufferOverrun::negativeBufferSizeCheckedNonZero","warning"}, + {"PremiumCheckHang::infiniteLoop",""}, + {"PremiumCheckHang::infiniteLoopContinue",""}, + {"PremiumCheckOther::arrayPointerComparison","style"}, + {"PremiumCheckOther::knownResult","style"}, + {"PremiumCheckOther::lossOfPrecision","style"}, + {"PremiumCheckOther::pointerCast","style"}, + {"PremiumCheckOther::reassignInLoop","style"}, + {"PremiumCheckOther::unreachableCode","style"}, + {"PremiumCheckStrictAlias::strictAliasCondition","warning"}, + {"PremiumCheckUninitVar::uninitmember",""}, + {"PremiumCheckUninitVar::uninitvar",""}, + {"PremiumCheckUnusedVar::unreadVariable","style"}, + {"PremiumCheckUnusedVar::unusedPrivateMember","style"}, }; diff --git a/lib/checkersreport.cpp b/lib/checkersreport.cpp new file mode 100644 index 00000000000..f3649503efb --- /dev/null +++ b/lib/checkersreport.cpp @@ -0,0 +1,223 @@ +/* + * 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 "checkersreport.h" +#include "checkers.h" +#include + +static bool isCppcheckPremium(const Settings& settings) { + return (settings.cppcheckCfgProductName.compare(0, 16, "Cppcheck Premium") == 0); +} + +static std::string getMisraRuleSeverity(const std::string& rule) { + if (checkers::misraRuleSeverity.count(rule) > 0) + return checkers::misraRuleSeverity.at(rule); + return "style"; +} + +static bool isMisraRuleInconclusive(const std::string& rule) { + return rule == "8.3"; +} + +static bool isMisraRuleActive(const std::string& rule, int amendment, const std::string& severity, const Settings& settings) { + if (!isCppcheckPremium(settings) && amendment >= 3) + return false; + const bool inconclusive = isMisraRuleInconclusive(rule); + if (inconclusive && !settings.certainty.isEnabled(Certainty::inconclusive)) + return false; + if (severity == "warning") + return settings.severity.isEnabled(Severity::warning); + if (severity == "style") + return settings.severity.isEnabled(Severity::style); + return true; // error severity +} + +CheckersReport::CheckersReport(const Settings& settings, const std::set& activeCheckers) + : mSettings(settings), mActiveCheckers(activeCheckers) +{} + +int CheckersReport::getActiveCheckersCount() +{ + if (mAllCheckersCount == 0) { + countCheckers(); + } + return mActiveCheckersCount; +} + +int CheckersReport::getAllCheckersCount() +{ + if (mAllCheckersCount == 0) { + countCheckers(); + } + return mAllCheckersCount; +} + +void CheckersReport::countCheckers() +{ + mActiveCheckersCount = mAllCheckersCount = 0; + + for (const auto& checkReq: checkers::allCheckers) { + if (mActiveCheckers.count(checkReq.first) > 0) + ++mActiveCheckersCount; + ++mAllCheckersCount; + } + for (const auto& checkReq: checkers::premiumCheckers) { + if (mActiveCheckers.count(checkReq.first) > 0) + ++mActiveCheckersCount; + ++mAllCheckersCount; + } + if (mSettings.premiumArgs.find("misra-c-") != std::string::npos || mSettings.addons.count("misra")) { + for (const checkers::MisraInfo& info: checkers::misraC2012Rules) { + const std::string rule = std::to_string(info.a) + "." + std::to_string(info.b); + const std::string severity = getMisraRuleSeverity(rule); + const bool active = isMisraRuleActive(rule, info.amendment, severity, mSettings); + if (active) + ++mActiveCheckersCount; + ++mAllCheckersCount; + } + } +} + +std::string CheckersReport::getReport(const std::string& criticalErrors) const +{ + std::ostringstream fout; + + fout << "Critical errors" << std::endl; + fout << "---------------" << std::endl; + if (!criticalErrors.empty()) { + fout << "There was critical errors (" << criticalErrors << ")" << std::endl; + fout << "All checking is skipped for a file with such error" << std::endl; + } else { + fout << "No critical errors, all files were checked." << std::endl; + fout << "Important: Analysis is still not guaranteed to be 'complete' it is possible there are false negatives." << std::endl; + } + + fout << std::endl << std::endl; + fout << "Open source checkers" << std::endl; + fout << "--------------------" << std::endl; + + int maxCheckerSize = 0; + for (const auto& checkReq: checkers::allCheckers) { + const std::string& checker = checkReq.first; + if (checker.size() > maxCheckerSize) + maxCheckerSize = checker.size(); + } + for (const auto& checkReq: checkers::allCheckers) { + const std::string& checker = checkReq.first; + const bool active = mActiveCheckers.count(checkReq.first) > 0; + const std::string& req = checkReq.second; + fout << (active ? "Yes " : "No ") << checker; + if (!active && !req.empty()) + fout << std::string(maxCheckerSize + 4 - checker.size(), ' ') << "require:" + req; + fout << std::endl; + } + + const bool cppcheckPremium = isCppcheckPremium(mSettings); + + auto reportSection = [&fout, cppcheckPremium] + (const std::string& title, + const Settings& settings, + const std::set& activeCheckers, + const std::map& premiumCheckers, + const std::string& substring) { + fout << std::endl << std::endl; + fout << title << std::endl; + fout << std::string(title.size(), '-') << std::endl; + if (!cppcheckPremium) { + fout << "Not available, Cppcheck Premium is not used" << std::endl; + return; + } + int maxCheckerSize = 0; + for (const auto& checkReq: premiumCheckers) { + const std::string& checker = checkReq.first; + if (checker.find(substring) != std::string::npos && checker.size() > maxCheckerSize) + maxCheckerSize = checker.size(); + } + for (const auto& checkReq: premiumCheckers) { + const std::string& checker = checkReq.first; + if (checker.find(substring) == std::string::npos) + continue; + std::string req = checkReq.second; + bool active = cppcheckPremium && activeCheckers.count(checker) > 0; + if (req == "warning") + active &= settings.severity.isEnabled(Severity::warning); + else if (req == "style") + active &= settings.severity.isEnabled(Severity::style); + else if (!req.empty()) + active = false; // FIXME: handle req + fout << (active ? "Yes " : "No ") << checker; + if (!req.empty()) + req = "premium," + req; + else + req = "premium"; + if (!active) + fout << std::string(maxCheckerSize + 4 - checker.size(), ' ') << "require:" + req; + fout << std::endl; + } + }; + + reportSection("Premium checkers", mSettings, mActiveCheckers, checkers::premiumCheckers, "::"); + reportSection("Autosar", mSettings, mActiveCheckers, checkers::premiumCheckers, "Autosar: "); + reportSection("Cert C", mSettings, mActiveCheckers, checkers::premiumCheckers, "Cert C: "); + reportSection("Cert C++", mSettings, mActiveCheckers, checkers::premiumCheckers, "Cert C++: "); + + int misra = 0; + if (mSettings.premiumArgs.find("misra-c-2012") != std::string::npos) + misra = 2012; + else if (mSettings.premiumArgs.find("misra-c-2023") != std::string::npos) + misra = 2023; + else if (mSettings.addons.count("misra")) + misra = 2012; + + if (misra == 0) { + fout << std::endl << std::endl; + fout << "Misra C" << std::endl; + fout << "-------" << std::endl; + fout << "Misra is not enabled" << std::endl; + } else { + fout << std::endl << std::endl; + fout << "Misra C " << misra << std::endl; + fout << "------------" << std::endl; + for (const checkers::MisraInfo& info: checkers::misraC2012Rules) { + const std::string rule = std::to_string(info.a) + "." + std::to_string(info.b); + const std::string severity = getMisraRuleSeverity(rule); + const bool active = isMisraRuleActive(rule, info.amendment, severity, mSettings); + const bool inconclusive = isMisraRuleInconclusive(rule); + fout << (active ? "Yes " : "No ") << rule; + std::string extra; + if (misra == 2012 && info.amendment >= 1) + extra = " amendment:" + std::to_string(info.amendment); + std::string reqs; + if (info.amendment >= 3) + reqs += ",premium"; + if (severity != "error") + reqs += "," + severity; + if (inconclusive) + reqs += ",inconclusive"; + if (!active && !reqs.empty()) + extra += " require:" + reqs.substr(1); + if (!extra.empty()) + fout << std::string(7 - rule.size(), ' ') << extra; + fout << '\n'; + } + } + + reportSection("Misra C++ 2008", mSettings, mActiveCheckers, checkers::premiumCheckers, "Misra C++: "); + + return fout.str(); +} diff --git a/lib/checkersreport.h b/lib/checkersreport.h new file mode 100644 index 00000000000..b6acab8800b --- /dev/null +++ b/lib/checkersreport.h @@ -0,0 +1,44 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "settings.h" +#include +#include + +class CPPCHECKLIB CheckersReport { +public: + CheckersReport(const Settings& settings, const std::set& activeCheckers); + + int getActiveCheckersCount(); + int getAllCheckersCount(); + + std::string getReport(const std::string& criticalErrors) const; + +private: + const Settings& mSettings; + const std::set& mActiveCheckers; + + void countCheckers(); + + int mActiveCheckersCount = 0; + int mAllCheckersCount = 0; +}; + + diff --git a/lib/checkother.cpp b/lib/checkother.cpp index e75688e9542..13ae85df44d 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -3704,8 +3704,7 @@ static bool isVariableExpression(const Token* tok) return isVariableExpression(tok->astOperand1()) && isVariableExpression(tok->astOperand2()); if (Token::simpleMatch(tok, "[")) - return isVariableExpression(tok->astOperand1()) && - tok->astOperand2() && tok->astOperand2()->hasKnownIntValue(); + return isVariableExpression(tok->astOperand1()); return false; } @@ -3764,11 +3763,17 @@ void CheckOther::checkKnownArgument() mTokenizer->isCPP(), true, tok->astOperand1(), tok->astOperand2(), mSettings->library, true, true)) continue; // ensure that there is a integer variable in expression with unknown value - const Token* vartok = findAstNode(tok, [](const Token* child) { + const Token* vartok = nullptr; + visitAstNodes(tok, [&](const Token* child) { if (Token::Match(child, "%var%|.|[")) { - return astIsIntegral(child, false) && !astIsPointer(child) && child->values().empty(); + if (child->hasKnownIntValue()) + return ChildrenToVisit::none; + if (astIsIntegral(child, false) && !astIsPointer(child) && child->values().empty()) { + vartok = child; + return ChildrenToVisit::done; + } } - return false; + return ChildrenToVisit::op1_and_op2; }); if (!vartok) continue; diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index fe9b6005034..481617d3489 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -315,8 +315,8 @@ static void createDumpFile(const Settings& settings, break; } - fdump << "" << std::endl; - fdump << "" << std::endl; + fdump << "\n"; + fdump << "\n"; fdump << " \n"; + << "/>" << '\n'; } static std::string executeAddon(const AddonInfo &addonInfo, @@ -352,6 +352,14 @@ static std::string executeAddon(const AddonInfo &addonInfo, #endif for (const char* py_exe : py_exes) { std::string out; +#ifdef _MSC_VER + // FIXME: hack to avoid debug assertion with _popen() in executeCommand() for non-existing commands + const std::string cmd = std::string(py_exe) + " --version >NUL"; + if (system(cmd.c_str()) != 0) { + // TODO: get more detailed error? + break; + } +#endif if (executeCommand(py_exe, split("--version"), redirect, out) && out.compare(0, 7, "Python ") == 0 && std::isdigit(out[7])) { pythonExe = py_exe; break; @@ -373,7 +381,7 @@ static std::string executeAddon(const AddonInfo &addonInfo, std::string result; if (!executeCommand(pythonExe, split(args), redirect, result)) { - std::string message("Failed to execute addon (command: '" + pythonExe + " " + args + "'). Exitcode is nonzero."); + std::string message("Failed to execute addon '" + addonInfo.name + "' (command: '" + pythonExe + " " + args + "'). Exitcode is nonzero."); if (result.size() > 2) { message = message + "\n" + message + "\nOutput:\n" + result; message.resize(message.find_last_not_of("\n\r")); @@ -571,16 +579,16 @@ unsigned int CppCheck::check(const std::string &path) std::string dumpFile; createDumpFile(mSettings, path, fdump, dumpFile); if (fdump.is_open()) { - fdump << "" << std::endl; + fdump << "\n"; for (const ErrorMessage& errmsg: compilerWarnings) fdump << " \n"; - fdump << " " << std::endl; - fdump << " " << std::endl; - fdump << " " << std::endl; - fdump << " " << std::endl; + fdump << " \n"; + fdump << " \n"; + fdump << " \n"; + fdump << " \n"; tokenizer.dump(fdump); - fdump << "" << std::endl; - fdump << "" << std::endl; + fdump << "\n"; + fdump << "\n"; fdump.close(); } @@ -588,13 +596,12 @@ unsigned int CppCheck::check(const std::string &path) executeAddons(dumpFile); } catch (const InternalError &e) { - internalError(path, e.errorMessage); - mExitCode = 1; // e.g. reflect a syntax error + internalError(path, "Processing Clang AST dump failed: " + e.errorMessage); } catch (const TerminateException &) { // Analysis is terminated return mExitCode; } catch (const std::exception &e) { - internalError(path, e.what()); + internalError(path, std::string("Processing Clang AST dump failed: ") + e.what()); } return mExitCode; @@ -975,22 +982,7 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string return mExitCode; } catch (const InternalError &e) { - std::list locationList; - if (e.token) { - locationList.emplace_back(e.token, &tokenizer.list); - } else { - locationList.emplace_back(filename, 0, 0); - if (filename != tokenizer.list.getSourceFilePath()) { - locationList.emplace_back(tokenizer.list.getSourceFilePath(), 0, 0); - } - } - ErrorMessage errmsg(std::move(locationList), - tokenizer.list.getSourceFilePath(), - Severity::error, - e.errorMessage, - e.id, - Certainty::normal); - + ErrorMessage errmsg = ErrorMessage::fromInternalError(e, &tokenizer.list, filename); reportErr(errmsg); } } @@ -1026,12 +1018,11 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string // Analysis is terminated return mExitCode; } catch (const std::runtime_error &e) { - internalError(filename, e.what()); - } catch (const std::bad_alloc &e) { - internalError(filename, e.what()); + internalError(filename, std::string("Checking file failed: ") + e.what()); + } catch (const std::bad_alloc &) { + internalError(filename, "Checking file failed: out of memory"); } catch (const InternalError &e) { - internalError(filename, e.errorMessage); - mExitCode=1; // e.g. reflect a syntax error + internalError(filename, "Checking file failed: " + e.errorMessage); } if (!mSettings.buildDir.empty()) { @@ -1050,9 +1041,10 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string return mExitCode; } +// TODO: replace with ErrorMessage::fromInternalError() void CppCheck::internalError(const std::string &filename, const std::string &msg) { - const std::string fullmsg("Bailing out from checking since there was an internal error: " + msg); + const std::string fullmsg("Bailing out from analysis: " + msg); const ErrorMessage::FileLocation loc1(filename, 0, 0); std::list callstack(1, loc1); @@ -1500,9 +1492,11 @@ void CppCheck::executeAddons(const std::vector& files) errmsg.setmsg(text); const std::string severity = obj["severity"].get(); errmsg.severity = Severity::fromString(severity); - if (errmsg.severity == Severity::SeverityType::none) - continue; - if (!mSettings.severity.isEnabled(errmsg.severity)) + if (errmsg.severity == Severity::SeverityType::none) { + if (!endsWith(errmsg.id, "-logChecker")) + continue; + } + else if (!mSettings.severity.isEnabled(errmsg.severity)) continue; errmsg.file0 = ((files.size() == 1) ? files[0] : ""); @@ -1525,8 +1519,7 @@ void CppCheck::executeAddonsWholeProgram(const std::map + @@ -122,6 +123,7 @@ + diff --git a/lib/cppcheck.vcxproj.filters b/lib/cppcheck.vcxproj.filters index 2bba20684fa..c3e5d34664f 100644 --- a/lib/cppcheck.vcxproj.filters +++ b/lib/cppcheck.vcxproj.filters @@ -194,6 +194,12 @@ Source Files + + Source Files + + + Source Files + @@ -406,6 +412,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + diff --git a/lib/errorlogger.cpp b/lib/errorlogger.cpp index 5b69bd489f6..af322cb0210 100644 --- a/lib/errorlogger.cpp +++ b/lib/errorlogger.cpp @@ -45,6 +45,7 @@ const std::set ErrorLogger::mCriticalErrorIds{ "internalAstError", "instantiationError", "internalError", + "premium-internalError", "preprocessorErrorDirective", "syntaxError", "unknownMacro" @@ -230,6 +231,32 @@ static void serializeString(std::string &oss, const std::string & str) oss += str; } +ErrorMessage ErrorMessage::fromInternalError(const InternalError &internalError, const TokenList *tokenList, const std::string &filename) +{ + if (internalError.token) + assert(tokenList != nullptr); // we need to make sure we can look up the provided token + + std::list locationList; + if (tokenList && internalError.token) { + ErrorMessage::FileLocation loc(internalError.token, tokenList); + locationList.push_back(std::move(loc)); + } else { + ErrorMessage::FileLocation loc2(filename, 0, 0); + locationList.push_back(std::move(loc2)); + if (tokenList && (filename != tokenList->getSourceFilePath())) { + ErrorMessage::FileLocation loc(tokenList->getSourceFilePath(), 0, 0); + locationList.push_back(std::move(loc)); + } + } + ErrorMessage errmsg(std::move(locationList), + tokenList ? tokenList->getSourceFilePath() : filename, + Severity::error, + internalError.errorMessage, + internalError.id, + Certainty::normal); + return errmsg; +} + std::string ErrorMessage::serialize() const { // Serialize this message into a simple string @@ -731,36 +758,36 @@ std::string ErrorMessage::FileLocation::stringify() const std::string ErrorLogger::toxml(const std::string &str) { - std::ostringstream xml; + std::string xml; for (const unsigned char c : str) { switch (c) { case '<': - xml << "<"; + xml += "<"; break; case '>': - xml << ">"; + xml += ">"; break; case '&': - xml << "&"; + xml += "&"; break; case '\"': - xml << """; + xml += """; break; case '\'': - xml << "'"; + xml += "'"; break; case '\0': - xml << "\\0"; + xml += "\\0"; break; default: if (c >= ' ' && c <= 0x7f) - xml << c; + xml += c; else - xml << 'x'; + xml += 'x'; break; } } - return xml.str(); + return xml; } std::string ErrorLogger::plistHeader(const std::string &version, const std::vector &files) diff --git a/lib/errorlogger.h b/lib/errorlogger.h index ae0466ce5b6..2d1bed3c4f5 100644 --- a/lib/errorlogger.h +++ b/lib/errorlogger.h @@ -198,6 +198,8 @@ class CPPCHECKLIB ErrorMessage { return mSymbolNames; } + static ErrorMessage fromInternalError(const InternalError &internalError, const TokenList *tokenList, const std::string &filename); + private: static std::string fixInvalidChars(const std::string& raw); diff --git a/lib/lib.pri b/lib/lib.pri index 09676f2dede..6c31cc91c34 100644 --- a/lib/lib.pri +++ b/lib/lib.pri @@ -16,6 +16,7 @@ HEADERS += $${PWD}/analyzer.h \ $${PWD}/checkbufferoverrun.h \ $${PWD}/checkclass.h \ $${PWD}/checkcondition.h \ + $${PWD}/checkersreport.h \ $${PWD}/checkexceptionsafety.h \ $${PWD}/checkfunctions.h \ $${PWD}/checkinternal.h \ @@ -85,6 +86,7 @@ SOURCES += $${PWD}/analyzerinfo.cpp \ $${PWD}/checkbufferoverrun.cpp \ $${PWD}/checkclass.cpp \ $${PWD}/checkcondition.cpp \ + $${PWD}/checkersreport.cpp \ $${PWD}/checkexceptionsafety.cpp \ $${PWD}/checkfunctions.cpp \ $${PWD}/checkinternal.cpp \ diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 01d2a94f142..9628dc0c6a0 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -3615,26 +3615,50 @@ bool Variable::arrayDimensions(const Settings* settings, bool& isContainer) return arr; } +static std::string scopeTypeToString(Scope::ScopeType type) +{ + switch (type) { + case Scope::ScopeType::eGlobal: + return "Global"; + case Scope::ScopeType::eClass: + return "Class"; + case Scope::ScopeType::eStruct: + return "Struct"; + case Scope::ScopeType::eUnion: + return "Union"; + case Scope::ScopeType::eNamespace: + return "Namespace"; + case Scope::ScopeType::eFunction: + return "Function"; + case Scope::ScopeType::eIf: + return "If"; + case Scope::ScopeType::eElse: + return "Else"; + case Scope::ScopeType::eFor: + return "For"; + case Scope::ScopeType::eWhile: + return "While"; + case Scope::ScopeType::eDo: + return "Do"; + case Scope::ScopeType::eSwitch: + return "Switch"; + case Scope::ScopeType::eTry: + return "Try"; + case Scope::ScopeType::eCatch: + return "Catch"; + case Scope::ScopeType::eUnconditional: + return "Unconditional"; + case Scope::ScopeType::eLambda: + return "Lambda"; + case Scope::ScopeType::eEnum: + return "Enum"; + } + return "Unknown"; +} + static std::ostream & operator << (std::ostream & s, Scope::ScopeType type) { - s << (type == Scope::eGlobal ? "Global" : - type == Scope::eClass ? "Class" : - type == Scope::eStruct ? "Struct" : - type == Scope::eUnion ? "Union" : - type == Scope::eNamespace ? "Namespace" : - type == Scope::eFunction ? "Function" : - type == Scope::eIf ? "If" : - type == Scope::eElse ? "Else" : - type == Scope::eFor ? "For" : - type == Scope::eWhile ? "While" : - type == Scope::eDo ? "Do" : - type == Scope::eSwitch ? "Switch" : - type == Scope::eTry ? "Try" : - type == Scope::eCatch ? "Catch" : - type == Scope::eUnconditional ? "Unconditional" : - type == Scope::eLambda ? "Lambda" : - type == Scope::eEnum ? "Enum" : - "Unknown"); + s << scopeTypeToString(type); return s; } @@ -4010,137 +4034,223 @@ void SymbolDatabase::printOut(const char *title) const void SymbolDatabase::printXml(std::ostream &out) const { - out << std::setiosflags(std::ios::boolalpha); + std::string outs; std::set variables; // Scopes.. - out << " " << std::endl; + outs += " \n"; for (std::list::const_iterator scope = scopeList.cbegin(); scope != scopeList.cend(); ++scope) { - out << " type << "\""; - if (!scope->className.empty()) - out << " className=\"" << ErrorLogger::toxml(scope->className) << "\""; - if (scope->bodyStart) - out << " bodyStart=\"" << scope->bodyStart << '\"'; - if (scope->bodyEnd) - out << " bodyEnd=\"" << scope->bodyEnd << '\"'; - if (scope->nestedIn) - out << " nestedIn=\"" << scope->nestedIn << "\""; - if (scope->function) - out << " function=\"" << scope->function << "\""; - if (scope->definedType) - out << " definedType=\"" << scope->definedType << "\""; + outs += " type); + outs += "\""; + if (!scope->className.empty()) { + outs += " className=\""; + outs += ErrorLogger::toxml(scope->className); + outs += "\""; + } + if (scope->bodyStart) { + outs += " bodyStart=\""; + outs += id_string(scope->bodyStart); + outs += '\"'; + } + if (scope->bodyEnd) { + outs += " bodyEnd=\""; + outs += id_string(scope->bodyEnd); + outs += '\"'; + } + if (scope->nestedIn) { + outs += " nestedIn=\""; + outs += id_string(scope->nestedIn); + outs += "\""; + } + if (scope->function) { + outs += " function=\""; + outs += id_string(scope->function); + outs += "\""; + } + if (scope->definedType) { + outs += " definedType=\""; + outs += id_string(scope->definedType); + outs += "\""; + } if (scope->functionList.empty() && scope->varlist.empty()) - out << "/>" << std::endl; + outs += "/>\n"; else { - out << '>' << std::endl; + outs += ">\n"; if (!scope->functionList.empty()) { - out << " " << std::endl; + outs += " \n"; for (std::list::const_iterator function = scope->functionList.cbegin(); function != scope->functionList.cend(); ++function) { - out << " token - << "\" tokenDef=\"" << function->tokenDef - << "\" name=\"" << ErrorLogger::toxml(function->name()) << '\"'; - out << " type=\"" << (function->type == Function::eConstructor? "Constructor" : - function->type == Function::eCopyConstructor ? "CopyConstructor" : - function->type == Function::eMoveConstructor ? "MoveConstructor" : - function->type == Function::eOperatorEqual ? "OperatorEqual" : - function->type == Function::eDestructor ? "Destructor" : - function->type == Function::eFunction ? "Function" : - function->type == Function::eLambda ? "Lambda" : - "Unknown") << '\"'; + outs += " token); + outs += "\" tokenDef=\""; + outs += id_string(function->tokenDef); + outs += "\" name=\""; + outs += ErrorLogger::toxml(function->name()); + outs += '\"'; + outs += " type=\""; + outs += (function->type == Function::eConstructor? "Constructor" : + function->type == Function::eCopyConstructor ? "CopyConstructor" : + function->type == Function::eMoveConstructor ? "MoveConstructor" : + function->type == Function::eOperatorEqual ? "OperatorEqual" : + function->type == Function::eDestructor ? "Destructor" : + function->type == Function::eFunction ? "Function" : + function->type == Function::eLambda ? "Lambda" : + "Unknown"); + outs += '\"'; if (function->nestedIn->definedType) { if (function->hasVirtualSpecifier()) - out << " hasVirtualSpecifier=\"true\""; + outs += " hasVirtualSpecifier=\"true\""; else if (function->isImplicitlyVirtual()) - out << " isImplicitlyVirtual=\"true\""; + outs += " isImplicitlyVirtual=\"true\""; + } + if (function->access == AccessControl::Public || function->access == AccessControl::Protected || function->access == AccessControl::Private) { + outs += " access=\""; + outs += accessControlToString(function->access); + outs +="\""; } - if (function->access == AccessControl::Public || function->access == AccessControl::Protected || function->access == AccessControl::Private) - out << " access=\"" << accessControlToString(function->access) << "\""; if (function->isInlineKeyword()) - out << " isInlineKeyword=\"true\""; + outs += " isInlineKeyword=\"true\""; if (function->isStatic()) - out << " isStatic=\"true\""; + outs += " isStatic=\"true\""; if (function->isAttributeNoreturn()) - out << " isAttributeNoreturn=\"true\""; - if (const Function* overriddenFunction = function->getOverriddenFunction()) - out << " overriddenFunction=\"" << overriddenFunction << "\""; + outs += " isAttributeNoreturn=\"true\""; + if (const Function* overriddenFunction = function->getOverriddenFunction()) { + outs += " overriddenFunction=\""; + outs += id_string(overriddenFunction); + outs += "\""; + } if (function->argCount() == 0U) - out << "/>" << std::endl; + outs += "/>\n"; else { - out << ">" << std::endl; + outs += ">\n"; for (unsigned int argnr = 0; argnr < function->argCount(); ++argnr) { const Variable *arg = function->getArgumentVar(argnr); - out << " " << std::endl; + outs += " \n"; variables.insert(arg); } - out << " " << std::endl; + outs += " \n"; } } - out << " " << std::endl; + outs += " \n"; } if (!scope->varlist.empty()) { - out << " " << std::endl; - for (std::list::const_iterator var = scope->varlist.cbegin(); var != scope->varlist.cend(); ++var) - out << " " << std::endl; - out << " " << std::endl; + outs += " \n"; + for (std::list::const_iterator var = scope->varlist.cbegin(); var != scope->varlist.cend(); ++var) { + outs += " \n"; + } + outs += " \n"; } - out << " " << std::endl; + outs += " \n"; } } - out << " " << std::endl; + outs += " \n"; if (!typeList.empty()) { - out << " \n"; + outs += " \n"; for (const Type& type:typeList) { - out << " \n"; + outs += "/>\n"; continue; } - out << ">\n"; + outs += ">\n"; for (const Type::BaseInfo& baseInfo: type.derivedFrom) { - out << " \n"; - } - out << " \n"; - } - out << " \n"; + outs += " " << std::endl; + outs += " \n"; for (const Variable *var : variables) { if (!var) continue; - out << " nameToken() << '\"'; - out << " typeStartToken=\"" << var->typeStartToken() << '\"'; - out << " typeEndToken=\"" << var->typeEndToken() << '\"'; - out << " access=\"" << accessControlToString(var->mAccess) << '\"'; - out << " scope=\"" << var->scope() << '\"'; - if (var->valueType()) - out << " constness=\"" << var->valueType()->constness << '\"'; - out << " isArray=\"" << var->isArray() << '\"'; - out << " isClass=\"" << var->isClass() << '\"'; - out << " isConst=\"" << var->isConst() << '\"'; - out << " isExtern=\"" << var->isExtern() << '\"'; - out << " isPointer=\"" << var->isPointer() << '\"'; - out << " isReference=\"" << var->isReference() << '\"'; - out << " isStatic=\"" << var->isStatic() << '\"'; - out << " isVolatile=\"" << var->isVolatile() << '\"'; - out << "/>" << std::endl; - } - out << " " << std::endl; - out << std::resetiosflags(std::ios::boolalpha); + outs += " nameToken()); + outs += '\"'; + outs += " typeStartToken=\""; + outs += id_string(var->typeStartToken()); + outs += '\"'; + outs += " typeEndToken=\""; + outs += id_string(var->typeEndToken()); + outs += '\"'; + outs += " access=\""; + outs += accessControlToString(var->mAccess); + outs += '\"'; + outs += " scope=\""; + outs += id_string(var->scope()); + outs += '\"'; + if (var->valueType()) { + outs += " constness=\""; + outs += std::to_string(var->valueType()->constness); + outs += '\"'; + } + outs += " isArray=\""; + outs += bool_to_string(var->isArray()); + outs += '\"'; + outs += " isClass=\""; + outs += bool_to_string(var->isClass()); + outs += '\"'; + outs += " isConst=\""; + outs += bool_to_string(var->isConst()); + outs += '\"'; + outs += " isExtern=\""; + outs += bool_to_string(var->isExtern()); + outs += '\"'; + outs += " isPointer=\""; + outs += bool_to_string(var->isPointer()); + outs += '\"'; + outs += " isReference=\""; + outs += bool_to_string(var->isReference()); + outs += '\"'; + outs += " isStatic=\""; + outs += bool_to_string(var->isStatic()); + outs += '\"'; + outs += " isVolatile=\""; + outs += bool_to_string(var->isVolatile()); + outs += '\"'; + outs += "/>\n"; + } + outs += " \n"; + + out << outs; } //--------------------------------------------------------------------------- @@ -5357,7 +5467,7 @@ const Function* Scope::findFunction(const Token *tok, bool requireConst) const addMatchingFunctions(this); - // check in anonumous namespaces + // check in anonymous namespaces for (const Scope *nestedScope : nestedList) { if (nestedScope->type == eNamespace && nestedScope->className.empty()) addMatchingFunctions(nestedScope); @@ -5577,6 +5687,25 @@ const Function* Scope::findFunction(const Token *tok, bool requireConst) const if (matches.size() == 1) return matches[0]; + // Prioritize matches in derived scopes + for (const auto& fb : { fallback1Func, fallback2Func }) { + const Function* ret = nullptr; + for (int i = 0; i < fb.size(); ++i) { + if (std::find(matches.cbegin(), matches.cend(), fb[i]) == matches.cend()) + continue; + if (this == fb[i]->nestedIn) { + if (!ret) + ret = fb[i]; + else { + ret = nullptr; + break; + } + } + } + if (ret) + return ret; + } + return nullptr; } @@ -7443,64 +7572,67 @@ bool ValueType::fromLibraryType(const std::string &typestr, const Settings &sett std::string ValueType::dump() const { - std::ostringstream ret; + std::string ret; switch (type) { case UNKNOWN_TYPE: return ""; case NONSTD: - ret << "valueType-type=\"nonstd\""; + ret += "valueType-type=\"nonstd\""; break; case POD: - ret << "valueType-type=\"pod\""; + ret += "valueType-type=\"pod\""; break; case RECORD: - ret << "valueType-type=\"record\""; + ret += "valueType-type=\"record\""; break; case SMART_POINTER: - ret << "valueType-type=\"smart-pointer\""; + ret += "valueType-type=\"smart-pointer\""; break; - case CONTAINER: - ret << "valueType-type=\"container\""; - ret << " valueType-containerId=\"" << container << "\""; + case CONTAINER: { + ret += "valueType-type=\"container\""; + ret += " valueType-containerId=\""; + ret += id_string(container); + ret += "\""; break; + } case ITERATOR: - ret << "valueType-type=\"iterator\""; + ret += "valueType-type=\"iterator\""; break; case VOID: - ret << "valueType-type=\"void\""; + ret += "valueType-type=\"void\""; break; case BOOL: - ret << "valueType-type=\"bool\""; + ret += "valueType-type=\"bool\""; break; case CHAR: - ret << "valueType-type=\"char\""; + ret += "valueType-type=\"char\""; break; case SHORT: - ret << "valueType-type=\"short\""; + ret += "valueType-type=\"short\""; break; case WCHAR_T: - ret << "valueType-type=\"wchar_t\""; + ret += "valueType-type=\"wchar_t\""; break; case INT: - ret << "valueType-type=\"int\""; + ret += "valueType-type=\"int\""; break; case LONG: - ret << "valueType-type=\"long\""; + ret += "valueType-type=\"long\""; break; case LONGLONG: - ret << "valueType-type=\"long long\""; + ret += "valueType-type=\"long long\""; break; case UNKNOWN_INT: - ret << "valueType-type=\"unknown int\""; + ret += "valueType-type=\"unknown int\""; break; case FLOAT: - ret << "valueType-type=\"float\""; + ret += "valueType-type=\"float\""; break; case DOUBLE: - ret << "valueType-type=\"double\""; + ret += "valueType-type=\"double\""; break; case LONGDOUBLE: - ret << "valueType-type=\"long double\""; + ret += "valueType-type=\"long double\""; break; } @@ -7508,36 +7640,51 @@ std::string ValueType::dump() const case Sign::UNKNOWN_SIGN: break; case Sign::SIGNED: - ret << " valueType-sign=\"signed\""; + ret += " valueType-sign=\"signed\""; break; case Sign::UNSIGNED: - ret << " valueType-sign=\"unsigned\""; + ret += " valueType-sign=\"unsigned\""; break; } - if (bits > 0) - ret << " valueType-bits=\"" << bits << '\"'; + if (bits > 0) { + ret += " valueType-bits=\""; + ret += std::to_string(bits); + ret += '\"'; + } - if (pointer > 0) - ret << " valueType-pointer=\"" << pointer << '\"'; + if (pointer > 0) { + ret += " valueType-pointer=\""; + ret += std::to_string(pointer); + ret += '\"'; + } - if (constness > 0) - ret << " valueType-constness=\"" << constness << '\"'; + if (constness > 0) { + ret += " valueType-constness=\""; + ret += std::to_string(constness); + ret += '\"'; + } if (reference == Reference::None) - ret << " valueType-reference=\"None\""; + ret += " valueType-reference=\"None\""; else if (reference == Reference::LValue) - ret << " valueType-reference=\"LValue\""; + ret += " valueType-reference=\"LValue\""; else if (reference == Reference::RValue) - ret << " valueType-reference=\"RValue\""; + ret += " valueType-reference=\"RValue\""; - if (typeScope) - ret << " valueType-typeScope=\"" << typeScope << '\"'; + if (typeScope) { + ret += " valueType-typeScope=\""; + ret += id_string(typeScope); + ret += '\"'; + } - if (!originalTypeName.empty()) - ret << " valueType-originalTypeName=\"" << ErrorLogger::toxml(originalTypeName) << '\"'; + if (!originalTypeName.empty()) { + ret += " valueType-originalTypeName=\""; + ret += ErrorLogger::toxml(originalTypeName); + ret += '\"'; + } - return ret.str(); + return ret; } bool ValueType::isConst(nonneg int indirect) const diff --git a/lib/token.cpp b/lib/token.cpp index a13f98763d4..0ad96387d61 100644 --- a/lib/token.cpp +++ b/lib/token.cpp @@ -1695,119 +1695,171 @@ std::string Token::astStringZ3() const void Token::printValueFlow(bool xml, std::ostream &out) const { + std::string outs; + int line = 0; if (xml) - out << " " << std::endl; + outs += " \n"; else - out << "\n\n##Value flow" << std::endl; + outs += "\n\n##Value flow\n"; for (const Token *tok = this; tok; tok = tok->next()) { const auto* const values = tok->mImpl->mValues; if (!values) continue; if (values->empty()) // Values might be removed by removeContradictions continue; - if (xml) - out << " " << std::endl; - else if (line != tok->linenr()) - out << "Line " << tok->linenr() << std::endl; + if (xml) { + outs += " "; + outs += '\n'; + } + else if (line != tok->linenr()) { + outs += "Line "; + outs += std::to_string(tok->linenr()); + outs += '\n'; + } line = tok->linenr(); if (!xml) { ValueFlow::Value::ValueKind valueKind = values->front().valueKind; const bool same = std::all_of(values->begin(), values->end(), [&](const ValueFlow::Value& value) { return value.valueKind == valueKind; }); - out << " " << tok->str() << " "; + outs += " "; + outs += tok->str(); + outs += " "; if (same) { switch (valueKind) { case ValueFlow::Value::ValueKind::Impossible: case ValueFlow::Value::ValueKind::Known: - out << "always "; + outs += "always "; break; case ValueFlow::Value::ValueKind::Inconclusive: - out << "inconclusive "; + outs += "inconclusive "; break; case ValueFlow::Value::ValueKind::Possible: - out << "possible "; + outs += "possible "; break; } } if (values->size() > 1U) - out << '{'; + outs += '{'; } for (const ValueFlow::Value& value : *values) { if (xml) { - out << " valueType() && tok->valueType()->sign == ValueType::UNSIGNED) - out << "intvalue=\"" << (MathLib::biguint)value.intvalue << '\"'; - else - out << "intvalue=\"" << value.intvalue << '\"'; + if (tok->valueType() && tok->valueType()->sign == ValueType::UNSIGNED) { + outs += "intvalue=\""; + outs += std::to_string(static_cast(value.intvalue)); + outs += '\"'; + } + else { + outs += "intvalue=\""; + outs += std::to_string(value.intvalue); + outs += '\"'; + } break; case ValueFlow::Value::ValueType::TOK: - out << "tokvalue=\"" << value.tokvalue << '\"'; + outs += "tokvalue=\""; + outs += id_string(value.tokvalue); + outs += '\"'; break; case ValueFlow::Value::ValueType::FLOAT: - out << "floatvalue=\"" << value.floatValue << '\"'; + outs += "floatvalue=\""; + outs += std::to_string(value.floatValue); // TODO: should this be MathLib::toString()? + outs += '\"'; break; case ValueFlow::Value::ValueType::MOVED: - out << "movedvalue=\"" << ValueFlow::Value::toString(value.moveKind) << '\"'; + outs += "movedvalue=\""; + outs += ValueFlow::Value::toString(value.moveKind); + outs += '\"'; break; case ValueFlow::Value::ValueType::UNINIT: - out << "uninit=\"1\""; + outs += "uninit=\"1\""; break; case ValueFlow::Value::ValueType::BUFFER_SIZE: - out << "buffer-size=\"" << value.intvalue << "\""; + outs += "buffer-size=\""; + outs += std::to_string(value.intvalue); + outs += "\""; break; case ValueFlow::Value::ValueType::CONTAINER_SIZE: - out << "container-size=\"" << value.intvalue << '\"'; + outs += "container-size=\""; + outs += std::to_string(value.intvalue); + outs += '\"'; break; case ValueFlow::Value::ValueType::ITERATOR_START: - out << "iterator-start=\"" << value.intvalue << '\"'; + outs += "iterator-start=\""; + outs += std::to_string(value.intvalue); + outs += '\"'; break; case ValueFlow::Value::ValueType::ITERATOR_END: - out << "iterator-end=\"" << value.intvalue << '\"'; + outs += "iterator-end=\""; + outs += std::to_string(value.intvalue); + outs += '\"'; break; case ValueFlow::Value::ValueType::LIFETIME: - out << "lifetime=\"" << value.tokvalue << '\"'; - out << " lifetime-scope=\"" << ValueFlow::Value::toString(value.lifetimeScope) << "\""; - out << " lifetime-kind=\"" << ValueFlow::Value::toString(value.lifetimeKind) << "\""; + outs += "lifetime=\""; + outs += id_string(value.tokvalue); + outs += '\"'; + outs += " lifetime-scope=\""; + outs += ValueFlow::Value::toString(value.lifetimeScope); + outs += "\""; + outs += " lifetime-kind=\""; + outs += ValueFlow::Value::toString(value.lifetimeKind); + outs += "\""; break; case ValueFlow::Value::ValueType::SYMBOLIC: - out << "symbolic=\"" << value.tokvalue << '\"'; - out << " symbolic-delta=\"" << value.intvalue << '\"'; + outs += "symbolic=\""; + outs += id_string(value.tokvalue); + outs += '\"'; + outs += " symbolic-delta=\""; + outs += std::to_string(value.intvalue); + outs += '\"'; break; } - out << " bound=\"" << ValueFlow::Value::toString(value.bound) << "\""; - if (value.condition) - out << " condition-line=\"" << value.condition->linenr() << '\"'; + outs += " bound=\""; + outs += ValueFlow::Value::toString(value.bound); + outs += "\""; + if (value.condition) { + outs += " condition-line=\""; + outs += std::to_string(value.condition->linenr()); + outs += '\"'; + } if (value.isKnown()) - out << " known=\"true\""; + outs += " known=\"true\""; else if (value.isPossible()) - out << " possible=\"true\""; + outs += " possible=\"true\""; else if (value.isImpossible()) - out << " impossible=\"true\""; + outs += " impossible=\"true\""; else if (value.isInconclusive()) - out << " inconclusive=\"true\""; - out << " path=\"" << value.path << "\""; - out << "/>" << std::endl; + outs += " inconclusive=\"true\""; + + outs += " path=\""; + outs += std::to_string(value.path); + outs += "\""; + + outs += "/>\n"; } else { if (&value != &values->front()) - out << ","; - out << value.toString(); + outs += ","; + outs += value.toString(); } } if (xml) - out << " " << std::endl; + outs += " \n"; else if (values->size() > 1U) - out << '}' << std::endl; + outs += "}\n"; else - out << std::endl; + outs += '\n'; } if (xml) - out << " " << std::endl; + outs += " \n"; + + out << outs; } const ValueFlow::Value * Token::getValueLE(const MathLib::bigint val, const Settings *settings) const diff --git a/lib/token.h b/lib/token.h index dfd601a7677..e50fbec9ac6 100644 --- a/lib/token.h +++ b/lib/token.h @@ -684,6 +684,13 @@ class CPPCHECKLIB Token { setFlag(fIsFinalType, b); } + bool isInitComma() const { + return getFlag(fIsInitComma); + } + void isInitComma(bool b) { + setFlag(fIsInitComma, b); + } + bool isBitfield() const { return mImpl->mBits > 0; } @@ -1307,14 +1314,14 @@ class CPPCHECKLIB Token { fIsAttributePacked = (1ULL << 15), // __attribute__((packed)) fIsAttributeExport = (1ULL << 16), // __attribute__((__visibility__("default"))), __declspec(dllexport) fIsAttributeMaybeUnused = (1ULL << 17), // [[maybe_unsed]] - fIsControlFlowKeyword = (1ULL << 18), // if/switch/while/... - fIsOperatorKeyword = (1ULL << 19), // operator=, etc - fIsComplex = (1ULL << 20), // complex/_Complex type - fIsEnumType = (1ULL << 21), // enumeration type - fIsName = (1ULL << 22), - fIsLiteral = (1ULL << 23), - fIsTemplateArg = (1ULL << 24), - fIsAttributeNodiscard = (1ULL << 25), // __attribute__ ((warn_unused_result)), [[nodiscard]] + fIsAttributeNodiscard = (1ULL << 18), // __attribute__ ((warn_unused_result)), [[nodiscard]] + fIsControlFlowKeyword = (1ULL << 19), // if/switch/while/... + fIsOperatorKeyword = (1ULL << 20), // operator=, etc + fIsComplex = (1ULL << 21), // complex/_Complex type + fIsEnumType = (1ULL << 22), // enumeration type + fIsName = (1ULL << 23), + fIsLiteral = (1ULL << 24), + fIsTemplateArg = (1ULL << 25), fAtAddress = (1ULL << 26), // @ 0x4000 fIncompleteVar = (1ULL << 27), fConstexpr = (1ULL << 28), @@ -1331,6 +1338,7 @@ class CPPCHECKLIB Token { fIsAtomic = (1ULL << 39), // Is this a _Atomic declaration fIsSimplifiedTypedef = (1ULL << 40), fIsFinalType = (1ULL << 41), // Is this a type with final specifier + fIsInitComma = (1ULL << 42), // Is this comma located inside some {..}. i.e: {1,2,3,4} }; enum : uint64_t { diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 86a843c9632..cc4f3cfa015 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -4014,8 +4014,8 @@ void Tokenizer::simplifyTemplates() /** Class used in Tokenizer::setVarIdPass1 */ class VariableMap { private: - std::map mVariableId; - std::map mVariableId_global; + std::unordered_map mVariableId; + std::unordered_map mVariableId_global; std::stack>> mScopeInfo; mutable nonneg int mVarId{}; public: @@ -4027,7 +4027,7 @@ class VariableMap { return mVariableId.find(varname) != mVariableId.end(); } - const std::map& map(bool global) const { + const std::unordered_map& map(bool global) const { return global ? mVariableId_global : mVariableId; } nonneg int getVarId() const { @@ -4067,7 +4067,7 @@ void VariableMap::addVariable(const std::string& varname, bool globalNamespace) mVariableId_global[varname] = mVariableId[varname]; return; } - std::map::iterator it = mVariableId.find(varname); + std::unordered_map::iterator it = mVariableId.find(varname); if (it == mVariableId.end()) { mScopeInfo.top().emplace_back(varname, 0); mVariableId[varname] = ++mVarId; @@ -4330,7 +4330,7 @@ void Tokenizer::setVarIdClassDeclaration(Token* const startToken, --indentlevel; inEnum = false; } else if (initList && indentlevel == 0 && Token::Match(tok->previous(), "[,:] %name% [({]")) { - const std::map::const_iterator it = variableMap.map(false).find(tok->str()); + const std::unordered_map::const_iterator it = variableMap.map(false).find(tok->str()); if (it != variableMap.map(false).end()) { tok->varId(it->second); } @@ -4348,7 +4348,7 @@ void Tokenizer::setVarIdClassDeclaration(Token* const startToken, } if (!inEnum) { - const std::map::const_iterator it = variableMap.map(false).find(tok->str()); + const std::unordered_map::const_iterator it = variableMap.map(false).find(tok->str()); if (it != variableMap.map(false).end()) { tok->varId(it->second); setVarIdStructMembers(&tok, structMembers, variableMap.getVarId()); @@ -4692,7 +4692,7 @@ void Tokenizer::setVarIdPass1() while (tok != end) { if (tok->isName() && !(Token::simpleMatch(tok->next(), "<") && Token::Match(tok->tokAt(-1), ":: %name%"))) { - const std::map::const_iterator it = variableMap.map(false).find(tok->str()); + const std::unordered_map::const_iterator it = variableMap.map(false).find(tok->str()); if (it != variableMap.map(false).end()) tok->varId(it->second); } @@ -4757,7 +4757,7 @@ void Tokenizer::setVarIdPass1() 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()); + const std::unordered_map::const_iterator it = variableMap.map(globalNamespace).find(tok->str()); if (it != variableMap.map(globalNamespace).end()) { tok->varId(it->second); setVarIdStructMembers(&tok, structMembers, variableMap.getVarId()); @@ -5797,134 +5797,223 @@ void Tokenizer::dump(std::ostream &out) const // The idea is not that this will be readable for humans. It's a // data dump that 3rd party tools could load and get useful info from. + std::string outs; + std::set containers; // tokens.. - out << " " << std::endl; + outs += " "; + outs += '\n'; for (const Token *tok = list.front(); tok; tok = tok->next()) { - out << " linenr() << "\" column=\"" << tok->column() << "\""; - out << " str=\"" << ErrorLogger::toxml(tok->str()) << '\"'; - out << " scope=\"" << tok->scope() << '\"'; + outs += " linenr()); + outs += "\" column=\""; + outs += std::to_string(tok->column()); + outs += "\""; + + outs += " str=\""; + outs += ErrorLogger::toxml(tok->str()); + outs += '\"'; + + outs += " scope=\""; + outs += id_string(tok->scope()); + outs += '\"'; if (tok->isName()) { - out << " type=\"name\""; + outs += " type=\"name\""; if (tok->isUnsigned()) - out << " isUnsigned=\"true\""; + outs += " isUnsigned=\"true\""; else if (tok->isSigned()) - out << " isSigned=\"true\""; + outs += " isSigned=\"true\""; } else if (tok->isNumber()) { - out << " type=\"number\""; + outs += " type=\"number\""; if (MathLib::isInt(tok->str())) - out << " isInt=\"true\""; + outs += " isInt=\"true\""; if (MathLib::isFloat(tok->str())) - out << " isFloat=\"true\""; - } else if (tok->tokType() == Token::eString) - out << " type=\"string\" strlen=\"" << Token::getStrLength(tok) << '\"'; + outs += " isFloat=\"true\""; + } else if (tok->tokType() == Token::eString) { + outs += " type=\"string\" strlen=\""; + outs += std::to_string(Token::getStrLength(tok)); + outs += '\"'; + } else if (tok->tokType() == Token::eChar) - out << " type=\"char\""; + outs += " type=\"char\""; else if (tok->isBoolean()) - out << " type=\"boolean\""; + outs += " type=\"boolean\""; else if (tok->isOp()) { - out << " type=\"op\""; + outs += " type=\"op\""; if (tok->isArithmeticalOp()) - out << " isArithmeticalOp=\"true\""; + outs += " isArithmeticalOp=\"true\""; else if (tok->isAssignmentOp()) - out << " isAssignmentOp=\"true\""; + outs += " isAssignmentOp=\"true\""; else if (tok->isComparisonOp()) - out << " isComparisonOp=\"true\""; + outs += " isComparisonOp=\"true\""; else if (tok->tokType() == Token::eLogicalOp) - out << " isLogicalOp=\"true\""; + outs += " isLogicalOp=\"true\""; } if (tok->isCast()) - out << " isCast=\"true\""; + outs += " isCast=\"true\""; if (tok->isExternC()) - out << " externLang=\"C\""; + outs += " externLang=\"C\""; if (tok->isExpandedMacro()) - out << " isExpandedMacro=\"true\""; + outs += " isExpandedMacro=\"true\""; if (tok->isTemplateArg()) - out << " isTemplateArg=\"true\""; + outs += " isTemplateArg=\"true\""; if (tok->isRemovedVoidParameter()) - out << " isRemovedVoidParameter=\"true\""; + outs += " isRemovedVoidParameter=\"true\""; if (tok->isSplittedVarDeclComma()) - out << " isSplittedVarDeclComma=\"true\""; + outs += " isSplittedVarDeclComma=\"true\""; if (tok->isSplittedVarDeclEq()) - out << " isSplittedVarDeclEq=\"true\""; + outs += " isSplittedVarDeclEq=\"true\""; if (tok->isImplicitInt()) - out << " isImplicitInt=\"true\""; + outs += " isImplicitInt=\"true\""; if (tok->isComplex()) - out << " isComplex=\"true\""; + outs += " isComplex=\"true\""; if (tok->isRestrict()) - out << " isRestrict=\"true\""; + outs += " isRestrict=\"true\""; if (tok->isAtomic()) - out << " isAtomic=\"true\""; + outs += " isAtomic=\"true\""; if (tok->isAttributeExport()) - out << " isAttributeExport=\"true\""; - if (tok->link()) - out << " link=\"" << tok->link() << '\"'; - if (tok->varId() > 0) - out << " varId=\"" << tok->varId() << '\"'; - if (tok->exprId() > 0) - out << " exprId=\"" << tok->exprId() << '\"'; - if (tok->variable()) - out << " variable=\"" << tok->variable() << '\"'; - if (tok->function()) - out << " function=\"" << tok->function() << '\"'; - if (!tok->values().empty()) - out << " values=\"" << &tok->values() << '\"'; - if (tok->type()) - out << " type-scope=\"" << tok->type()->classScope << '\"'; - if (tok->astParent()) - out << " astParent=\"" << tok->astParent() << '\"'; - if (tok->astOperand1()) - out << " astOperand1=\"" << tok->astOperand1() << '\"'; - if (tok->astOperand2()) - out << " astOperand2=\"" << tok->astOperand2() << '\"'; - if (!tok->originalName().empty()) - out << " originalName=\"" << tok->originalName() << '\"'; + outs += " isAttributeExport=\"true\""; + if (tok->link()) { + outs += " link=\""; + outs += id_string(tok->link()); + outs += '\"'; + } + if (tok->varId() > 0) { + outs += " varId=\""; + outs += std::to_string(tok->varId()); + outs += '\"'; + } + if (tok->exprId() > 0) { + outs += " exprId=\""; + outs += std::to_string(tok->exprId()); + outs += '\"'; + } + if (tok->variable()) { + outs += " variable=\""; + outs += id_string(tok->variable()); + outs += '\"'; + } + if (tok->function()) { + outs += " function=\""; + outs += id_string(tok->function()); + outs += '\"'; + } + if (!tok->values().empty()) { + outs += " values=\""; + outs += id_string(&tok->values()); + outs += '\"'; + } + if (tok->type()) { + outs += " type-scope=\""; + outs += id_string(tok->type()->classScope); + outs += '\"'; + } + if (tok->astParent()) { + outs += " astParent=\""; + outs += id_string(tok->astParent()); + outs += '\"'; + } + if (tok->astOperand1()) { + outs += " astOperand1=\""; + outs += id_string(tok->astOperand1()); + outs += '\"'; + } + if (tok->astOperand2()) { + outs += " astOperand2=\""; + outs += id_string(tok->astOperand2()); + outs += '\"'; + } + if (!tok->originalName().empty()) { + outs += " originalName=\""; + outs += tok->originalName(); + outs += '\"'; + } if (tok->valueType()) { const std::string vt = tok->valueType()->dump(); - if (!vt.empty()) - out << ' ' << vt; + if (!vt.empty()) { + outs += ' '; + outs += vt; + } containers.insert(tok->valueType()->container); } if (!tok->varId() && tok->scope()->isExecutable() && Token::Match(tok, "%name% (")) { if (mSettings->library.isnoreturn(tok)) - out << " noreturn=\"true\""; + outs += " noreturn=\"true\""; } - out << "/>" << std::endl; + outs += "/>"; + outs += '\n'; } - out << " " << std::endl; + outs += " "; + outs += '\n'; + + out << outs; + outs.clear(); mSymbolDatabase->printXml(out); containers.erase(nullptr); if (!containers.empty()) { - out << " \n"; + outs += " "; + outs += '\n'; for (const Library::Container* c: containers) { - out << " arrayLike_indexOp ? "true" : "false") << "\" " - << "std-string-like=\"" << (c->stdStringLike ? "true" : "false") << "\"/>\n"; + outs += " arrayLike_indexOp ? "true" : "false"); + outs += "\" "; + outs += "std-string-like=\""; + outs +=(c->stdStringLike ? "true" : "false"); + outs += "\"/>"; + outs += '\n'; } - out << " \n"; + outs += " "; + outs += '\n'; } if (list.front()) list.front()->printValueFlow(true, out); if (!mTypedefInfo.empty()) { - out << " " << std::endl; + outs += " "; + outs += '\n'; for (const TypedefInfo &typedefInfo: mTypedefInfo) { - out << " " << std::endl; + outs += " " << std::endl; + outs += " "; + outs += '\n'; } - out << mTemplateSimplifier->dump(); + outs += mTemplateSimplifier->dump(); + + out << outs; } void Tokenizer::simplifyHeadersAndUnusedTemplates() diff --git a/lib/utils.h b/lib/utils.h index e942c82f1da..46ee7d69861 100644 --- a/lib/utils.h +++ b/lib/utils.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -269,4 +270,53 @@ std::size_t getArrayLength(const T (& /*unused*/)[size]) return size; } +/** + * @brief get id string. i.e. for dump files + * it will be a hexadecimal output. + */ +static inline std::string id_string_i(std::uintptr_t l) +{ + if (!l) + return "0"; + + static constexpr int ptr_size = sizeof(void*); + + // two characters of each byte / contains terminating \0 + static constexpr int buf_size = (ptr_size * 2) + 1; + + char buf[buf_size]; + + // needs to be signed so we don't underflow in padding loop + int idx = buf_size - 1; + buf[idx] = '\0'; + + while (l != 0) + { + char c; + const uintptr_t temp = l % 16; // get the remainder + if (temp < 10) { + // 0-9 + c = '0' + temp; + } + else { + // a-f + c = 'a' + (temp - 10); + } + buf[--idx] = c; // store in reverse order + l = l / 16; + } + + return &buf[idx]; +} + +static inline std::string id_string(const void* p) +{ + return id_string_i(reinterpret_cast(p)); +} + +static inline std::string bool_to_string(bool b) +{ + return b ? "true" : "false"; +} + #endif diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index b97389fe9bb..396879b90d4 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -605,7 +605,6 @@ static ValueFlow::Value truncateImplicitConversion(Token* parent, const ValueFlo static void setTokenValue(Token* tok, ValueFlow::Value value, const Settings* settings, - bool isInitList = false, SourceLocation loc = SourceLocation::current()) { // Skip setting values that are too big since its ambiguous @@ -629,7 +628,7 @@ static void setTokenValue(Token* tok, if (!parent) return; - if (!isInitList && Token::simpleMatch(parent, ",") && astIsRHS(tok)) { + if (Token::simpleMatch(parent, ",") && !parent->isInitComma() && astIsRHS(tok)) { const Token* callParent = findParent(parent, [](const Token* p) { return !Token::simpleMatch(p, ","); }); @@ -947,7 +946,7 @@ static void setTokenValue(Token* tok, if (Token::simpleMatch(parent, "-") && value2.bound == result.bound && value2.bound != ValueFlow::Value::Bound::Point) result.invertBound(); - setTokenValue(parent, result, settings, isInitList); + setTokenValue(parent, result, settings); } } } @@ -1131,7 +1130,7 @@ size_t ValueFlow::getSizeOf(const ValueType &vt, const Settings *settings) static bool getMinMaxValues(const ValueType* vt, const cppcheck::Platform& platform, MathLib::bigint& minValue, MathLib::bigint& maxValue); // Handle various constants.. -static Token * valueFlowSetConstantValue(Token *tok, const Settings *settings, bool cpp, bool isInitList = false) +static Token * valueFlowSetConstantValue(Token *tok, const Settings *settings, bool cpp) { if ((tok->isNumber() && MathLib::isInt(tok->str())) || (tok->tokType() == Token::eChar)) { try { @@ -1145,7 +1144,7 @@ static Token * valueFlowSetConstantValue(Token *tok, const Settings *settings, b ValueFlow::Value value(signedValue); if (!tok->isTemplateArg()) value.setKnown(); - setTokenValue(tok, std::move(value), settings, isInitList); + setTokenValue(tok, std::move(value), settings); } catch (const std::exception & /*e*/) { // Bad character literal } @@ -1155,17 +1154,17 @@ static Token * valueFlowSetConstantValue(Token *tok, const Settings *settings, b value.floatValue = MathLib::toDoubleNumber(tok->str()); if (!tok->isTemplateArg()) value.setKnown(); - setTokenValue(tok, std::move(value), settings, isInitList); + setTokenValue(tok, std::move(value), settings); } else if (tok->enumerator() && tok->enumerator()->value_known) { ValueFlow::Value value(tok->enumerator()->value); if (!tok->isTemplateArg()) value.setKnown(); - setTokenValue(tok, std::move(value), settings, isInitList); + setTokenValue(tok, std::move(value), settings); } else if (tok->str() == "NULL" || (cpp && tok->str() == "nullptr")) { ValueFlow::Value value(0); if (!tok->isTemplateArg()) value.setKnown(); - setTokenValue(tok, std::move(value), settings, isInitList); + setTokenValue(tok, std::move(value), settings); } else if (Token::simpleMatch(tok, "sizeof (")) { if (tok->next()->astOperand2() && !tok->next()->astOperand2()->isLiteral() && tok->next()->astOperand2()->valueType() && (tok->next()->astOperand2()->valueType()->pointer == 0 || // <- TODO this is a bailout, abort when there are array->pointer conversions @@ -1333,17 +1332,17 @@ static Token * valueFlowSetConstantValue(Token *tok, const Settings *settings, b ValueFlow::Value value(0); if (!tok->isTemplateArg()) value.setKnown(); - setTokenValue(tok->next(), std::move(value), settings, isInitList); + setTokenValue(tok->next(), std::move(value), settings); } else if (Token::Match(tok, "%name% = {") && tok->variable() && (tok->variable()->isPointer() || (tok->variable()->valueType() && tok->variable()->valueType()->isIntegral()))) { if (Token::simpleMatch(tok->tokAt(3), "}")) { ValueFlow::Value value(0); value.setKnown(); - setTokenValue(tok->tokAt(2), std::move(value), settings, isInitList); + setTokenValue(tok->tokAt(2), std::move(value), settings); } else if (tok->tokAt(2)->astOperand1() && tok->tokAt(2)->astOperand1()->hasKnownIntValue()) { ValueFlow::Value value(tok->tokAt(2)->astOperand1()->getKnownIntValue()); value.setKnown(); - setTokenValue(tok->tokAt(2), std::move(value), settings, isInitList); + setTokenValue(tok->tokAt(2), std::move(value), settings); } } return tok->next(); @@ -1351,16 +1350,8 @@ static Token * valueFlowSetConstantValue(Token *tok, const Settings *settings, b static void valueFlowNumber(TokenList &tokenlist, const Settings* settings) { - bool isInitList = false; - const Token* endInit{}; for (Token *tok = tokenlist.front(); tok;) { - if (!isInitList && tok->str() == "{" && (Token::simpleMatch(tok->astOperand1(), ",") || Token::simpleMatch(tok->astOperand2(), ","))) { - isInitList = true; - endInit = tok->link(); - } - tok = valueFlowSetConstantValue(tok, settings, tokenlist.isCPP(), isInitList); - if (isInitList && tok == endInit) - isInitList = false; + tok = valueFlowSetConstantValue(tok, settings, tokenlist.isCPP()); } if (tokenlist.isCPP()) { @@ -3064,12 +3055,11 @@ struct SingleValueFlowAnalyzer : ValueFlowAnalyzer { } bool isGlobal() const override { - for (const auto&p:getVars()) { + const auto& vars = getVars(); + return std::any_of(vars.cbegin(), vars.cend(), [] (const std::pair& p) { const Variable* var = p.second; - if (!var->isLocal() && !var->isArgument() && !var->isConst()) - return true; - } - return false; + return !var->isLocal() && !var->isArgument() && !var->isConst(); + }); } bool lowerToPossible() override { @@ -5848,6 +5838,7 @@ static bool intersects(const C1& c1, const C2& c2) { if (c1.size() > c2.size()) return intersects(c2, c1); + // NOLINTNEXTLINE(readability-use-anyofallof) - TODO: fix if possible / also Cppcheck false negative for (auto&& x : c1) { if (c2.find(x) != c2.end()) return true; @@ -8048,7 +8039,7 @@ static void valueFlowUninit(TokenList& tokenlist, const Settings* settings) } } -static bool isContainerSizeChanged(nonneg int varId, +static bool isContainerSizeChanged(const Token* expr, const Token* start, const Token* end, int indirect, @@ -8097,7 +8088,7 @@ static bool isContainerSizeChangedByFunction(const Token* tok, if (!arg->nameToken()) return false; if (depth > 0) - return isContainerSizeChanged(arg->declarationId(), + return isContainerSizeChanged(arg->nameToken(), scope->bodyStart, scope->bodyEnd, addressOf ? indirect + 1 : indirect, @@ -8351,7 +8342,7 @@ bool ValueFlow::isContainerSizeChanged(const Token* tok, int indirect, const Set return isContainerSizeChangedByFunction(tok, indirect, settings, depth); } -static bool isContainerSizeChanged(nonneg int varId, +static bool isContainerSizeChanged(const Token* expr, const Token* start, const Token* end, int indirect, @@ -8359,7 +8350,7 @@ static bool isContainerSizeChanged(nonneg int varId, int depth) { for (const Token *tok = start; tok != end; tok = tok->next()) { - if (tok->varId() != varId) + if (tok->exprId() != expr->exprId() && !isAliasOf(tok, expr)) continue; if (ValueFlow::isContainerSizeChanged(tok, indirect, settings, depth)) return true; @@ -9466,6 +9457,18 @@ void ValueFlow::setValues(TokenList& tokenlist, for (Token* tok = tokenlist.front(); tok; tok = tok->next()) tok->clearValueFlow(); + // commas in init.. + for (Token* tok = tokenlist.front(); tok; tok = tok->next()) { + if (tok->str() != "{" || !tok->astOperand1()) + continue; + for (Token* tok2 = tok->next(); tok2 != tok->link(); tok2 = tok2->next()) { + if (tok2->link() && Token::Match(tok2, "[{[(<]")) + tok2 = tok2->link(); + else if (tok2->str() == ",") + tok2->isInitComma(true); + } + } + ValueFlowPassRunner runner{ValueFlowState{tokenlist, symboldatabase, errorLogger, settings}, timerResults}; runner.run_once({ VFA(valueFlowEnumValue(symboldatabase, settings)), diff --git a/releasenotes.txt b/releasenotes.txt index c54c20094a9..e302a8481f3 100644 --- a/releasenotes.txt +++ b/releasenotes.txt @@ -10,8 +10,12 @@ Improved checking: - constParameter*/constVariable* checks find more instances of pointers/references that can be const, e.g. when calling library functions GUI: +- Show in statistics which checkers have been activated in latest analysis +- Make it more visible if there has been critical errors that caused checkers to be skipped Changed interface: +- Write how many checkers was activated after a run +- Added --checkers-report that can be used to generate a report in a file that shows what checkers was activated and disabled Deprecations: - The qmake build system has been deprecated and will be removed in a future version. @@ -20,3 +24,4 @@ Deprecations: Other: - "USE_QT6=On" will no longer fallback to Qt5 when Qt6 is not found. +- When the execution of an addon fails with an exitcode it will now result in an 'internalError' instead of being silently ignored. \ No newline at end of file diff --git a/test/cli/test-other.py b/test/cli/test-other.py index a630699a115..2d58b2584f4 100644 --- a/test/cli/test-other.py +++ b/test/cli/test-other.py @@ -141,3 +141,82 @@ def test_progress_j(tmpdir): assert exitcode == 0 assert stdout == "Checking {} ...\n".format(test_file) assert stderr == "" + + +@pytest.mark.timeout(10) +def test_slow_array_many_floats(tmpdir): + # 11649 + # cppcheck valueflow takes a long time when an array has many floats + filename = os.path.join(tmpdir, 'hang.c') + with open(filename, 'wt') as f: + f.write("const float f[] = {\n") + for i in range(20000): + f.write(' 13.6f,\n') + f.write("};\n") + cppcheck([filename]) # should not take more than ~1 second + + +@pytest.mark.timeout(10) +def test_slow_array_many_strings(tmpdir): + # 11901 + # cppcheck valueflow takes a long time when analyzing a file with many strings + filename = os.path.join(tmpdir, 'hang.c') + with open(filename, 'wt') as f: + f.write("const char *strings[] = {\n") + for i in range(20000): + f.write(' "abc",\n') + f.write("};\n") + cppcheck([filename]) # should not take more than ~1 second + + +def test_execute_addon_failure(tmpdir): + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt') as f: + f.write(""" + void f(); + """) + + args = ['--addon=naming', test_file] + + # provide empty PATH environment variable so python is not found and execution of addon fails + env = {'PATH': ''} + _, _, stderr = cppcheck(args, env) + assert stderr == '{}:0:0: error: Bailing out from analysis: Checking file failed: Failed to auto detect python [internalError]\n\n^\n'.format(test_file) + + +def test_execute_addon_failure_2(tmpdir): + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt') as f: + f.write(""" + void f(); + """) + + # specify non-existent python executbale so execution of addon fails + args = ['--addon=naming', '--addon-python=python5.x', test_file] + + _, _, stderr = cppcheck(args) + # /tmp/pytest-of-sshuser/pytest-215/test_execute_addon_failure_20/test.cpp:0:0: error: Bailing out from analysis: Checking file failed: Failed to execute addon 'naming' (command: 'python5.x /mnt/s/GitHub/cppcheck-fw/addons/runaddon.py /mnt/s/GitHub/cppcheck-fw/addons/naming.py --cli /tmp/pytest-of-sshuser/pytest-215/test_execute_addon_failure_20/test.cpp.7306.dump'). Exitcode is nonzero. [internalError]\n\n^\n + # "C:\\Users\\Quotenjugendlicher\\AppData\\Local\\Temp\\pytest-of-Quotenjugendlicher\\pytest-15\\test_execute_addon_failure_20\\test.cpp:0:0: error: Bailing out from analysis: Checking file failed: Failed to execute addon (command: 'python5.x S:\\GitHub\\cppcheck-fw\\bin\\debug\\addons\\runaddon.py S:\\GitHub\\cppcheck-fw\\bin\\debug\\addons\\naming.py --cli C:\\Users\\Quotenjugendlicher\\AppData\\Local\\Temp\\pytest-of-Quotenjugendlicher\\pytest-15\\test_execute_addon_failure_20\\test.cpp.9892.dump'). Exitcode is nonzero. [internalError]\n\n^\n + assert stderr.startswith('{}:0:0: error: Bailing out from analysis: Checking file failed: Failed to execute addon \'naming\' (command: \'python5.x '.format(test_file)) + assert stderr.endswith(' [internalError]\n\n^\n') + + +# TODO: find a test case which always fails +@pytest.mark.skip +def test_internal_error(tmpdir): + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt') as f: + f.write(""" +#include + +void f() { + double gc = 3333.3333; + char stat[80]; + sprintf(stat,"'%2.1f'",gc); +} + """) + + args = [test_file] + + _, _, stderr = cppcheck(args) + assert stderr == '{}:0:0: error: Bailing from out analysis: Checking file failed: converting \'1f\' to integer failed - not an integer [internalError]\n\n^\n'.format(test_file) \ No newline at end of file diff --git a/test/cli/testutils.py b/test/cli/testutils.py index c1a1455eab7..1e53c65db48 100644 --- a/test/cli/testutils.py +++ b/test/cli/testutils.py @@ -63,12 +63,12 @@ def lookup_cppcheck_exe(): # Run Cppcheck with args -def cppcheck(args): +def cppcheck(args, env=None): exe = lookup_cppcheck_exe() assert exe is not None, 'no cppcheck binary found' logging.info(exe + ' ' + ' '.join(args)) - p = subprocess.Popen([exe] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p = subprocess.Popen([exe] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) comm = p.communicate() stdout = comm[0].decode(encoding='utf-8', errors='ignore').replace('\r\n', '\n') stderr = comm[1].decode(encoding='utf-8', errors='ignore').replace('\r\n', '\n') diff --git a/test/testconstructors.cpp b/test/testconstructors.cpp index 55925648811..418025e33c9 100644 --- a/test/testconstructors.cpp +++ b/test/testconstructors.cpp @@ -116,6 +116,7 @@ class TestConstructors : public TestFixture { TEST_CASE(initvar_chained_assign); // BUG 2270433 TEST_CASE(initvar_2constructors); // BUG 2270353 TEST_CASE(initvar_constvar); + TEST_CASE(initvar_mutablevar); TEST_CASE(initvar_staticvar); TEST_CASE(initvar_brace_init); TEST_CASE(initvar_union); @@ -1194,6 +1195,18 @@ class TestConstructors : public TestFixture { } + void initvar_mutablevar() { + check("class Foo {\n" + "public:\n" + " Foo() { update(); }\n" + "private:\n" + " void update() const;\n" + " mutable int x;\n" + "};"); + ASSERT_EQUALS("", errout.str()); + } + + void initvar_staticvar() { check("class Fred\n" "{\n" diff --git a/test/testother.cpp b/test/testother.cpp index 4f5782e6d90..829265a3790 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -11033,6 +11033,18 @@ class TestOther : public TestFixture { "}"); ASSERT_EQUALS("", errout.str()); + // #11894 + check("struct S {\n" + " int *p, n;\n" + "};\n" + "S* g() {\n" + " S* s = static_cast(calloc(1, sizeof(S)));\n" + " s->n = 100;\n" + " s->p = static_cast(malloc(s->n * sizeof(int)));\n" + " return s;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + // #11679 check("bool g(int);\n" "void h(int);\n" @@ -11041,6 +11053,20 @@ class TestOther : public TestFixture { " if (g(k(i))) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); + + // #11889 + check("struct S {\n" + " int a[5];\n" + " void f(int i);\n" + "}\n" + "void g(int);\n" + "void S::f(int i) {\n" + " if (a[i] == 1) {\n" + " a[i] = 0;\n" + " g(a[i]);\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void knownArgumentHiddenVariableExpression() { diff --git a/test/testrunner.vcxproj.filters b/test/testrunner.vcxproj.filters index db85b4d9e37..7aa2676a830 100644 --- a/test/testrunner.vcxproj.filters +++ b/test/testrunner.vcxproj.filters @@ -256,6 +256,9 @@ Source Files + + Source Files + diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 13c7846061f..aaf934775b4 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -442,6 +442,7 @@ class TestSymbolDatabase : public TestFixture { TEST_CASE(findFunction47); TEST_CASE(findFunction48); TEST_CASE(findFunction49); // #11888 + TEST_CASE(findFunction50); // #11904 - method with same name and arguments in derived class TEST_CASE(findFunctionContainer); TEST_CASE(findFunctionExternC); TEST_CASE(findFunctionGlobalScope); // ::foo @@ -7304,6 +7305,26 @@ class TestSymbolDatabase : public TestFixture { ASSERT_EQUALS(4, ftok->function()->tokenDef->linenr()); } + void findFunction50() { + { + GET_SYMBOL_DB("struct B { B(); void init(unsigned int value); };\n" + "struct D: B { D(); void init(unsigned int value); };\n" + "D::D() { init(0); }\n" + "void D::init(unsigned int value) {}\n"); + const Token* call = Token::findsimplematch(tokenizer.tokens(), "init ( 0 ) ;"); + ASSERT(call && call->function() && call->function()->functionScope); + } + + { + GET_SYMBOL_DB("struct B { B(); void init(unsigned int value); };\n" + "struct D: B { D(); void init(unsigned int value); };\n" + "D::D() { init(0ULL); }\n" + "void D::init(unsigned int value) {}\n"); + const Token* call = Token::findsimplematch(tokenizer.tokens(), "init ( 0ULL ) ;"); + ASSERT(call && call->function() && call->function()->functionScope); + } + } + void findFunctionContainer() { { GET_SYMBOL_DB("void dostuff(std::vector v);\n" diff --git a/test/testutils.cpp b/test/testutils.cpp index 09f92d3815a..bdc095f54a0 100644 --- a/test/testutils.cpp +++ b/test/testutils.cpp @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include @@ -36,6 +38,7 @@ class TestUtils : public TestFixture { TEST_CASE(isStringLiteral); TEST_CASE(isCharLiteral); TEST_CASE(strToInt); + TEST_CASE(id_string); } void isValidGlobPattern() const { @@ -330,6 +333,18 @@ class TestUtils : public TestFixture { ASSERT_EQUALS("out of range (stoull)", err); } } + + void id_string() const + { + ASSERT_EQUALS("0", id_string_i(0)); + ASSERT_EQUALS("f1", id_string_i(0xF1)); + ASSERT_EQUALS("123", id_string_i(0x123)); + ASSERT_EQUALS("1230", id_string_i(0x1230)); + ASSERT_EQUALS(std::string(8,'f'), id_string_i(0xffffffffU)); + if (sizeof(void*) == 8) { + ASSERT_EQUALS(std::string(16,'f'), id_string_i(~0ULL)); + } + } }; REGISTER_TEST(TestUtils) diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index e1302d9ce73..788b96f7623 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -6643,6 +6643,18 @@ class TestValueFlow : public TestFixture { code = "int f() { auto a = std::array{}; return a[1]; }"; ASSERT_EQUALS("values.size():0", isKnownContainerSizeValue(tokenValues(code, "a ["), 0)); + + code = "void g(std::vector* w) {\n" + " std::vector &r = *w;\n" + " r.push_back(0);\n" + "}\n" + "int f() {\n" + " std::vector v;\n" + " g(&v);\n" + " return v[0];\n" + "}\n"; + ASSERT(!isKnownContainerSizeValue(tokenValues(code, "v ["), 0).empty()); + ASSERT(!isPossibleContainerSizeValue(tokenValues(code, "v ["), 0).empty()); } void valueFlowContainerElement() diff --git a/tools/get_checkers.py b/tools/get_checkers.py index b43e457a08f..7dbdbf36801 100644 --- a/tools/get_checkers.py +++ b/tools/get_checkers.py @@ -43,27 +43,400 @@ print('static std::map premiumCheckers{') premium_checkers = """ -$ grep logChecker *.cpp | sed 's/.*logChecker/logChecker/' -logChecker("CheckBufferOverrun::addressOfPointerArithmetic"); // warning -logChecker("CheckBufferOverrun::negativeBufferSizeCheckedNonZero"); // warning -logChecker("CheckBufferOverrun::negativeBufferSizeCheckedNonZero"); // warning -logChecker("CheckHang::infiniteLoop"); -logChecker("CheckHang::infiniteLoopContinue"); -logChecker("CheckOther::arrayPointerComparison"); // style -logChecker("CheckOther::knownResult"); // style -logChecker("CheckOther::lossOfPrecision"); // style -logChecker("CheckOther::pointerCast"); // style -logChecker("CheckOther::reassignInLoop"); // style -logChecker("CheckOther::unreachableCode"); // style -logChecker("CheckStrictAlias::strictAliasCondition"); // warning -logChecker("CheckUninitVar::uninitvar"); -logChecker("CheckUninitVar::uninitmember"); -logChecker("CheckUnusedVar::unreadVariable"); // style -logChecker("CheckUnusedVar::unusedPrivateMember"); // style +$ grep logChecker src/*.cpp | sed 's/.*logChecker/logChecker/' | sort > 1.txt +logChecker("Autosar: A0-1-3"); // style +logChecker("Autosar: A0-4-2"); // style +logChecker("Autosar: A0-4-4"); // style +logChecker("Autosar: A10-1-1"); // style +logChecker("Autosar: A11-0-2"); // style +logChecker("Autosar: A11-3-1"); // style +logChecker("Autosar: A13-2-1"); // style +logChecker("Autosar: A13-2-3"); // style +logChecker("Autosar: A13-5-2"); // style +logChecker("Autosar: A13-5-5"); // style +logChecker("Autosar: A15-1-2"); // style +logChecker("Autosar: A15-3-5"); // style +logChecker("Autosar: A16-6-1"); // style +logChecker("Autosar: A16-7-1"); // style +logChecker("Autosar: A18-0-3"); // style +logChecker("Autosar: A18-1-1"); // style +logChecker("Autosar: A18-1-2"); // style +logChecker("Autosar: A18-1-3"); // style +logChecker("Autosar: A18-5-1"); // style +logChecker("Autosar: A18-9-1"); // style +logChecker("Autosar: A2-11-1"); // style +logChecker("Autosar: A2-13-1"); // style +logChecker("Autosar: A2-13-3"); // style +logChecker("Autosar: A2-13-5"); // style +logChecker("Autosar: A2-13-6"); // style +logChecker("Autosar: A2-5-2"); // style +logChecker("Autosar: A3-1-3"); // style +logChecker("Autosar: A3-1-4"); // style +logChecker("Autosar: A3-3-1"); // style +logChecker("Autosar: A4-10-1"); // style +logChecker("Autosar: A4-7-1"); // style +logChecker("Autosar: A5-0-2"); // style +logChecker("Autosar: A5-0-3"); // style +logChecker("Autosar: A5-0-4"); // style +logChecker("Autosar: A5-1-1"); // style +logChecker("Autosar: A5-1-2"); // style +logChecker("Autosar: A5-1-3"); // style +logChecker("Autosar: A5-16-1"); // style +logChecker("Autosar: A5-1-6"); // style +logChecker("Autosar: A5-1-7"); // style +logChecker("Autosar: A5-2-1"); // style +logChecker("Autosar: A5-2-4"); // style +logChecker("Autosar: A6-5-3"); // style +logChecker("Autosar: A7-1-4"); // style +logChecker("Autosar: A7-1-6"); // style +logChecker("Autosar: A7-1-7"); // style +logChecker("Autosar: A8-4-1"); // style +logChecker("Autosar: A8-5-3"); // style +logChecker("Autosar: A9-3-1"); // style +logChecker("Cert C: ARR30-C"); // warning +logChecker("Cert C: ARR32-C"); // warning +logChecker("Cert C: ARR37-C"); // warning +logChecker("Cert C: ARR38-C"); +logChecker("Cert C: ARR39-C"); // warning +logChecker("Cert C: CON30-C"); // style +logChecker("Cert C: CON31-C"); // style +logChecker("Cert C: CON32-C"); // style +logChecker("Cert C: CON33-C"); // style +logChecker("Cert C: CON34-C"); // warning +logChecker("Cert C: CON35-C"); // warning +logChecker("Cert C: CON36-C"); // style +logChecker("Cert C: CON37-C"); // style +logChecker("Cert C: CON38-C"); // warning +logChecker("Cert C: CON39-C"); // warning +logChecker("Cert C: CON40-C"); // warning +logChecker("Cert C: CON41-C"); // style +logChecker("Cert C++: CON51-CPP"); +logChecker("Cert C++: CON52-CPP"); // style +logChecker("Cert C++: CON53-CPP"); // style +logChecker("Cert C++: CON54-CPP"); // style +logChecker("Cert C++: CON55-CPP"); // style +logChecker("Cert C++: CON56-CPP"); +logChecker("Cert C++: CTR50-CPP"); +logChecker("Cert C++: CTR52-CPP"); +logChecker("Cert C++: CTR53-CPP"); +logChecker("Cert C++: CTR56-CPP"); // style +logChecker("Cert C++: CTR57-CPP"); +logChecker("Cert C++: CTR58-CPP"); +logChecker("Cert C: DCL31-C"); // style +logChecker("Cert C: DCL36-C"); // style +logChecker("Cert C: DCL37-C"); // style +logChecker("Cert C: DCL38-C"); // style +logChecker("Cert C: DCL39-C"); // style +logChecker("Cert C: DCL40-C"); // style +logChecker("Cert C: DCL41-C"); // style +logChecker("Cert C++: DCL50-CPP"); // style +logChecker("Cert C++: DCL51-CPP"); // style +logChecker("Cert C++: DCL52-CPP"); // style +logChecker("Cert C++: DCL53-CPP"); // style +logChecker("Cert C++: DCL54-CPP"); +logChecker("Cert C++: DCL56-CPP"); +logChecker("Cert C++: DCL58-CPP"); // style +logChecker("Cert C++: DCL59-CPP"); // style +logChecker("Cert C: ENV30-C"); // style +logChecker("Cert C: ENV31-C"); // style +logChecker("Cert C: ENV32-C"); // style +logChecker("Cert C: ENV33-C"); // style +logChecker("Cert C: ENV34-C"); // style +logChecker("Cert C: ERR30-C"); // warning +logChecker("Cert C: ERR32-C"); // warning +logChecker("Cert C: ERR33-C"); // warning +logChecker("Cert C++: ERR50-CPP"); +logChecker("Cert C++: ERR51-CPP"); // style +logChecker("Cert C++: ERR52-CPP"); // style +logChecker("Cert C++: ERR53-CPP"); +logChecker("Cert C++: ERR54-CPP"); +logChecker("Cert C++: ERR55-CPP"); +logChecker("Cert C++: ERR56-CPP"); +logChecker("Cert C++: ERR58-CPP"); +logChecker("Cert C++: ERR59-CPP"); // warning +logChecker("Cert C++: ERR60-CPP"); // warning +logChecker("Cert C++: ERR61-CPP"); // style +logChecker("Cert C++: ERR62-CPP"); // style +logChecker("Cert C: EXP32-C"); // warning +logChecker("Cert C: EXP35-C"); +logChecker("Cert C: EXP36-C"); // style +logChecker("Cert C: EXP37-C"); // style +logChecker("Cert C: EXP39-C"); // style +logChecker("Cert C: EXP40-C"); // style +logChecker("Cert C: EXP42-C"); // style +logChecker("Cert C: EXP43-C"); // style +logChecker("Cert C: EXP45-C"); // warning +logChecker("Cert C++: EXP50-CPP"); +logChecker("Cert C++: EXP51-CPP"); +logChecker("Cert C++: EXP55-CPP"); +logChecker("Cert C++: EXP56-CPP"); +logChecker("Cert C++: EXP57-CPP"); // style +logChecker("Cert C++: EXP58-CPP"); // style +logChecker("Cert C++: EXP59-CPP"); +logChecker("Cert C: FIO30-C"); // warning +logChecker("Cert C: FIO32-C"); // style +logChecker("Cert C: FIO34-C"); // style +logChecker("Cert C: FIO37-C"); +logChecker("Cert C: FIO38-C"); // style +logChecker("Cert C: FIO40-C"); // style +logChecker("Cert C: FIO41-C"); // style +logChecker("Cert C: FIO44-C"); // warning +logChecker("Cert C: FIO45-C"); // warning +logChecker("Cert C++: FIO51-CPP"); // style +logChecker("Cert C: FLP30-C"); // warning +logChecker("Cert C: FLP36-C"); // portability +logChecker("Cert C: FLP37-C"); // style +logChecker("Cert C: INT30-C"); // warning +logChecker("Cert C: INT31-C"); // warning +logChecker("Cert C: INT32-C"); // warning +logChecker("Cert C: INT33-C"); // warning +logChecker("Cert C: INT34-C"); // warning +logChecker("Cert C: INT35-C"); // warning +logChecker("Cert C: INT36-C"); // warning +logChecker("Cert C++: INT50-CPP"); // style +logChecker("Cert C: MEM33-C"); // style +logChecker("Cert C: MEM35-C"); // warning +logChecker("Cert C: MEM36-C"); // warning +logChecker("Cert C++: MEM52-CPP"); +logChecker("Cert C++: MEM53-CPP"); +logChecker("Cert C++: MEM54-CPP"); +logChecker("Cert C++: MEM55-CPP"); +logChecker("Cert C++: MEM57-CPP"); // style +logChecker("Cert C: MSC30-C"); // style +logChecker("Cert C: MSC32-C"); // style +logChecker("Cert C: MSC33-C"); // style +logChecker("Cert C: MSC38-C"); // warning +logChecker("Cert C: MSC39-C"); // warning +logChecker("Cert C: MSC40-C"); // warning +logChecker("Cert C++: MSC50-CPP"); // style +logChecker("Cert C++: MSC51-CPP"); // style +logChecker("Cert C++: MSC53-CPP"); +logChecker("Cert C++: MSC54-CPP"); // style +logChecker("Cert C++: OOP51-CPP"); +logChecker("Cert C++: OOP55-CPP"); +logChecker("Cert C++: OOP56-CPP"); +logChecker("Cert C++: OOP57-CPP"); +logChecker("Cert C++: OOP58-CPP"); // style +logChecker("Cert C: PRE31-C"); // style +logChecker("Cert C: SIG30-C"); // style +logChecker("Cert C: SIG31-C"); // warning +logChecker("Cert C: SIG34-C"); // style +logChecker("Cert C: SIG35-C"); // warning +logChecker("Cert C: STR31-C"); // warning +logChecker("Cert C: STR32-C"); // warning +logChecker("Cert C: STR34-C"); // warning +logChecker("Cert C: STR38-C"); // style +logChecker("Cert C++: STR50-CPP"); +logChecker("Cert C++: STR53-CPP"); +logChecker("Misra C: 10.1"); // style +logChecker("Misra C: 10.2"); // style +logChecker("Misra C: 10.3"); // style +logChecker("Misra C: 10.4"); // style +logChecker("Misra C: 10.5"); // style +logChecker("Misra C: 10.6"); // style +logChecker("Misra C: 10.7"); // style +logChecker("Misra C: 10.8"); // style +logChecker("Misra C: 11.10"); // style +logChecker("Misra C: 12.6"); +logChecker("Misra C: 1.5"); // style +logChecker("Misra C: 17.10"); // style +logChecker("Misra C: 17.11"); // style +logChecker("Misra C: 17.12"); // style +logChecker("Misra C: 17.9"); // style +logChecker("Misra C: 18.10"); // style +logChecker("Misra C: 18.9"); // style +logChecker("Misra C: 21.12"); // style +logChecker("Misra C: 21.22"); // style +logChecker("Misra C: 21.23"); // style +logChecker("Misra C: 21.24"); // style +logChecker("Misra C: 21.25"); // warning +logChecker("Misra C: 21.26"); // warning +logChecker("Misra C: 22.11"); +logChecker("Misra C: 22.12"); // style +logChecker("Misra C: 22.13"); // style +logChecker("Misra C: 22.14"); // style +logChecker("Misra C: 22.15"); // style +logChecker("Misra C: 22.16"); // warning +logChecker("Misra C: 22.17"); // warning +logChecker("Misra C: 22.18"); // warning +logChecker("Misra C: 22.19"); // warning +logChecker("Misra C: 22.20"); // style +logChecker("Misra C: 23.1"); // style +logChecker("Misra C: 23.2"); // style +logChecker("Misra C: 23.3"); // style +logChecker("Misra C: 23.4"); // style +logChecker("Misra C: 23.5"); // style +logChecker("Misra C: 23.6"); // style +logChecker("Misra C: 23.7"); // style +logChecker("Misra C: 23.8"); // style +logChecker("Misra C: 6.3"); // style +logChecker("Misra C: 7.5"); // style +logChecker("Misra C: 7.6"); // style +logChecker("Misra C: 8.10"); // style +logChecker("Misra C: 8.15"); // style +logChecker("Misra C: 8.16"); // style +logChecker("Misra C: 8.17"); // style +logChecker("Misra C: 9.6"); // style +logChecker("Misra C: 9.7"); +logChecker("Misra C++: M0-1-11"); // style +logChecker("Misra C++: M0-1-12"); // style +logChecker("Misra C++: M0-1-4"); // style +logChecker("Misra C++: M0-1-5"); // style +logChecker("Misra C++: M0-1-7"); // style +logChecker("Misra C++: M0-1-8"); // style +logChecker("Misra C++: M10-1-1"); // style +logChecker("Misra C++: M10-1-2"); // style +logChecker("Misra C++: M10-1-3"); // style +logChecker("Misra C++: M10-2-1"); // style +logChecker("Misra C++: M10-3-3"); // style +logChecker("Misra C++: M11-0-1"); // style +logChecker("Misra C++: M12-8-2"); // style +logChecker("Misra C++: M14-5-1"); // warning +logChecker("Misra C++: M14-5-2"); // warning +logChecker("Misra C++: M14-5-3"); // warning +logChecker("Misra C++: M14-6-1"); // warning +logChecker("Misra C++: M14-7-1"); // style +logChecker("Misra C++: M14-7-2"); // style +logChecker("Misra C++: M15-0-3"); +logChecker("Misra C++: M15-1-1"); +logChecker("Misra C++: M15-1-2"); // style +logChecker("Misra C++: M15-1-3"); // style +logChecker("Misra C++: M15-3-2"); // warning +logChecker("Misra C++: M15-3-3"); +logChecker("Misra C++: M15-4-1"); // style +logChecker("Misra C++: M16-0-1"); // style +logChecker("Misra C++: M16-0-2"); // style +logChecker("Misra C++: M16-0-3"); // style +logChecker("Misra C++: M16-0-4"); // style +logChecker("Misra C++: M16-1-1"); // style +logChecker("Misra C++: M16-2-1"); // style +logChecker("Misra C++: M16-2-2"); // style +logChecker("Misra C++: M16-2-3"); // style +logChecker("Misra C++: M16-2-4"); // style +logChecker("Misra C++: M16-2-5"); // style +logChecker("Misra C++: M16-2-6"); // style +logChecker("Misra C++: M16-3-1"); // style +logChecker("Misra C++: M16-3-2"); // style +logChecker("Misra C++: M17-0-1"); // style +logChecker("Misra C++: M17-0-2"); // style +logChecker("Misra C++: M17-0-3"); // style +logChecker("Misra C++: M17-0-5"); // style +logChecker("Misra C++: M18-0-1"); // style +logChecker("Misra C++: M18-0-2"); // style +logChecker("Misra C++: M18-0-3"); // style +logChecker("Misra C++: M18-0-4"); // style +logChecker("Misra C++: M18-0-5"); // style +logChecker("Misra C++: M18-2-1"); // style +logChecker("Misra C++: M18-4-1"); // style +logChecker("Misra C++: M18-7-1"); // style +logChecker("Misra C++: M19-3-1"); // style +logChecker("Misra C++: M2-10-1"); // style +logChecker("Misra C++: M2-10-3"); // style +logChecker("Misra C++: M2-10-4"); // style +logChecker("Misra C++: M2-10-5"); // style +logChecker("Misra C++: M2-10-6"); // style +logChecker("Misra C++: M2-13-4"); // style +logChecker("Misra C++: M2-13-5"); // style +logChecker("Misra C++: M27-0-1"); // style +logChecker("Misra C++: M2-7-1"); // style +logChecker("Misra C++: M2-7-2"); // style +logChecker("Misra C++: M2-7-3"); // style +logChecker("Misra C++: M3-1-1"); // style +logChecker("Misra C++: M3-1-2"); // style +logChecker("Misra C++: M3-1-3"); // style +logChecker("Misra C++: M3-2-1"); +logChecker("Misra C++: M3-3-1"); // style +logChecker("Misra C++: M3-3-2"); // style +logChecker("Misra C++: M3-9-1"); // style +logChecker("Misra C++: M3-9-2"); // style +logChecker("Misra C++: M3-9-3"); // style +logChecker("Misra C++: M4-10-1"); // style +logChecker("Misra C++: M4-10-2"); // style +logChecker("Misra C++: M4-5-1"); // style +logChecker("Misra C++: M4-5-2"); // style +logChecker("Misra C++: M4-5-3"); // style +logChecker("Misra C++: M5-0-10"); // style +logChecker("Misra C++: M5-0-11"); // style +logChecker("Misra C++: M5-0-12"); // style +logChecker("Misra C++: M5-0-14"); // style +logChecker("Misra C++: M5-0-15"); // style +logChecker("Misra C++: M5-0-20"); // style +logChecker("Misra C++: M5-0-21"); // style +logChecker("Misra C++: M5-0-2"); // style +logChecker("Misra C++: M5-0-3"); // style +logChecker("Misra C++: M5-0-4"); // style +logChecker("Misra C++: M5-0-5"); // style +logChecker("Misra C++: M5-0-6"); // style +logChecker("Misra C++: M5-0-7"); // style +logChecker("Misra C++: M5-0-8"); // style +logChecker("Misra C++: M5-0-9"); // style +logChecker("Misra C++: M5-2-10"); // style +logChecker("Misra C++: M5-2-11"); // style +logChecker("Misra C++: M5-2-12"); // style +logChecker("Misra C++: M5-2-1"); // style +logChecker("Misra C++: M5-2-2"); // style +logChecker("Misra C++: M5-2-3"); // style +logChecker("Misra C++: M5-2-5"); // style +logChecker("Misra C++: M5-2-6"); // style +logChecker("Misra C++: M5-2-7"); // style +logChecker("Misra C++: M5-2-8"); // style +logChecker("Misra C++: M5-2-9"); // style +logChecker("Misra C++: M5-3-1"); // style +logChecker("Misra C++: M5-3-2"); // style +logChecker("Misra C++: M5-3-3"); // style +logChecker("Misra C++: M6-2-3"); // style +logChecker("Misra C++: M6-4-4"); // style +logChecker("Misra C++: M6-4-6"); // style +logChecker("Misra C++: M6-4-7"); // style +logChecker("Misra C++: M6-4-8"); // style +logChecker("Misra C++: M6-5-1"); // style +logChecker("Misra C++: M6-5-2"); // style +logChecker("Misra C++: M6-5-3"); // style +logChecker("Misra C++: M6-5-4"); // style +logChecker("Misra C++: M6-5-5"); // style +logChecker("Misra C++: M6-5-6"); // style +logChecker("Misra C++: M6-6-1"); // style +logChecker("Misra C++: M6-6-3"); // style +logChecker("Misra C++: M6-6-4"); // style +logChecker("Misra C++: M6-6-5"); // style +logChecker("Misra C++: M7-2-1"); // style +logChecker("Misra C++: M7-3-1"); // style +logChecker("Misra C++: M7-3-2"); // style +logChecker("Misra C++: M7-3-3"); // style +logChecker("Misra C++: M7-3-4"); // style +logChecker("Misra C++: M7-3-5"); // style +logChecker("Misra C++: M7-3-6"); // style +logChecker("Misra C++: M7-4-2"); // style +logChecker("Misra C++: M7-4-3"); // style +logChecker("Misra C++: M7-5-3"); // style +logChecker("Misra C++: M8-0-1"); // style +logChecker("Misra C++: M8-3-1"); // style +logChecker("Misra C++: M8-4-4"); // style +logChecker("Misra C++: M9-3-1"); // style +logChecker("Misra C++: M9-5-1"); // style +logChecker("Misra C++: M9-6-2"); // style +logChecker("Misra C++: M9-6-3"); // style +logChecker("Misra C++: M9-6-4"); // style +logChecker("PremiumCheckBufferOverrun::addressOfPointerArithmetic"); // warning +logChecker("PremiumCheckBufferOverrun::negativeBufferSizeCheckedNonZero"); // warning +logChecker("PremiumCheckBufferOverrun::negativeBufferSizeCheckedNonZero"); // warning +logChecker("PremiumCheckHang::infiniteLoop"); +logChecker("PremiumCheckHang::infiniteLoopContinue"); +logChecker("PremiumCheckOther::arrayPointerComparison"); // style +logChecker("PremiumCheckOther::knownResult"); // style +logChecker("PremiumCheckOther::lossOfPrecision"); // style +logChecker("PremiumCheckOther::pointerCast"); // style +logChecker("PremiumCheckOther::reassignInLoop"); // style +logChecker("PremiumCheckOther::unreachableCode"); // style +logChecker("PremiumCheckStrictAlias::strictAliasCondition"); // warning +logChecker("PremiumCheckUninitVar::uninitmember"); +logChecker("PremiumCheckUninitVar::uninitvar"); +logChecker("PremiumCheckUnusedVar::unreadVariable"); // style +logChecker("PremiumCheckUnusedVar::unusedPrivateMember"); // style """ for line in premium_checkers.split('\n'): - res = re.match(r'logChecker\("([:_a-zA-Z0-9]+)"\);.*', line) + res = re.match(r'logChecker\("([^"]+)"\);.*', line) if res is None: continue if line.find('//') > 0: