diff --git a/analyzer/codechecker_analyzer/analysis_manager.py b/analyzer/codechecker_analyzer/analysis_manager.py index 101f97e54c..39669a98de 100644 --- a/analyzer/codechecker_analyzer/analysis_manager.py +++ b/analyzer/codechecker_analyzer/analysis_manager.py @@ -20,6 +20,7 @@ import zipfile from threading import Timer +from pathlib import Path import multiprocess import psutil @@ -712,8 +713,10 @@ def skip_cpp(compile_actions, skip_handlers): analyze = [] skip = [] for compile_action in compile_actions: - - if skip_handlers and skip_handlers.should_skip(compile_action.source): + if skip_handlers and \ + skip_handlers.should_skip( + str(Path(Path(compile_action.directory).resolve(), + compile_action.source))): skip.append(compile_action) else: analyze.append(compile_action) diff --git a/analyzer/codechecker_analyzer/buildlog/log_parser.py b/analyzer/codechecker_analyzer/buildlog/log_parser.py index 6be2c9e308..3fecf522c4 100644 --- a/analyzer/codechecker_analyzer/buildlog/log_parser.py +++ b/analyzer/codechecker_analyzer/buildlog/log_parser.py @@ -11,6 +11,7 @@ # pylint: disable=no-name-in-module from distutils.spawn import find_executable from enum import Enum +from pathlib import Path import glob import json @@ -1290,7 +1291,9 @@ def parse_unique_log(compilation_database, # Skipping of the compile commands is done differently if no # CTU or statistics related feature was enabled. if analysis_skip_handlers \ - and analysis_skip_handlers.should_skip(entry['file']) \ + and analysis_skip_handlers.should_skip( + str(Path(Path(entry["directory"]).resolve(), + entry["file"]))) \ and (not ctu_or_stats_enabled or pre_analysis_skip_handlers and pre_analysis_skip_handlers.should_skip( entry['file'])): diff --git a/analyzer/tests/functional/skip/test_files/multidir/b.cpp b/analyzer/tests/functional/skip/test_files/multidir/b.cpp new file mode 100644 index 0000000000..c1b7e271dc --- /dev/null +++ b/analyzer/tests/functional/skip/test_files/multidir/b.cpp @@ -0,0 +1,3 @@ +int root(){ + return 1/0; +} diff --git a/analyzer/tests/functional/skip/test_files/multidir/compile_commnands.json b/analyzer/tests/functional/skip/test_files/multidir/compile_commnands.json new file mode 100644 index 0000000000..4654effc5a --- /dev/null +++ b/analyzer/tests/functional/skip/test_files/multidir/compile_commnands.json @@ -0,0 +1,25 @@ +[ + { + "directory": ".", + "command": "/usr/bin/g++ -c b.cpp -o /dev/null", + "file": "b.cpp" + } + , + { + "directory": ".", + "command": "/usr/bin/g++ -c lib/lib.cpp -o /dev/null", + "file": "lib/lib.cpp" + } + , + { + "directory": ".", + "command": "/usr/bin/g++ -c src/a.cpp -o /dev/null", + "file": "src/a.cpp" + } + , + { + "directory": ".", + "command": "/usr/bin/g++ -c src/b.cpp -o /dev/null", + "file": "src/b.cpp" + } +] diff --git a/analyzer/tests/functional/skip/test_files/multidir/lib/lib.cpp b/analyzer/tests/functional/skip/test_files/multidir/lib/lib.cpp index 55adf91564..cc9f57dc82 100644 --- a/analyzer/tests/functional/skip/test_files/multidir/lib/lib.cpp +++ b/analyzer/tests/functional/skip/test_files/multidir/lib/lib.cpp @@ -1,4 +1,8 @@ #include "../include/lib.h" +/* + * The error in this file should only be reported in + * CTU mode, through a.cpp. + */ int myDiv(int x) { diff --git a/analyzer/tests/functional/skip/test_files/multidir/src/a.cpp b/analyzer/tests/functional/skip/test_files/multidir/src/a.cpp index f48738a98c..26d8ef6096 100644 --- a/analyzer/tests/functional/skip/test_files/multidir/src/a.cpp +++ b/analyzer/tests/functional/skip/test_files/multidir/src/a.cpp @@ -1,5 +1,8 @@ #include "../include/lib.h" void foo() { + int* p = new int(0); + delete p; + delete p; myDiv(0); } diff --git a/analyzer/tests/functional/skip/test_skip.py b/analyzer/tests/functional/skip/test_skip.py index 885d4dccbd..90a183435b 100644 --- a/analyzer/tests/functional/skip/test_skip.py +++ b/analyzer/tests/functional/skip/test_skip.py @@ -270,7 +270,7 @@ def test_analyze_header_with_file_option(self): self.assertTrue(plist_files) self.assertTrue(all('skip_header.cpp' in f for f in plist_files)) - def test_analyze_header_with_file_option_and_intercept_json(self): + def test_analyze_header_with_file_opt_and_intercept_json(self): """ Analyze a header file with the --file option and a compilation database produced by intercept build. @@ -302,7 +302,7 @@ def test_analyze_header_with_file_option_and_intercept_json(self): self.assertTrue(plist_files) self.assertTrue(all('skip_header.cpp' in f for f in plist_files)) - def test_analyze_file_option_skip_everything(self): + def test_analyze_file_opt_skip_everything(self): """ Test analyze command --file option when everything is skipped by a skipfile. @@ -417,18 +417,310 @@ def test_parse_file_option(self): r['file']['original_path'].endswith('/skip_header.cpp') for r in data['reports'])) - def test_analyze_file_option_with_path_prefixes(self): - """ Analyze a header file with the --file option. """ - out, _ = self.__log_and_analyze( - proj="multidir", - analyzer_extra_options=["--file", "*/src/b.cpp"]) + def _test_analyze_file_option(self, + project, + file_option, + expected_files_analyze, + prohibited_files_analyze, + expected_files_parse, + prohibited_files_parse): + """ + Analyze a source file with the --file option. + """ + out, _ = self.__analyze( + "compile_commnands.json", + project, + ["--file", file_option]) + + for expected_file in expected_files_analyze: + self.assertIn(f"analyzed {expected_file} successfully.", out) + + for prohibited_file in prohibited_files_analyze: + self.assertNotIn(f"analyzed {prohibited_file} successfully.", out) - # Only src/b.cpp should be analyzed. - self.assertIn("analyzed b.cpp successfully.", out) - self.assertNotIn("analyzed a.cpp successfully.", out) - self.assertNotIn("analyzed lib.cpp successfully.", out) out, _, _ = self.__run_parse() - # Only reports from b.cpp should be present in the report folder. - self.assertIn("b.cpp", out) - self.assertNotIn("a.cpp", out) - self.assertNotIn("lib.cpp", out) + + for expected_file in expected_files_parse: + self.assertIn(expected_file, out) + + for prohibited_file in prohibited_files_parse: + self.assertNotIn(prohibited_file, out) + + def test_analyze_file_opt_abs(self): + """ + "/ABS/PATH/TO/multidir/src/b.cpp" -> "src/b.cpp" + """ + filter_expr = os.path.join(self.test_dir, "multidir", "src", "b.cpp") + filter_expr = os.path.abspath(filter_expr) + self._test_analyze_file_option( + project="multidir", + file_option=filter_expr, + expected_files_analyze=["b.cpp"], + prohibited_files_analyze=["a.cpp", "lib.cpp"], + expected_files_parse=["src/b.cpp"], + prohibited_files_parse=["multidir/b.cpp", + "src/a.cpp", + "lib/lib.cpp"] + ) + + def test_analyze_file_opt_wc(self): + """ + "*" -> "multidir/b.cpp", "src/a.cpp", "src/b.cpp", "lib/lib.cpp" + """ + self._test_analyze_file_option( + project="multidir", + file_option="*", + expected_files_analyze=["a.cpp", "b.cpp", "lib.cpp"], + prohibited_files_analyze=[], + expected_files_parse=["multidir/b.cpp", "src/a.cpp", "src/b.cpp"], + prohibited_files_parse=[] + ) + + def test_analyze_file_opt_wc_ext(self): + """ + "*.cpp" -> "multidir/b.cpp", "src/a.cpp", "src/b.cpp", "lib/lib.cpp" + """ + self._test_analyze_file_option( + project="multidir", + file_option="*.cpp", + expected_files_analyze=["a.cpp", "b.cpp", "lib.cpp"], + prohibited_files_analyze=[], + expected_files_parse=["multidir/b.cpp", "src/a.cpp", "src/b.cpp"], + prohibited_files_parse=[] + ) + + def test_analyze_file_opt_cwd_wc_ext(self): + """ + "./*.cpp" -> "multidir/b.cpp", "src/a.cpp", "src/b.cpp", "lib/lib.cpp" + """ + self._test_analyze_file_option( + project="multidir", + file_option="./*.cpp", + expected_files_analyze=["a.cpp", "b.cpp", "lib.cpp"], + prohibited_files_analyze=[], + expected_files_parse=["multidir/b.cpp", "src/a.cpp", "src/b.cpp"], + prohibited_files_parse=[] + ) + + def test_analyze_file_opt_filename_no_slash(self): + """ + "b.cpp" -> "b.cpp" + """ + self._test_analyze_file_option( + project="multidir", + file_option="b.cpp", + expected_files_analyze=["b.cpp"], + prohibited_files_analyze=["a.cpp", "lib.cpp"], + expected_files_parse=["multidir/b.cpp"], + prohibited_files_parse=["src/a.cpp", "src/b.cpp", "lib/lib.cpp"] + ) + + def test_analyze_file_opt_filename(self): + """ + "./b.cpp" -> "b.cpp" + """ + self._test_analyze_file_option( + project="multidir", + file_option="./b.cpp", + expected_files_analyze=["b.cpp"], + prohibited_files_analyze=["a.cpp", "lib.cpp"], + expected_files_parse=["multidir/b.cpp"], + prohibited_files_parse=["src/a.cpp", "src/b.cpp", "lib/lib.cpp"] + ) + + def test_analyze_file_opt_wc_filename(self): + """ + "*b.cpp" -> "multidir/b.cpp", "src/b.cpp" + """ + self._test_analyze_file_option( + project="multidir", + file_option="*/b.cpp", + expected_files_analyze=["b.cpp"], + prohibited_files_analyze=["a.cpp", "lib.cpp"], + expected_files_parse=["src/b.cpp"], + prohibited_files_parse=["src/a.cpp", "lib/lib.cpp"] + ) + + def test_analyze_file_opt_wc_sep_filename(self): + """ + "*/b.cpp" -> "src/b.cpp" + """ + self._test_analyze_file_option( + project="multidir", + file_option="*/b.cpp", + expected_files_analyze=["b.cpp"], + prohibited_files_analyze=["a.cpp", "lib.cpp"], + expected_files_parse=["src/b.cpp"], + prohibited_files_parse=["src/a.cpp", + "multidir/b.cpp", + "lib/lib.cpp"] + ) + + def test_analyze_file_pathpref_filename(self): + """ + "src/b.cpp" -> "src/b.cpp" + """ + self._test_analyze_file_option( + project="multidir", + file_option="src/b.cpp", + expected_files_analyze=["b.cpp"], + prohibited_files_analyze=["a.cpp", "lib.cpp"], + expected_files_parse=["src/b.cpp"], + prohibited_files_parse=["src/a.cpp", "lib/lib.cpp"] + ) + + def test_analyze_file_opt_dot_pathpref_filename(self): + """ + "./src/b.cpp" -> "src/b.cpp" + """ + self._test_analyze_file_option( + project="multidir", + file_option="./src/b.cpp", + expected_files_analyze=["b.cpp"], + prohibited_files_analyze=["a.cpp", "lib.cpp"], + expected_files_parse=["src/b.cpp"], + prohibited_files_parse=["src/a.cpp", "lib/lib.cpp"] + ) + + def test_analyze_file_opt_wc_pathpref_filename(self): + """ + "*src/b.cpp" -> "src/b.cpp" + """ + self._test_analyze_file_option( + project="multidir", + file_option="*src/b.cpp", + expected_files_analyze=["b.cpp"], + prohibited_files_analyze=["a.cpp", "lib.cpp"], + expected_files_parse=["src/b.cpp"], + prohibited_files_parse=["src/a.cpp", "lib/lib.cpp"] + ) + + def test_analyze_file_opt_wc_sep_pathpref_filename(self): + """ + "*/src/b.cpp" -> "src/b.cpp" + """ + self._test_analyze_file_option( + project="multidir", + file_option="*/src/b.cpp", + expected_files_analyze=["b.cpp"], + prohibited_files_analyze=["a.cpp", "lib.cpp"], + expected_files_parse=["src/b.cpp"], + prohibited_files_parse=["src/a.cpp", "lib/lib.cpp"] + ) + + def test_analyze_file_opt_pathpref_sep_wc(self): + """ + "src/*" -> "src/a.cpp", "src/b.cpp" + """ + self._test_analyze_file_option( + project="multidir", + file_option="src/*", + expected_files_analyze=["a.cpp", "b.cpp"], + prohibited_files_analyze=["lib.cpp"], + expected_files_parse=["src/a.cpp", "src/b.cpp"], + prohibited_files_parse=["multidir/b.cpp", "lib/lib.cpp"] + ) + + def test_analyze_file_opt_cwd_pathpref_sep_wc(self): + """ + "./src/*" -> "src/a.cpp", "src/b.cpp" + """ + self._test_analyze_file_option( + project="multidir", + file_option="./src/*", + expected_files_analyze=["a.cpp", "b.cpp"], + prohibited_files_analyze=["lib.cpp"], + expected_files_parse=["src/a.cpp", "src/b.cpp"], + prohibited_files_parse=["multidir/b.cpp", "lib/lib.cpp"] + ) + + def test_analyze_file_wc_sep_pathpref_sep_wc(self): + """ + "*/src/*" -> "src/a.cpp", "src/b.cpp" + """ + self._test_analyze_file_option( + project="multidir", + file_option="*/src/*", + expected_files_analyze=["a.cpp", "b.cpp"], + prohibited_files_analyze=["lib.cpp"], + expected_files_parse=["src/a.cpp", "src/b.cpp"], + prohibited_files_parse=["multidir/b.cpp", "lib/lib.cpp"] + ) + + def test_analyze_file_opt_wc_pathpref_sep_wc(self): + """ + "*src/*" -> "src/a.cpp", "src/b.cpp" + """ + self._test_analyze_file_option( + project="multidir", + file_option="*src/*", + expected_files_analyze=["a.cpp", "b.cpp"], + prohibited_files_analyze=["lib.cpp"], + expected_files_parse=["src/a.cpp", "src/b.cpp"], + prohibited_files_parse=["multidir/b.cpp", "lib/lib.cpp"] + ) + + def test_analyze_file_opt_pathpref_sep_wc_ext(self): + """ + "src/*.cpp" -> "src/a.cpp", "src/b.cpp" + """ + self._test_analyze_file_option( + project="multidir", + file_option="src/*.cpp", + expected_files_analyze=["a.cpp", "b.cpp"], + prohibited_files_analyze=["lib.cpp"], + expected_files_parse=["src/a.cpp", "src/b.cpp"], + prohibited_files_parse=["multidir/b.cpp", "lib/lib.cpp"] + ) + + def test_analyze_file_opt_cwd_pathpref_sep_wc_ext(self): + """ + "./src/*.cpp" -> "src/a.cpp", "src/b.cpp" + """ + self._test_analyze_file_option( + project="multidir", + file_option="./src/*.cpp", + expected_files_analyze=["a.cpp", "b.cpp"], + prohibited_files_analyze=["lib.cpp"], + expected_files_parse=["src/a.cpp", "src/b.cpp"], + prohibited_files_parse=["multidir/b.cpp", "lib/lib.cpp"] + ) + + def test_analyze_file_wc_sep_pathpref_sep_wc_ext(self): + """ + "*/src/*.cpp" -> "src/a.cpp", "src/b.cpp" + """ + self._test_analyze_file_option( + project="multidir", + file_option="*/src/*.cpp", + expected_files_analyze=["a.cpp", "b.cpp"], + prohibited_files_analyze=["lib.cpp"], + expected_files_parse=["src/a.cpp", "src/b.cpp"], + prohibited_files_parse=["multidir/b.cpp", "lib/lib.cpp"] + ) + + def test_analyze_file_opt_wc_pathpref_sep_wc_ext(self): + """ + "*src/*.cpp" -> "src/a.cpp", "src/b.cpp" + """ + self._test_analyze_file_option( + project="multidir", + file_option="*src/*.cpp", + expected_files_analyze=["a.cpp", "b.cpp"], + prohibited_files_analyze=["lib.cpp"], + expected_files_parse=["src/a.cpp", "src/b.cpp"], + prohibited_files_parse=["multidir/b.cpp", "lib/lib.cpp"] + ) + + def test_analyze_file_opt_partialpathpref_filename(self): + """ + "*rc/b.cpp" -> "src/b.cpp" + """ + self._test_analyze_file_option( + project="multidir", + file_option="*rc/b.cpp", + expected_files_analyze=["b.cpp"], + prohibited_files_analyze=["a.cpp", "lib.cpp"], + expected_files_parse=["src/b.cpp"], + prohibited_files_parse=["src/a.cpp", "lib/lib.cpp"] + ) diff --git a/codechecker_common/skiplist_handler.py b/codechecker_common/skiplist_handler.py index 2c07c3a46a..d9a1c75864 100644 --- a/codechecker_common/skiplist_handler.py +++ b/codechecker_common/skiplist_handler.py @@ -10,11 +10,11 @@ import fnmatch -import re -import os from codechecker_common.logger import get_logger +from pathlib import Path + LOG = get_logger('system') @@ -53,10 +53,9 @@ def __gen_regex(self, skip_lines): the regular expressions. """ for skip_line in skip_lines: - norm_skip_path = os.path.normpath(skip_line[1:].strip()) - rexpr = re.compile( - fnmatch.translate('*' + norm_skip_path + '*')) - self.__skip.append((skip_line, rexpr)) + LOG.info("Processing skip line: %s", skip_line) + matcher = skip_line + self.__skip.append((skip_line, matcher)) def __check_line_format(self, skip_lines): """ @@ -97,13 +96,22 @@ def should_skip(self, source): if not self.__skip: return False - for line, rexpr in self.__skip: - if rexpr.match(source): - sign = line[0] + path = Path(source).resolve() + for filter, _ in self.__skip: + print(path, filter) + sign = filter[0] + filter = filter[1:] + if filter.startswith("*/") and "/" in filter[2:]: + print("before replace", filter) + filter = filter[0] + filter[2:] + print("replaced filter", filter) + else: + filter = Path(filter).resolve() + print("resolved filter", filter) + if fnmatch.fnmatch(str(path), str(filter)): return sign == '-' return False - class SkipListHandlers(list): def should_skip(self, file_path: str): """