diff --git a/web/client/codechecker_client/cli/store.py b/web/client/codechecker_client/cli/store.py index a027527765..6b968efa4c 100644 --- a/web/client/codechecker_client/cli/store.py +++ b/web/client/codechecker_client/cli/store.py @@ -37,8 +37,7 @@ from codechecker_report_converter import twodim from codechecker_report_converter.report import Report, report_file, \ reports as reports_helper, statistics as report_statistics -from codechecker_report_converter.report.hash import HashType, \ - get_report_path_hash +from codechecker_report_converter.report.hash import get_report_path_hash from codechecker_report_converter.report.parser.base import AnalyzerInfo try: @@ -421,17 +420,15 @@ def get_reports( """ Get reports from the given analyzer result file. """ reports = report_file.get_reports( analyzer_result_file_path, checker_labels) - - # CppCheck generates a '0' value for the report hash. In case all of the - # reports in a result file contain only a hash with '0' value, overwrite - # the hash values in the report files with a context free hash value. - if all(r.report_hash == '0' for r in reports): - report_file.replace_report_hash( - analyzer_result_file_path, HashType.CONTEXT_FREE) - - reports = report_file.get_reports( - analyzer_result_file_path, checker_labels) - + # If CppCheck is ran natively (without CodeChecker), it generates a '0' + # value for the report hash by default. We used to correct this during the + # store operation, but we no longer accept plists that are not compliant + # with out plist specification in docs/tools/plist.md. + if reports and all(r.report_hash == '0' for r in reports): + LOG.error("All bug hashes are 0 in the report file, which is no " + "longer supported. Please re-analyze the project using " + "CodeChecker!") + sys.exit(1) return reports diff --git a/web/tests/functional/cppcheck/test_proj/Makefile b/web/tests/functional/cppcheck/0_hashes_test_proj/Makefile similarity index 100% rename from web/tests/functional/cppcheck/test_proj/Makefile rename to web/tests/functional/cppcheck/0_hashes_test_proj/Makefile diff --git a/web/tests/functional/cppcheck/test_proj/divide_zero.cpp b/web/tests/functional/cppcheck/0_hashes_test_proj/divide_zero.cpp similarity index 95% rename from web/tests/functional/cppcheck/test_proj/divide_zero.cpp rename to web/tests/functional/cppcheck/0_hashes_test_proj/divide_zero.cpp index c80ae6608b..9c45951217 100644 --- a/web/tests/functional/cppcheck/test_proj/divide_zero.cpp +++ b/web/tests/functional/cppcheck/0_hashes_test_proj/divide_zero.cpp @@ -13,7 +13,7 @@ void test1(int z) { int x = 1 / z; // warn } -void test2{ +void test2(){ int x = 1; - div(x) + div(x); } diff --git a/web/tests/functional/cppcheck/test_proj/divide_zero.plist b/web/tests/functional/cppcheck/0_hashes_test_proj/divide_zero_0_hashes.plist similarity index 100% rename from web/tests/functional/cppcheck/test_proj/divide_zero.plist rename to web/tests/functional/cppcheck/0_hashes_test_proj/divide_zero_0_hashes.plist diff --git a/web/tests/functional/cppcheck/test_proj/lib.h b/web/tests/functional/cppcheck/0_hashes_test_proj/lib.h similarity index 100% rename from web/tests/functional/cppcheck/test_proj/lib.h rename to web/tests/functional/cppcheck/0_hashes_test_proj/lib.h diff --git a/web/tests/functional/cppcheck/correct_test_proj/Makefile b/web/tests/functional/cppcheck/correct_test_proj/Makefile new file mode 100644 index 0000000000..bd49cfbe88 --- /dev/null +++ b/web/tests/functional/cppcheck/correct_test_proj/Makefile @@ -0,0 +1,5 @@ + +all: analyze + +analyze: + cppcheck --enable=all --plist-output=. . diff --git a/web/tests/functional/cppcheck/correct_test_proj/divide_zero.cpp b/web/tests/functional/cppcheck/correct_test_proj/divide_zero.cpp new file mode 100644 index 0000000000..9c45951217 --- /dev/null +++ b/web/tests/functional/cppcheck/correct_test_proj/divide_zero.cpp @@ -0,0 +1,19 @@ +// ------------------------------------------------------------------------- +// Part of the CodeChecker project, under the Apache License v2.0 with +// LLVM Exceptions. See LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// ------------------------------------------------------------------------- + +// core.DivideZero (C, C++, ObjC) +// Check for division by zero. +#include "lib.h" + +void test1(int z) { + if (z == 0) + int x = 1 / z; // warn +} + +void test2(){ + int x = 1; + div(x); +} diff --git a/web/tests/functional/cppcheck/correct_test_proj/divide_zero.plist b/web/tests/functional/cppcheck/correct_test_proj/divide_zero.plist new file mode 100644 index 0000000000..9aefe93730 --- /dev/null +++ b/web/tests/functional/cppcheck/correct_test_proj/divide_zero.plist @@ -0,0 +1,310 @@ + + + + + diagnostics + + + category + error + check_name + cppcheck-zerodiv + description + Division by zero. + issue_hash_content_of_line_in_context + f5daf9f6c6b883716a45183adde1d9d3 + location + + col + 13 + file + 1 + line + 11 + + path + + + depth + 0 + kind + event + location + + col + 13 + file + 1 + line + 11 + + message + Division by zero + ranges + + + + col + 13 + file + 1 + line + 11 + + + col + 13 + file + 1 + line + 11 + + + + + + depth + 0 + kind + event + location + + col + 13 + file + 1 + line + 11 + + message + Division by zero. + ranges + + + + col + 13 + file + 1 + line + 11 + + + col + 13 + file + 1 + line + 11 + + + + + + + + category + warning + check_name + cppcheck-zerodivcond + description + Either the condition 'z==0' is redundant or there is division by zero at line 13. + issue_hash_content_of_line_in_context + cb1ef69bbe051721d0b09f981ee63319 + location + + col + 15 + file + 0 + line + 13 + + path + + + edges + + + end + + + col + 15 + file + 0 + line + 13 + + + col + 15 + file + 0 + line + 13 + + + start + + + col + 9 + file + 0 + line + 12 + + + col + 9 + file + 0 + line + 12 + + + + + kind + control + + + depth + 0 + kind + event + location + + col + 9 + file + 0 + line + 12 + + message + Assuming that condition 'z==0' is not redundant + ranges + + + + col + 9 + file + 0 + line + 12 + + + col + 9 + file + 0 + line + 12 + + + + + + depth + 0 + kind + event + location + + col + 15 + file + 0 + line + 13 + + message + Division by zero + ranges + + + + col + 15 + file + 0 + line + 13 + + + col + 15 + file + 0 + line + 13 + + + + + + depth + 0 + kind + event + location + + col + 15 + file + 0 + line + 13 + + message + Either the condition 'z==0' is redundant or there is division by zero at line 13. + ranges + + + + col + 15 + file + 0 + line + 13 + + + col + 15 + file + 0 + line + 13 + + + + + + + + files + + divide_zero.cpp + lib.h + + metadata + + analyzer + + name + cppcheck + + generated_by + + name + CodeChecker + version + 6.29.0 + + + + diff --git a/web/tests/functional/cppcheck/correct_test_proj/lib.h b/web/tests/functional/cppcheck/correct_test_proj/lib.h new file mode 100644 index 0000000000..7ded7be578 --- /dev/null +++ b/web/tests/functional/cppcheck/correct_test_proj/lib.h @@ -0,0 +1,12 @@ +// ------------------------------------------------------------------------- +// Part of the CodeChecker project, under the Apache License v2.0 with +// LLVM Exceptions. See LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// ------------------------------------------------------------------------- + +// core.DivideZero (C, C++, ObjC) +// Check for division by zero. + +void div(int x) { + int y = x % 0; // warn +} diff --git a/web/tests/functional/cppcheck/test_cppcheck.py b/web/tests/functional/cppcheck/test_cppcheck.py index e48eb5ace3..0b4c23a6fb 100644 --- a/web/tests/functional/cppcheck/test_cppcheck.py +++ b/web/tests/functional/cppcheck/test_cppcheck.py @@ -62,7 +62,6 @@ def setup_class(self): def teardown_class(self): """Clean up after the test.""" - # TODO: If environment variable is set keep the workspace # and print out the path. global TEST_WORKSPACE @@ -78,18 +77,17 @@ def setup_method(self, _): # Get the test workspace used to cppcheck tests. self._test_workspace = os.environ['TEST_WORKSPACE'] - test_class = self.__class__.__name__ print('Running ' + test_class + ' tests in ' + self._test_workspace) self._test_cfg = env.import_test_cfg(self._test_workspace) def test_cppcheck_report_storage(self): - """ In the stored report not the default zero hash should be used. """ + """ Test storing a Codechecker generated, CppCheck analyzed plist. """ test_dir = os.path.dirname(os.path.realpath(__file__)) - report_dir = os.path.join(test_dir, 'test_proj') + report_dir = os.path.join(test_dir, 'correct_test_proj') codechecker_cfg = self._test_cfg['codechecker_cfg'] @@ -97,10 +95,12 @@ def test_cppcheck_report_storage(self): # files in the repository. # Report files will be overwritten during the tests. temp_workspace = os.path.join(codechecker_cfg['workspace'], - 'test_proj') + 'correct_test_proj') + shutil.copytree(report_dir, temp_workspace) - report_file = os.path.join(temp_workspace, 'divide_zero.plist') + report_file = os.path.join(temp_workspace, + 'divide_zero.plist') # Convert file paths to absolute in the report. plist_test.prefix_file_path(report_file, temp_workspace) @@ -111,7 +111,7 @@ def test_cppcheck_report_storage(self): temp_workspace] out = subprocess.check_output( - store_cmd, encoding="utf-8", errors="ignore") + store_cmd, encoding="utf-8") print(out) query_cmd = [env.codechecker_cmd(), 'cmd', 'results', run_name, # Use the 'Default' product. @@ -121,11 +121,50 @@ def test_cppcheck_report_storage(self): query_cmd, encoding="utf-8", errors="ignore") print(out) reports = json.loads(out) - self.assertEqual(len(reports), 5) + self.assertEqual(len(reports), 2) for report in reports: - # The stored hash should not be "0". - self.assertNotEqual(report["bugHash"], "0") # The stored checker name should not be the fake(d) default that # was created because no 'metadata.json' (and thus no checker # list) exists for this "project". self.assertNotEqual(report["checkerId"], "__FAKE__") + + def test_cppcheck_0_hash(self): + """ Test storing a CppCheck generated plist that contains 0 as Hash""" + + test_dir = os.path.dirname(os.path.realpath(__file__)) + + report_dir = os.path.join(test_dir, '0_hashes_test_proj') + + codechecker_cfg = self._test_cfg['codechecker_cfg'] + + # Copy report files to a temporary directory not to modify the + # files in the repository. + # Report files will be overwritten during the tests. + temp_workspace = os.path.join(codechecker_cfg['workspace'], + '0_hashes_test_proj') + + shutil.copytree(report_dir, temp_workspace) + + report_file = os.path.join(temp_workspace, + 'divide_zero_0_hashes.plist') + # Convert file paths to absolute in the report. + plist_test.prefix_file_path(report_file, temp_workspace) + + run_name = 'cppcheck' + store_cmd = [env.codechecker_cmd(), 'store', '--name', run_name, + # Use the 'Default' product. + '--url', env.parts_to_url(codechecker_cfg), + temp_workspace] + # As the plist file contains 0 as Hash the expected + # behaviour is to give the following error + # and terminate with error code 1. + expected_log = ( + "All bug hashes are 0 in the report file, which is no " + "longer supported. Please re-analyze the project using " + "CodeChecker!") + result = subprocess.run( + store_cmd, encoding="utf-8", + errors="ignore", check=False, + capture_output=True) + self.assertIn(expected_log, result.stdout) + self.assertEqual(result.returncode, 1)