diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 00e8ffca07..860dc1b530 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,6 +5,10 @@ on: [push, pull_request] permissions: read-all +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: # Note: UI related linter tests will run in the gui job. lint: diff --git a/analyzer/codechecker_analyzer/analyzer.py b/analyzer/codechecker_analyzer/analyzer.py index 062c770731..9b83037315 100644 --- a/analyzer/codechecker_analyzer/analyzer.py +++ b/analyzer/codechecker_analyzer/analyzer.py @@ -142,10 +142,8 @@ def perform_analysis(args, skip_handlers, rs_handler: ReviewStatusHandler, ctu_reanalyze_on_failure = 'ctu_reanalyze_on_failure' in args and \ args.ctu_reanalyze_on_failure - analyzers = args.analyzers if 'analyzers' in args \ - else analyzer_types.supported_analyzers - analyzers, errored = analyzer_types.check_supported_analyzers(analyzers) - analyzer_types.check_available_analyzers(analyzers, errored) + analyzers, errored = \ + analyzer_types.check_available_analyzers(args.analyzers) ctu_collect = False ctu_analyze = False @@ -248,9 +246,8 @@ def perform_analysis(args, skip_handlers, rs_handler: ReviewStatusHandler, if state == CheckerState.ENABLED: enabled_checkers[analyzer].append(check) - # TODO: cppcheck may require a different environment than clang. version = analyzer_types.supported_analyzers[analyzer] \ - .get_binary_version(context.analyzer_env) + .get_binary_version() metadata_info['analyzer_statistics']['version'] = version metadata_tool['analyzers'][analyzer] = metadata_info @@ -358,7 +355,8 @@ def perform_analysis(args, skip_handlers, rs_handler: ReviewStatusHandler, end_time = time.time() LOG.info("Analysis length: %s sec.", end_time - start_time) - analyzer_types.print_unsupported_analyzers(errored) + if args.analyzers: + analyzer_types.print_unsupported_analyzers(errored) metadata_tool['timestamps'] = {'begin': start_time, 'end': end_time} diff --git a/analyzer/codechecker_analyzer/analyzer_context.py b/analyzer/codechecker_analyzer/analyzer_context.py index 082c508a20..16babf7b89 100644 --- a/analyzer/codechecker_analyzer/analyzer_context.py +++ b/analyzer/codechecker_analyzer/analyzer_context.py @@ -14,16 +14,14 @@ from argparse import ArgumentTypeError import os -import platform import sys -from pathlib import Path - from codechecker_analyzer.arg import analyzer_binary from codechecker_common import logger from codechecker_common.checker_labels import CheckerLabels from codechecker_common.singleton import Singleton from codechecker_common.util import load_json +from pathlib import Path from . import env @@ -65,20 +63,20 @@ def __init__(self): self.__package_build_date = None self.__package_git_hash = None self.__analyzers = {} - self.__analyzer_env = None - machine = platform.uname().machine + # CodeChecker's current runtime environment + self.__cc_env = None + # cc_env extended with packaged LD_LIBRARY_PATH for packaged binaries + self.__package_env = None + # Original caller environment of CodeChecker for external binaries + self.__original_env = None self.logger_lib_dir_path = os.path.join( - self._data_files_dir_path, 'ld_logger', 'lib', machine) + self._data_files_dir_path, 'ld_logger', 'lib') if not os.path.exists(self.logger_lib_dir_path): self.logger_lib_dir_path = os.path.join( - self._lib_dir_path, - 'codechecker_analyzer', - 'ld_logger', - 'lib', - machine) + self._lib_dir_path, 'codechecker_analyzer', 'ld_logger', 'lib') self.logger_bin = None self.logger_file = None @@ -93,9 +91,9 @@ def __init__(self): def __parse_cc_analyzer_bin(self): env_var_bins = {} - if 'CC_ANALYZER_BIN' in self.analyzer_env: + if 'CC_ANALYZER_BIN' in self.cc_env: had_error = False - for value in self.__analyzer_env['CC_ANALYZER_BIN'].split(';'): + for value in self.__cc_env['CC_ANALYZER_BIN'].split(';'): try: analyzer_name, path = analyzer_binary(value) except ArgumentTypeError as e: @@ -163,6 +161,9 @@ def __init_env(self): self.env_vars['cc_logger_compiles']) self.ld_preload = os.environ.get(self.env_vars['ld_preload']) self.ld_lib_path = self.env_vars['env_ld_lib_path'] + self.__original_env = env.get_original_env() + self.__package_env = env.extend(self.path_env_extra, + self.ld_lib_path_extra) def __set_version(self): """ @@ -194,18 +195,42 @@ def __set_version(self): logger.DEBUG_ANALYZER): self.__package_git_tag = package_git_dirtytag + def get_env_for_bin(self, binary): + """ + binary must be a binary with full path + + Returns the correct environment for binaries called by CodeChecker. + For binaries packaged with CodeChecker the LD_LIBRARY path is extended. + For non-packaged binaries, the original calling environment + is returned. + """ + bin_path = Path(binary).resolve() + if not bin_path.exists(): + LOG.error("Binary %s not found", binary) + return None + + codechecker_dir = Path(self._data_files_dir_path) + + if str(bin_path).startswith(str(codechecker_dir)): + LOG.debug("Package env is returned for %s", bin_path) + return self.__package_env + else: + LOG.debug("Original env is returned for %s", bin_path) + return self.__original_env + def __populate_analyzers(self): """ Set analyzer binaries for each registered analyzers. """ - analyzer_env = None + cc_env = None analyzer_from_path = env.is_analyzer_from_path() if not analyzer_from_path: - analyzer_env = self.analyzer_env + cc_env = self.cc_env env_var_bin = self.__parse_cc_analyzer_bin() compiler_binaries = self.pckg_layout.get('analyzers') for name, value in compiler_binaries.items(): if name in env_var_bin: + # env_var_bin has priority over package config and PATH self.__analyzers[name] = env_var_bin[name] continue @@ -217,7 +242,7 @@ def __populate_analyzers(self): self.__analyzers[name] = os.path.join( self._data_files_dir_path, value) else: - env_path = analyzer_env['PATH'] if analyzer_env else None + env_path = cc_env['PATH'] if cc_env else None compiler_binary = which(cmd=value, path=env_path) if not compiler_binary: LOG.debug("'%s' binary can not be found in your PATH!", @@ -281,12 +306,8 @@ def path_logger_bin(self): return os.path.join(self._bin_dir_path, 'ld_logger') @property - def logger_lib_path(self): - """ - Returns the absolute path to the logger library. - """ - return str(Path(self.logger_lib_dir_path, - self.logger_lib_name).absolute()) + def path_logger_lib(self): + return self.logger_lib_dir_path @property def logger_lib_name(self): @@ -320,11 +341,10 @@ def ld_lib_path_extra(self): return ld_paths @property - def analyzer_env(self): - if not self.__analyzer_env: - self.__analyzer_env = \ - env.extend(self.path_env_extra, self.ld_lib_path_extra) - return self.__analyzer_env + def cc_env(self): + if not self.__cc_env: + self.__cc_env = os.environ.copy() + return self.__cc_env @property def analyzer_binaries(self): diff --git a/analyzer/codechecker_analyzer/analyzers/analyzer_base.py b/analyzer/codechecker_analyzer/analyzers/analyzer_base.py index d6d92b5aa8..a510db4634 100644 --- a/analyzer/codechecker_analyzer/analyzers/analyzer_base.py +++ b/analyzer/codechecker_analyzer/analyzers/analyzer_base.py @@ -58,7 +58,7 @@ def resolve_missing_binary(cls, configured_binary, environ): @classmethod @abstractmethod - def get_binary_version(cls, environ, details=False) -> str: + def get_binary_version(cls, details=False) -> str: """ Return the version number of the binary that CodeChecker found, even if its incompatible. If details is true, additional version information @@ -68,7 +68,7 @@ def get_binary_version(cls, environ, details=False) -> str: raise NotImplementedError("Subclasses should implement this!") @classmethod - def is_binary_version_incompatible(cls, environ) -> Optional[str]: + def is_binary_version_incompatible(cls) -> Optional[str]: """ CodeChecker can only execute certain versions of analyzers. Returns a error object (an optional string). If the return value is @@ -102,7 +102,7 @@ def construct_result_handler(self, buildaction, report_output, """ raise NotImplementedError("Subclasses should implement this!") - def analyze(self, analyzer_cmd, res_handler, proc_callback=None, env=None): + def analyze(self, analyzer_cmd, res_handler, proc_callback=None): """ Run the analyzer. """ @@ -116,8 +116,7 @@ def analyze(self, analyzer_cmd, res_handler, proc_callback=None, env=None): ret_code, stdout, stderr \ = SourceAnalyzer.run_proc(analyzer_cmd, res_handler.buildaction.directory, - proc_callback, - env) + proc_callback) res_handler.analyzer_returncode = ret_code res_handler.analyzer_stdout = stdout res_handler.analyzer_stderr = stderr @@ -141,7 +140,7 @@ def post_analyze(self, result_handler): """ @staticmethod - def run_proc(command, cwd=None, proc_callback=None, env=None): + def run_proc(command, cwd=None, proc_callback=None): """ Just run the given command and return the return code and the stdout and stderr outputs of the process. @@ -157,8 +156,11 @@ def signal_handler(signum, _): signal.signal(signal.SIGINT, signal_handler) - if env is None: - env = analyzer_context.get_context().analyzer_env + env = analyzer_context.get_context().get_env_for_bin(command[0]) + + LOG.debug('\nexecuting:%s\n', command) + LOG.debug('\nENV:\n') + LOG.debug(env) proc = subprocess.Popen( command, diff --git a/analyzer/codechecker_analyzer/analyzers/analyzer_types.py b/analyzer/codechecker_analyzer/analyzers/analyzer_types.py index 6e8a492ecf..130288c728 100644 --- a/analyzer/codechecker_analyzer/analyzers/analyzer_types.py +++ b/analyzer/codechecker_analyzer/analyzers/analyzer_types.py @@ -104,7 +104,9 @@ def is_ignore_conflict_supported(): proc = subprocess.Popen([context.replacer_binary, '--help'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, - env=context.analyzer_env, + env=context + .get_env_for_bin( + context.replacer_binary), encoding="utf-8", errors="ignore") out, _ = proc.communicate() return '--ignore-insert-conflict' in out @@ -113,22 +115,35 @@ def is_ignore_conflict_supported(): def print_unsupported_analyzers(errored): """ Print error messages which occured during analyzer detection. """ for analyzer_binary, reason in errored: - LOG.warning("Analyzer '%s' is enabled but CodeChecker is failed to " + LOG.warning("Analyzer '%s' is enabled but CodeChecker failed to " "execute analysis with it: '%s'. Please check your " "'PATH' environment variable and the " "'config/package_layout.json' file!", analyzer_binary, reason) -def check_available_analyzers(analyzers, errored): - """ Handle use case when no analyzer can be found on the user machine. """ - if analyzers: - return +def check_available_analyzers(args_analyzers=None): + """ + Handle use case when no analyzer can be found or a supported, explicitly + given analyzer cannot be found on the user machine. + """ - print_unsupported_analyzers(errored) - LOG.error("Failed to run command because no analyzers can be found on " - "your machine!") - sys.exit(1) + if args_analyzers: + analyzers, errored = check_supported_analyzers(args_analyzers) + if errored: + print_unsupported_analyzers(errored) + LOG.error("Failed to run command because the given analyzer(s) " + "cannot be found on your machine!") + sys.exit(1) + + else: + analyzers, errored = check_supported_analyzers(supported_analyzers) + if not analyzers: + print_unsupported_analyzers(errored) + LOG.error("Failed to run command because no analyzers can be " + "found on your machine!") + sys.exit(1) + return analyzers, errored def check_supported_analyzers(analyzers): @@ -144,7 +159,6 @@ def check_supported_analyzers(analyzers): """ context = analyzer_context.get_context() - check_env = context.analyzer_env analyzer_binaries = context.analyzer_binaries @@ -167,7 +181,8 @@ def check_supported_analyzers(analyzers): elif not os.path.isabs(analyzer_bin): # If the analyzer is not in an absolute path, try to find it... found_bin = supported_analyzers[analyzer_name].\ - resolve_missing_binary(analyzer_bin, check_env) + resolve_missing_binary(analyzer_bin, + context.get_env_for_bin(analyzer_bin)) # found_bin is an absolute path, an executable in one of the # PATH folders. @@ -186,7 +201,7 @@ def check_supported_analyzers(analyzers): # Check version compatibility of the analyzer binary. if analyzer_bin: analyzer = supported_analyzers[analyzer_name] - error = analyzer.is_binary_version_incompatible(check_env) + error = analyzer.is_binary_version_incompatible() if error: failed_analyzers.add((analyzer_name, f"Incompatible version: {error} " @@ -196,7 +211,7 @@ def check_supported_analyzers(analyzers): available_analyzer = False if not analyzer_bin or \ - not host_check.check_analyzer(analyzer_bin, check_env): + not host_check.check_analyzer(analyzer_bin): # Analyzers unavailable under absolute paths are deliberately a # configuration problem. failed_analyzers.add((analyzer_name, diff --git a/analyzer/codechecker_analyzer/analyzers/clangsa/analyzer.py b/analyzer/codechecker_analyzer/analyzers/clangsa/analyzer.py index 6bb89697c7..d31a1ce40c 100644 --- a/analyzer/codechecker_analyzer/analyzers/clangsa/analyzer.py +++ b/analyzer/codechecker_analyzer/analyzers/clangsa/analyzer.py @@ -51,7 +51,8 @@ def parse_clang_help_page( help_page = subprocess.check_output( command, stderr=subprocess.STDOUT, - env=analyzer_context.get_context().analyzer_env, + env=analyzer_context.get_context() + .get_env_for_bin(command[0]), universal_newlines=True, encoding="utf-8", errors="ignore") @@ -171,8 +172,11 @@ def __add_plugin_load_flags(cls, analyzer_cmd: List[str]): analyzer_cmd.extend(["-load", plugin]) @classmethod - def get_binary_version(cls, environ, details=False) -> str: + def get_binary_version(cls, details=False) -> str: # No need to LOG here, we will emit a warning later anyway. + + environ = analyzer_context.get_context().get_env_for_bin( + cls.analyzer_binary()) if not cls.analyzer_binary(): return None @@ -207,7 +211,8 @@ def ctu_capability(cls): if not cls.__ctu_autodetection: cls.__ctu_autodetection = CTUAutodetection( cls.analyzer_binary(), - analyzer_context.get_context().analyzer_env) + analyzer_context.get_context() + .get_env_for_bin(cls.analyzer_binary())) return cls.__ctu_autodetection @@ -473,7 +478,8 @@ def construct_analyzer_cmd(self, result_handler): analyzer_cmd.extend(self.buildaction.analyzer_options) analyzer_cmd.extend(prepend_all( - '-isystem', + '-isystem' if config.add_gcc_include_dirs_with_isystem else + '-idirafter', self.buildaction.compiler_includes)) analyzer_cmd.append(self.source_file) @@ -584,7 +590,7 @@ def resolve_missing_binary(cls, configured_binary, environ): return clang @classmethod - def is_binary_version_incompatible(cls, environ): + def is_binary_version_incompatible(cls): """ We support pretty much every ClangSA version. """ @@ -606,7 +612,8 @@ def construct_result_handler(self, buildaction, report_output, def construct_config_handler(cls, args): context = analyzer_context.get_context() - environ = context.analyzer_env + environ = context.get_env_for_bin( + cls.analyzer_binary()) handler = config_handler.ClangSAConfigHandler(environ) @@ -618,6 +625,10 @@ def construct_config_handler(cls, args): handler.enable_z3_refutation = 'enable_z3_refutation' in args and \ args.enable_z3_refutation == 'on' + handler.add_gcc_include_dirs_with_isystem = \ + 'add_gcc_include_dirs_with_isystem' in args and \ + args.add_gcc_include_dirs_with_isystem + if 'ctu_phases' in args: handler.ctu_dir = os.path.join(args.output_path, args.ctu_dir) diff --git a/analyzer/codechecker_analyzer/analyzers/clangsa/ctu_autodetection.py b/analyzer/codechecker_analyzer/analyzers/clangsa/ctu_autodetection.py index dacf46fa90..d418b45419 100644 --- a/analyzer/codechecker_analyzer/analyzers/clangsa/ctu_autodetection.py +++ b/analyzer/codechecker_analyzer/analyzers/clangsa/ctu_autodetection.py @@ -13,6 +13,7 @@ import subprocess from codechecker_common.logger import get_logger +from codechecker_analyzer import analyzer_context from codechecker_analyzer import host_check from codechecker_analyzer.analyzers.clangsa import version @@ -75,7 +76,7 @@ def ctu_mapping(clang_version_info): return None, None -def invoke_binary_checked(binary_path, args=None, environ=None): +def invoke_binary_checked(binary_path, args=None): """ Invoke the binary with the specified args, and return the output if the command finished running with zero exit code. Return False otherwise. @@ -91,6 +92,7 @@ def invoke_binary_checked(binary_path, args=None, environ=None): args = args or [] invocation = [binary_path] invocation.extend(args) + environ = analyzer_context.get_context().get_env_for_bin(binary_path) try: output = subprocess.check_output( invocation, @@ -123,7 +125,7 @@ def __init__(self, analyzer_binary, environ): return analyzer_version = invoke_binary_checked( - self.__analyzer_binary, ['--version'], self.environ) + self.__analyzer_binary, ['--version']) if analyzer_version is False: LOG.debug('Failed to invoke command to get Clang version!') @@ -222,7 +224,7 @@ def is_ctu_capable(self): if not tool_path: return False - return invoke_binary_checked(tool_path, ['-version'], self.environ) \ + return invoke_binary_checked(tool_path, ['-version']) \ is not False @property @@ -233,8 +235,7 @@ def is_on_demand_ctu_available(self): """ analyzer_options = invoke_binary_checked( - self.__analyzer_binary, ['-cc1', '-analyzer-config-help'], - self.environ) + self.__analyzer_binary, ['-cc1', '-analyzer-config-help']) if analyzer_options is False: return False diff --git a/analyzer/codechecker_analyzer/analyzers/clangsa/ctu_manager.py b/analyzer/codechecker_analyzer/analyzers/clangsa/ctu_manager.py index 77caae42d9..8002aba2cd 100644 --- a/analyzer/codechecker_analyzer/analyzers/clangsa/ctu_manager.py +++ b/analyzer/codechecker_analyzer/analyzers/clangsa/ctu_manager.py @@ -126,11 +126,10 @@ def generate_ast(triple_arch, action, source, config): os.makedirs(ast_dir) except OSError: pass - cmdstr = ' '.join(cmd) LOG.debug_analyzer("Generating AST using '%s'", cmdstr) ret_code, _, err = \ - analyzer_base.SourceAnalyzer.run_proc(cmd, action.directory) + analyzer_base.SourceAnalyzer.run_proc(cmd, action.directory, None) if ret_code != 0: LOG.error("Error generating AST.\n\ncommand:\n\n%s\n\nstderr:\n\n%s", diff --git a/analyzer/codechecker_analyzer/analyzers/clangsa/ctu_triple_arch.py b/analyzer/codechecker_analyzer/analyzers/clangsa/ctu_triple_arch.py index 3d2650c20f..a00ee828a6 100644 --- a/analyzer/codechecker_analyzer/analyzers/clangsa/ctu_triple_arch.py +++ b/analyzer/codechecker_analyzer/analyzers/clangsa/ctu_triple_arch.py @@ -35,7 +35,10 @@ def get_compile_command(action, config, source='', output=''): # -isystem, etc. flags where it is found. For this reason we append the # implicit include paths to the end of the analyzer command in order to get # less precedence than the user's explicit include paths. - cmd.extend(prepend_all('-isystem', action.compiler_includes)) + cmd.extend(prepend_all( + '-isystem' if config.add_gcc_include_dirs_with_isystem else + '-idirafter', + action.compiler_includes)) if output: cmd.extend(['-o', output]) if source: diff --git a/analyzer/codechecker_analyzer/analyzers/clangsa/statistics.py b/analyzer/codechecker_analyzer/analyzers/clangsa/statistics.py index 46a3b7cca1..d6e83e0902 100644 --- a/analyzer/codechecker_analyzer/analyzers/clangsa/statistics.py +++ b/analyzer/codechecker_analyzer/analyzers/clangsa/statistics.py @@ -73,7 +73,10 @@ def build_stat_coll_cmd(action, config, source): if not has_flag('-std', cmd) and not has_flag('--std', cmd): cmd.append(action.compiler_standard) - cmd.extend(prepend_all('-isystem', action.compiler_includes)) + cmd.extend(prepend_all( + '-isystem' if config.add_gcc_include_dirs_with_isystem else + '-idirafter', + action.compiler_includes)) if source: cmd.append(source) diff --git a/analyzer/codechecker_analyzer/analyzers/clangsa/version.py b/analyzer/codechecker_analyzer/analyzers/clangsa/version.py index 38f7578384..1b72f2c876 100644 --- a/analyzer/codechecker_analyzer/analyzers/clangsa/version.py +++ b/analyzer/codechecker_analyzer/analyzers/clangsa/version.py @@ -77,7 +77,7 @@ def get(clang_binary): """ compiler_version = subprocess.check_output( [clang_binary, '--version'], - env=analyzer_context.get_context().analyzer_env, + env=analyzer_context.get_context().get_env_for_bin(clang_binary), encoding="utf-8", errors="ignore") version_parser = ClangVersionInfoParser(clang_binary) diff --git a/analyzer/codechecker_analyzer/analyzers/clangtidy/analyzer.py b/analyzer/codechecker_analyzer/analyzers/clangtidy/analyzer.py index a03d372727..cbc803edfc 100644 --- a/analyzer/codechecker_analyzer/analyzers/clangtidy/analyzer.py +++ b/analyzer/codechecker_analyzer/analyzers/clangtidy/analyzer.py @@ -136,17 +136,19 @@ def get_diagtool_bin(): if os.path.exists(diagtool_bin): return diagtool_bin - LOG.debug("'diagtool' can not be found next to the clang binary (%s)!", - clang_bin) + LOG.warning( + "'diagtool' can not be found next to the clang binary (%s)!", + clang_bin) return None -def get_warnings(environment=None): +def get_warnings(): """ Returns list of warning flags by using diagtool. """ diagtool_bin = get_diagtool_bin() + environment = analyzer_context.get_context().get_env_for_bin(diagtool_bin) if not diagtool_bin: return [] @@ -236,10 +238,12 @@ def analyzer_binary(cls): .analyzer_binaries[cls.ANALYZER_NAME] @classmethod - def get_binary_version(cls, environ, details=False) -> str: - # No need to LOG here, we will emit a warning later anyway. + def get_binary_version(cls, details=False) -> str: if not cls.analyzer_binary(): return None + # No need to LOG here, we will emit a warning later anyway. + environ = analyzer_context.get_context().get_env_for_bin( + cls.analyzer_binary()) version = [cls.analyzer_binary(), '--version'] try: @@ -270,7 +274,8 @@ def get_analyzer_checkers(cls): if cls.__analyzer_checkers: return cls.__analyzer_checkers - environ = analyzer_context.get_context().analyzer_env + environ = analyzer_context\ + .get_context().get_env_for_bin(cls.analyzer_binary()) result = subprocess.check_output( [cls.analyzer_binary(), "-list-checks", "-checks=*"], env=environ, @@ -281,7 +286,7 @@ def get_analyzer_checkers(cls): checker_description.extend( ("clang-diagnostic-" + warning, "") - for warning in get_warnings(environ)) + for warning in get_warnings()) cls.__analyzer_checkers = checker_description @@ -297,7 +302,8 @@ def get_checker_config(cls): try: result = subprocess.check_output( [cls.analyzer_binary(), "-dump-config", "-checks=*"], - env=analyzer_context.get_context().analyzer_env, + env=analyzer_context.get_context() + .get_env_for_bin(cls.analyzer_binary()), universal_newlines=True, encoding="utf-8", errors="ignore") @@ -310,10 +316,14 @@ def get_analyzer_config(cls): """ Return the analyzer configuration with all checkers enabled. """ + if not cls.analyzer_binary(): + return [] + try: result = subprocess.check_output( [cls.analyzer_binary(), "-dump-config", "-checks=*"], - env=analyzer_context.get_context().analyzer_env, + env=analyzer_context.get_context() + .get_env_for_bin(cls.analyzer_binary()), universal_newlines=True, encoding="utf-8", errors="ignore") @@ -393,7 +403,12 @@ def get_checker_list(self, config) -> Tuple[List[str], List[str]]: # as -clang-diagnostic-... . elif warning_type == CheckerType.ANALYZER: if state == CheckerState.ENABLED: - compiler_warnings.append('-W' + warning_name) + if checker_name == "clang-diagnostic-error": + # Disable warning of clang-diagnostic-error to + # avoid generated compiler errors. + compiler_warnings.append('-Wno-' + warning_name) + else: + compiler_warnings.append('-W' + warning_name) enabled_checkers.append(checker_name) else: compiler_warnings.append('-Wno-' + warning_name) @@ -486,7 +501,8 @@ def construct_analyzer_cmd(self, result_handler): analyzer_cmd.extend(self.buildaction.analyzer_options) analyzer_cmd.extend(prepend_all( - '-isystem', + '-isystem' if config.add_gcc_include_dirs_with_isystem else + '-idirafter', self.buildaction.compiler_includes)) if not has_flag('-std', analyzer_cmd) and not \ @@ -558,7 +574,7 @@ def resolve_missing_binary(cls, configured_binary, environ): return clangtidy @classmethod - def is_binary_version_incompatible(cls, environ): + def is_binary_version_incompatible(cls): """ We support pretty much every Clang-Tidy version. """ @@ -582,6 +598,10 @@ def construct_config_handler(cls, args): handler.report_hash = args.report_hash \ if 'report_hash' in args else None + handler.add_gcc_include_dirs_with_isystem = \ + 'add_gcc_include_dirs_with_isystem' in args and \ + args.add_gcc_include_dirs_with_isystem + # FIXME We cannot get the resource dir from the clang-tidy binary, # therefore we get a sibling clang binary which of clang-tidy. # TODO Support "clang-tidy -print-resource-dir" . diff --git a/analyzer/codechecker_analyzer/analyzers/config_handler.py b/analyzer/codechecker_analyzer/analyzers/config_handler.py index e4c45eb9f0..bfc8be40fa 100644 --- a/analyzer/codechecker_analyzer/analyzers/config_handler.py +++ b/analyzer/codechecker_analyzer/analyzers/config_handler.py @@ -14,6 +14,7 @@ from enum import Enum import collections import platform +import re from codechecker_analyzer import analyzer_context from codechecker_common.logger import get_logger @@ -88,10 +89,10 @@ def set_checker_enabled(self, checker_name, enabled=True): Explicitly handle checker state, keep description if already set. """ changed_states = [] + regex = "^" + re.escape(str(checker_name)) + "\\b.*$" for ch_name, values in self.__available_checkers.items(): - if ch_name.startswith(checker_name) or \ - ch_name.endswith(checker_name): + if re.match(regex, ch_name): _, description = values state = CheckerState.ENABLED if enabled \ else CheckerState.DISABLED diff --git a/analyzer/codechecker_analyzer/analyzers/cppcheck/analyzer.py b/analyzer/codechecker_analyzer/analyzers/cppcheck/analyzer.py index 4977462be3..4ad42a296e 100644 --- a/analyzer/codechecker_analyzer/analyzers/cppcheck/analyzer.py +++ b/analyzer/codechecker_analyzer/analyzers/cppcheck/analyzer.py @@ -13,7 +13,6 @@ from packaging.version import Version from pathlib import Path import os -import pickle import re import shlex import shutil @@ -83,11 +82,13 @@ def analyzer_binary(cls): .analyzer_binaries[cls.ANALYZER_NAME] @classmethod - def get_binary_version(cls, environ, details=False) -> str: + def get_binary_version(cls, details=False) -> str: """ Get analyzer version information. """ # No need to LOG here, we will emit a warning later anyway. if not cls.analyzer_binary(): return None + environ = analyzer_context.get_context().get_env_for_bin( + cls.analyzer_binary()) version = [cls.analyzer_binary(), '--version'] try: output = subprocess.check_output(version, @@ -243,10 +244,13 @@ def get_analyzer_checkers(cls): """ Return the list of the supported checkers. """ + if not cls.analyzer_binary(): + return [] command = [cls.analyzer_binary(), "--errorlist"] - + environ = analyzer_context.get_context().get_env_for_bin( + command[0]) try: - result = subprocess.check_output(command) + result = subprocess.check_output(command, env=environ) return parse_checkers(result) except (subprocess.CalledProcessError) as e: LOG.error(e.stderr) @@ -272,18 +276,6 @@ def get_checker_config(cls): """ return [] - def analyze(self, analyzer_cmd, res_handler, proc_callback=None, _=None): - environment = None - - original_env_file = os.environ.get( - 'CODECHECKER_ORIGINAL_BUILD_ENV') - if original_env_file: - with open(original_env_file, 'rb') as env_file: - environment = pickle.load(env_file, encoding='utf-8') - - return super().analyze( - analyzer_cmd, res_handler, proc_callback, environment) - def post_analyze(self, result_handler): """ Post process the reuslts after the analysis. @@ -337,11 +329,11 @@ def resolve_missing_binary(cls, configured_binary, environ): return cppcheck @classmethod - def is_binary_version_incompatible(cls, environ): + def is_binary_version_incompatible(cls): """ Check the version compatibility of the given analyzer binary. """ - analyzer_version = cls.get_binary_version(environ) + analyzer_version = cls.get_binary_version() # The analyzer version should be above 1.80 because '--plist-output' # argument was introduced in this release. @@ -393,12 +385,6 @@ def construct_config_handler(cls, args): # No cppcheck arguments file was given in the command line. LOG.debug_analyzer(aerr) - check_env = context.analyzer_env - - # Overwrite PATH to contain only the parent of the cppcheck binary. - if os.path.isabs(Cppcheck.analyzer_binary()): - check_env['PATH'] = os.path.dirname(Cppcheck.analyzer_binary()) - checkers = cls.get_analyzer_checkers() # Cppcheck can and will report with checks that have a different diff --git a/analyzer/codechecker_analyzer/analyzers/gcc/analyzer.py b/analyzer/codechecker_analyzer/analyzers/gcc/analyzer.py index f8814be82e..d99e60cf5b 100644 --- a/analyzer/codechecker_analyzer/analyzers/gcc/analyzer.py +++ b/analyzer/codechecker_analyzer/analyzers/gcc/analyzer.py @@ -7,8 +7,6 @@ # ------------------------------------------------------------------------- from collections import defaultdict from packaging.version import Version -import os -import pickle import shlex import subprocess @@ -57,6 +55,8 @@ def construct_analyzer_cmd(self, result_handler): # unforeseen exceptions where a general catch is justified? config = self.config_handler + if not Gcc.analyzer_binary(): + return None # We don't want GCC do start linking, but -fsyntax-only stops the # compilation process too early for proper diagnostics generation. analyzer_cmd = [Gcc.analyzer_binary(), '-fanalyzer', '-c', @@ -93,10 +93,14 @@ def get_analyzer_checkers(cls): Return the list of the supported checkers. """ command = [cls.analyzer_binary(), "--help=warning"] + if not cls.analyzer_binary(): + return [] + environ = analyzer_context.get_context().get_env_for_bin( + command[0]) checker_list = [] try: - output = subprocess.check_output(command) + output = subprocess.check_output(command, env=environ) # Still contains the help message we need to remove. for entry in output.decode().split('\n'): @@ -132,17 +136,6 @@ def get_checker_config(cls): # TODO return [] - def analyze(self, analyzer_cmd, res_handler, proc_callback=None, _=None): - env = None - - original_env_file = os.environ.get( - 'CODECHECKER_ORIGINAL_BUILD_ENV') - if original_env_file: - with open(original_env_file, 'rb') as env_file: - env = pickle.load(env_file, encoding='utf-8') - - return super().analyze(analyzer_cmd, res_handler, proc_callback, env) - def post_analyze(self, result_handler: GccResultHandler): """ Post process the reuslts after the analysis. @@ -162,13 +155,15 @@ def resolve_missing_binary(cls, configured_binary, environ): # TODO @classmethod - def get_binary_version(cls, environ, details=False) -> str: + def get_binary_version(cls, details=False) -> str: """ Return the analyzer version. """ # No need to LOG here, we will emit a warning later anyway. if not cls.analyzer_binary(): return None + environ = analyzer_context.get_context().get_env_for_bin( + cls.analyzer_binary()) if details: version = [cls.analyzer_binary(), '--version'] else: @@ -187,11 +182,11 @@ def get_binary_version(cls, environ, details=False) -> str: return None @classmethod - def is_binary_version_incompatible(cls, environ): + def is_binary_version_incompatible(cls): """ Check the version compatibility of the given analyzer binary. """ - analyzer_version = cls.get_binary_version(environ) + analyzer_version = cls.get_binary_version() if analyzer_version is None: return "GCC binary is too old to support -dumpfullversion." diff --git a/analyzer/codechecker_analyzer/buildlog/log_parser.py b/analyzer/codechecker_analyzer/buildlog/log_parser.py index c64eb991d5..c495598e37 100644 --- a/analyzer/codechecker_analyzer/buildlog/log_parser.py +++ b/analyzer/codechecker_analyzer/buildlog/log_parser.py @@ -99,6 +99,7 @@ '-fno-keep-inline-dllexport' '-fno-keep-static-consts', '-fno-lifetime-dse', + '-f(no-)?printf-return-value', '-f(no-)?reorder-functions', '-fno-strength-reduce', '-fno-toplevel-reorder', diff --git a/analyzer/codechecker_analyzer/checkers.py b/analyzer/codechecker_analyzer/checkers.py index 00d12a434c..1e9b6f7461 100644 --- a/analyzer/codechecker_analyzer/checkers.py +++ b/analyzer/codechecker_analyzer/checkers.py @@ -28,7 +28,7 @@ def available(ordered_checkers, available_checkers): name_match = False for available_checker in available_checkers: - regex = "^" + re.escape(str(checker_name)) + ".*$" + regex = "^" + re.escape(str(checker_name)) + "\\b.*$" c_name = available_checker match = re.match(regex, c_name) if match: diff --git a/analyzer/codechecker_analyzer/cmd/analyze.py b/analyzer/codechecker_analyzer/cmd/analyze.py index e2116cdba7..4ebf0b9ee9 100644 --- a/analyzer/codechecker_analyzer/cmd/analyze.py +++ b/analyzer/codechecker_analyzer/cmd/analyze.py @@ -243,6 +243,17 @@ def add_arguments_to_parser(parser): "fails with error message related to __builtin " "symbols.") + parser.add_argument('--add-gcc-include-dirs-with-isystem', + dest="add_gcc_include_dirs_with_isystem", + required=False, + action='store_true', + default=False, + help="Implicit include directories are appended to " + "the analyzer command with -idirafter. If " + "-isystem is needed instead, as it was used " + "before CodeChecker 6.24.1, this flag can be " + "used.") + parser.add_argument('-t', '--type', '--output-format', dest="output_format", required=False, @@ -361,7 +372,7 @@ def add_arguments_to_parser(parser): metavar='ANALYZER', required=False, choices=analyzer_types.supported_analyzers, - default=argparse.SUPPRESS, + default=None, help="Run analysis only with the analyzers " "specified. Currently supported analyzers " "are: " + diff --git a/analyzer/codechecker_analyzer/cmd/analyzers.py b/analyzer/codechecker_analyzer/cmd/analyzers.py index aa6e4b2722..e235a08234 100644 --- a/analyzer/codechecker_analyzer/cmd/analyzers.py +++ b/analyzer/codechecker_analyzer/cmd/analyzers.py @@ -118,10 +118,13 @@ def main(args): if args.dump_config: binary = context.analyzer_binaries.get(args.dump_config) + environ = analyzer_context.get_context().get_env_for_bin( + binary) if args.dump_config == 'clang-tidy': subprocess.call([binary, '-dump-config', '-checks=*'], - encoding="utf-8", errors="ignore") + encoding="utf-8", errors="ignore", + env=environ) elif args.dump_config == 'clangsa': ret = subprocess.call([binary, '-cc1', @@ -129,7 +132,8 @@ def main(args): '-analyzer-checker-option-help-alpha'], stderr=subprocess.PIPE, encoding="utf-8", - errors="ignore") + errors="ignore", + env=environ) if ret: # This flag is supported from Clang 9. @@ -186,10 +190,9 @@ def uglify(text): rows = [] for analyzer_name, analyzer_class in \ analyzer_types.supported_analyzers.items(): - check_env = context.analyzer_env - version = analyzer_class.get_binary_version(check_env) + version = analyzer_class.get_binary_version() if not version: - version = 'ERROR' + version = 'NOT FOUND' binary = context.analyzer_binaries.get(analyzer_name) rows.append([analyzer_name, binary, version]) diff --git a/analyzer/codechecker_analyzer/cmd/check.py b/analyzer/codechecker_analyzer/cmd/check.py index 2e20d19e6d..e654755f8f 100644 --- a/analyzer/codechecker_analyzer/cmd/check.py +++ b/analyzer/codechecker_analyzer/cmd/check.py @@ -152,6 +152,17 @@ def add_arguments_to_parser(parser): "fails with error message related to __builtin " "symbols.") + parser.add_argument('--add-gcc-include-dirs-with-isystem', + dest="add_gcc_include_dirs_with_isystem", + required=False, + action='store_true', + default=False, + help="Implicit include directories are appended to " + "the analyzer command with -idirafter. If " + "-isystem is needed instead, as it was used " + "before CodeChecker 6.24.1, this flag can be " + "used.") + log_args = parser.add_argument_group( "log arguments", """ @@ -294,7 +305,7 @@ def add_arguments_to_parser(parser): metavar='ANALYZER', required=False, choices=analyzer_types.supported_analyzers, - default=argparse.SUPPRESS, + default=None, help="Run analysis only with the analyzers " "specified. Currently supported analyzers " "are: " + @@ -881,6 +892,7 @@ def __update_if_key_exists(source, target, key): 'capture_analysis_output', 'generate_reproducer', 'config_file', + 'ctu_ast_mode', 'ctu_phases', 'ctu_reanalyze_on_failure', 'stats_output', @@ -896,6 +908,7 @@ def __update_if_key_exists(source, target, key): 'review_status_config', 'compile_uniqueing', 'report_hash', + 'add_gcc_include_dirs_with_isystem', 'enable_z3', 'enable_z3_refutation'] for key in args_to_update: diff --git a/analyzer/codechecker_analyzer/cmd/checkers.py b/analyzer/codechecker_analyzer/cmd/checkers.py index fc26a2c7d8..c567870497 100644 --- a/analyzer/codechecker_analyzer/cmd/checkers.py +++ b/analyzer/codechecker_analyzer/cmd/checkers.py @@ -522,9 +522,8 @@ def __print_checker_config(args: argparse.Namespace): if args.output_format == 'custom': args.output_format = 'rows' - working_analyzers, errored = analyzer_types.check_supported_analyzers( - args.analyzers) - analyzer_types.check_available_analyzers(working_analyzers, errored) + working_analyzers, errored = \ + analyzer_types.check_available_analyzers(args.analyzers) if 'details' in args: header = ['Option', 'Description'] @@ -556,7 +555,10 @@ def __print_checker_config(args: argparse.Namespace): if rows: print(twodim.to_str(args.output_format, header, rows)) - analyzer_types.print_unsupported_analyzers(errored) + # Don't print this warning unless the analyzer list is + # given by the user. + if args.analyzers: + analyzer_types.print_unsupported_analyzers(errored) if analyzer_failures: LOG.error("Failed to get checker configuration options for '%s' " diff --git a/analyzer/codechecker_analyzer/cmd/fixit.py b/analyzer/codechecker_analyzer/cmd/fixit.py index 33bcd3d020..9ed9d45b8e 100644 --- a/analyzer/codechecker_analyzer/cmd/fixit.py +++ b/analyzer/codechecker_analyzer/cmd/fixit.py @@ -285,10 +285,14 @@ def apply_process(out_dir): """ Execute clang-apply-replacements binary. """ + context = analyzer_context.get_context() + replacer_env = context.get_env_for_bin( + analyzer_context.get_context().replacer_binary) subprocess.Popen([ analyzer_context.get_context().replacer_binary, *ignore_flag, - out_dir]).communicate() + out_dir], + env=replacer_env).communicate() not_existing_files = set() existing_files = set() diff --git a/analyzer/codechecker_analyzer/env.py b/analyzer/codechecker_analyzer/env.py index 454a59c54c..a9e29817f6 100644 --- a/analyzer/codechecker_analyzer/env.py +++ b/analyzer/codechecker_analyzer/env.py @@ -9,6 +9,7 @@ import os import re +import pickle from codechecker_analyzer import analyzer_context from codechecker_common.logger import get_logger @@ -27,7 +28,18 @@ def get_log_env(logfile, original_env): new_env[context.env_var_cc_logger_bin] = context.path_logger_bin - new_env['LD_PRELOAD'] = context.logger_lib_path + if 'LD_PRELOAD' in new_env: + new_env['LD_PRELOAD'] = new_env['LD_PRELOAD'] \ + + " " + context.logger_lib_name + else: + new_env['LD_PRELOAD'] = context.logger_lib_name + + try: + original_ld_library_path = new_env['LD_LIBRARY_PATH'] + new_env['LD_LIBRARY_PATH'] = context.path_logger_lib + ':' + \ + original_ld_library_path + except KeyError: + new_env['LD_LIBRARY_PATH'] = context.path_logger_lib # Set ld logger logfile. new_env[context.env_var_cc_logger_file] = logfile @@ -35,6 +47,24 @@ def get_log_env(logfile, original_env): return new_env +def get_original_env(): + original_env = os.environ + try: + original_env_file = os.environ.get('CODECHECKER_ORIGINAL_BUILD_ENV') + if original_env_file: + LOG.debug_analyzer('Loading original build env from: %s', + original_env_file) + + with open(original_env_file, 'rb') as env_file: + original_env = pickle.load(env_file, encoding='utf-8') + + except Exception as ex: + LOG.warning(str(ex)) + LOG.warning('Failed to get saved original_env ') + original_env = None + return original_env + + def extend(path_env_extra, ld_lib_path_extra): """Extend the checker environment. diff --git a/analyzer/codechecker_analyzer/host_check.py b/analyzer/codechecker_analyzer/host_check.py index 1445db51dc..4456f908c4 100644 --- a/analyzer/codechecker_analyzer/host_check.py +++ b/analyzer/codechecker_analyzer/host_check.py @@ -21,24 +21,28 @@ LOG = get_logger('analyzer') -def check_analyzer(compiler_bin, env): +def check_analyzer(compiler_bin): """ Simple check if clang is available. """ clang_version_cmd = [compiler_bin, '--version'] LOG.debug_analyzer(' '.join(clang_version_cmd)) + environ = analyzer_context.get_context().get_env_for_bin( + compiler_bin) try: - res = subprocess.call( + proc = subprocess.Popen( clang_version_cmd, - env=env, + env=environ, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf-8", errors="ignore") - if not res: + out, err = proc.communicate() + if not proc.returncode: return True - - LOG.debug_analyzer('Failed to run: "%s"', ' '.join(clang_version_cmd)) + LOG.error('Failed to run: "%s"', ' '.join(clang_version_cmd)) + LOG.error('stdout: %s', out) + LOG.error('stderr: %s', err) return False except OSError as oerr: @@ -53,13 +57,14 @@ def has_analyzer_config_option(clang_bin, config_option_name): cmd = [clang_bin, "-cc1", "-analyzer-config-help"] LOG.debug('run: "%s"', ' '.join(cmd)) + context = analyzer_context.get_context() try: proc = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - env=analyzer_context.get_context().analyzer_env, + env=context.get_env_for_bin(clang_bin), encoding="utf-8", errors="ignore") out, err = proc.communicate() LOG.debug("stdout:\n%s", out) @@ -92,7 +97,7 @@ def has_analyzer_option(clang_bin, feature): cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - env=analyzer_context.get_context().analyzer_env, + env=analyzer_context.get_context().get_env_for_bin(clang_bin), encoding="utf-8", errors="ignore") out, err = proc.communicate() LOG.debug("stdout:\n%s", out) diff --git a/analyzer/tests/functional/analyze/test_analyze.py b/analyzer/tests/functional/analyze/test_analyze.py index 478ea84a42..88e46ad47c 100644 --- a/analyzer/tests/functional/analyze/test_analyze.py +++ b/analyzer/tests/functional/analyze/test_analyze.py @@ -263,7 +263,7 @@ def test_compiler_info_file_is_loaded(self): print(out) self.assertTrue("-std=FAKE_STD" in out) self.assertTrue("--target=FAKE_TARGET" in out) - self.assertTrue("-isystem /FAKE_INCLUDE_DIR" in out) + self.assertTrue("-idirafter /FAKE_INCLUDE_DIR" in out) def test_capture_analysis_output(self): """ @@ -1316,6 +1316,22 @@ def test_invalid_compilation_database(self): self.assertEqual(process.returncode, 1) + def test_disable_error_checker_tidy(self): + """Make sure clang-diagnostic-error checker is disabled (PR #4325)""" + cmd = [self._codechecker_cmd, "check", "-l", self.__get_build_json(), + '--enable', 'clang-diagnostic-error'] + + process = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=self.test_dir, + encoding="utf-8", + errors="ignore") + out, _ = process.communicate() + + self.assertNotIn("error", out) + def test_compilation_db_relative_file_path(self): """ Test relative path in compilation database. diff --git a/analyzer/tests/libtest/cmd_line.py b/analyzer/tests/libtest/cmd_line.py new file mode 100644 index 0000000000..258f72c459 --- /dev/null +++ b/analyzer/tests/libtest/cmd_line.py @@ -0,0 +1,33 @@ +# ------------------------------------------------------------------------- +# +# 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 +# +# ------------------------------------------------------------------------- + +import argparse +from codechecker_analyzer.cmd import analyze + + +class NoExitArgumentParser(argparse.ArgumentParser): + """ + ArgumentParser that does not exit on error. + """ + def error(self, _): + pass + + +def create_analyze_argparse(args=None): + """ + Create argparse object for analyze command. + + :param args: list of command line arguments to parse. + """ + if args is None: + args = [] + + parser = NoExitArgumentParser() + analyze.add_arguments_to_parser(parser) + + return parser.parse_args(args) diff --git a/analyzer/tests/unit/test_analyzer_command.py b/analyzer/tests/unit/test_analyzer_command.py new file mode 100644 index 0000000000..28ca23eabd --- /dev/null +++ b/analyzer/tests/unit/test_analyzer_command.py @@ -0,0 +1,65 @@ +# ------------------------------------------------------------------------- +# +# 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 +# +# ------------------------------------------------------------------------- + +import argparse +import unittest +from codechecker_analyzer.analyzers.clangsa.analyzer import ClangSA +from codechecker_analyzer.buildlog import log_parser +from codechecker_analyzer.cmd import analyze +from libtest.cmd_line import create_analyze_argparse + + +def create_analyzer_sa(args=None): + parser = argparse.ArgumentParser() + analyze.add_arguments_to_parser(parser) + cfg_handler = ClangSA.construct_config_handler( + create_analyze_argparse(args)) + + action = { + 'file': 'main.cpp', + 'command': "g++ -o main main.cpp", + 'directory': '/'} + build_action = log_parser.parse_options(action) + + return ClangSA(cfg_handler, build_action) + + +def create_result_handler(analyzer): + """ + Create result handler for construct_analyzer_cmd call. + """ + + build_action = analyzer.buildaction + + rh = analyzer.construct_result_handler( + build_action, + build_action.directory, + None) + + rh.analyzed_source_file = build_action.source + + return rh + + +class AnalyzerCommandClangSATest(unittest.TestCase): + def test_isystem_idirafter(self): + """ + Test that the implicit include paths are added to the analyzer command + with -idirafter. + """ + analyzer = create_analyzer_sa(['--add-gcc-include-dirs-with-isystem']) + + result_handler = create_result_handler(analyzer) + cmd = analyzer.construct_analyzer_cmd(result_handler) + self.assertIn('-isystem', cmd) + + analyzer = create_analyzer_sa() + + result_handler = create_result_handler(analyzer) + cmd = analyzer.construct_analyzer_cmd(result_handler) + self.assertIn('-idirafter', cmd) diff --git a/analyzer/tests/unit/test_checker_handling.py b/analyzer/tests/unit/test_checker_handling.py index 1d62348d76..7c5dd81db4 100644 --- a/analyzer/tests/unit/test_checker_handling.py +++ b/analyzer/tests/unit/test_checker_handling.py @@ -31,6 +31,8 @@ from codechecker_analyzer import analyzer_context from codechecker_analyzer.buildlog import log_parser +from libtest.cmd_line import create_analyze_argparse + class MockClangsaCheckerLabels: def checkers_by_labels(self, labels): @@ -68,8 +70,7 @@ def checkers(self, _=None): def create_analyzer_sa(): - args = [] - cfg_handler = ClangSA.construct_config_handler(args) + cfg_handler = ClangSA.construct_config_handler(create_analyze_argparse()) action = { 'file': 'main.cpp', @@ -146,7 +147,7 @@ def f(checks, checkers): return set(checkers) <= result return f - args = [] + args = create_analyze_argparse() # "security" profile, but alpha -> not in default. security_profile_alpha = [ @@ -250,6 +251,13 @@ def f(checks, checkers): self.assertTrue(all_with_status(CheckerState.DISABLED) (cfg_handler.checks(), cert_guideline)) + cfg_handler = ClangSA.construct_config_handler(args) + cfg_handler.initialize_checkers(checkers, + [('default', False), + ('DeadStores', True)]) + self.assertTrue(all_with_status(CheckerState.DISABLED) + (cfg_handler.checks(), default_profile)) + # Enable "LOW" severity checkers. cfg_handler = ClangSA.construct_config_handler(args) cfg_handler.initialize_checkers(checkers, @@ -257,6 +265,27 @@ def f(checks, checkers): self.assertTrue(all_with_status(CheckerState.ENABLED) (cfg_handler.checks(), low_severity)) + # Enable checkers with a checker group prefix. + cfg_handler = ClangSA.construct_config_handler(args) + cfg_handler.initialize_checkers(checkers, + [('default', False), + ('cplusplus.NewDelete', True)]) + self.assertTrue( + all_with_status(CheckerState.ENABLED) + (cfg_handler.checks(), ['cplusplus.NewDelete'])) + self.assertTrue( + all_with_status(CheckerState.DISABLED) + (cfg_handler.checks(), ['cplusplus.NewDeleteLeaks'])) + + cfg_handler = ClangSA.construct_config_handler(args) + cfg_handler.initialize_checkers(checkers, + [('default', False), + ('cplusplus', True)]) + self.assertTrue( + all_with_status(CheckerState.ENABLED) + (cfg_handler.checks(), ['cplusplus.NewDelete', + 'cplusplus.NewDeleteLeaks'])) + # Test if statisticsbased checkers are enabled by --stats flag # by default. stats_capable = strtobool( @@ -305,10 +334,8 @@ def occurring_values(self, label): def create_analyzer_tidy(args=None): - if args is None: - args = [] - - cfg_handler = ClangTidy.construct_config_handler(args) + cfg_handler = ClangTidy.construct_config_handler( + create_analyze_argparse(args)) action = { 'file': 'main.cpp', @@ -402,9 +429,7 @@ def test_disable_clangsa_checkers(self): self.assertTrue(is_compiler_warning('Wreserved-id-macro')) self.assertFalse(is_compiler_warning('hicpp')) - args = Namespace() - args.ordered_checkers = [('Wreserved-id-macro', True)] - analyzer = create_analyzer_tidy(args) + analyzer = create_analyzer_tidy(['--enable', 'Wreserved-id-macro']) result_handler = create_result_handler(analyzer) analyzer.config_handler.checker_config = '{}' @@ -465,11 +490,9 @@ def test_enable_all_disable_warning(self): Side note: we use -Weverything instead of listing all enabled warnings to represent --enable-all. """ - args = Namespace() - args.ordered_checkers = [('clang-diagnostic-unused-variable', False)] - args.enable_all = True - - analyzer = create_analyzer_tidy(args) + analyzer = create_analyzer_tidy([ + '--enable-all', + '--disable', 'clang-diagnostic-unused-variable']) result_handler = create_result_handler(analyzer) analyzer_cmd = analyzer.construct_analyzer_cmd(result_handler) @@ -507,13 +530,10 @@ def test_clang_diags_as_compiler_warnings(self): Test that clang-diagnostic-* checkers are enabled as compiler warnings. """ - args = Namespace() - args.ordered_checkers = [ + analyzer = create_analyzer_tidy([ # This should enable -Wvla and -Wvla-extension. - ('clang-diagnostic-vla', True), - ('clang-diagnostic-unused-value', False) - ] - analyzer = create_analyzer_tidy(args) + '--enable', 'clang-diagnostic-vla', + '--disable', 'clang-diagnostic-unused-value']) result_handler = create_result_handler(analyzer) analyzer.config_handler.checker_config = '{}' diff --git a/analyzer/tests/unit/test_env_var.py b/analyzer/tests/unit/test_env_var.py index 2c1ed54bd9..ebf24c4477 100644 --- a/analyzer/tests/unit/test_env_var.py +++ b/analyzer/tests/unit/test_env_var.py @@ -12,6 +12,8 @@ import unittest +import tempfile +import os from codechecker_analyzer import analyzer_context from codechecker_analyzer.analyzers.gcc.analyzer import Gcc @@ -48,7 +50,7 @@ def _get_analyzer_bin_for_cc_analyzer_bin(self, analyzer_bin_conf: str): initialized with the binary that was given by the env var). """ context = analyzer_context.get_context() - context.analyzer_env["CC_ANALYZER_BIN"] = analyzer_bin_conf + context.cc_env["CC_ANALYZER_BIN"] = analyzer_bin_conf context._Context__populate_analyzers() analyzer = create_analyzer_gcc() @@ -80,7 +82,7 @@ def test_cc_analyzer_bin_overrides_cc_analyzers_from_path(self): """ context = analyzer_context.get_context() - context.analyzer_env["CC_ANALYZERS_FROM_PATH"] = '1' + context.cc_env["CC_ANALYZERS_FROM_PATH"] = '1' bin_gcc_var = self._get_analyzer_bin_for_cc_analyzer_bin("gcc:gcc") self.assertTrue(bin_gcc_var.endswith("gcc")) @@ -91,3 +93,71 @@ def test_cc_analyzer_bin_overrides_cc_analyzers_from_path(self): self.assertTrue(not bin_gpp_var.endswith("gcc")) self.assertNotEqual(bin_gcc_var, bin_gpp_var) + + def test_cc_analyzer_internal_env(self): + """ + Check whether the ld_library_path is extended with the internal + lib path if internally packaged analyzer is invoked. + """ + + data_files_dir = tempfile.mkdtemp() + + package_layout_json = """ + { + "ld_lib_path_extra": [], + "path_env_extra": [], + "runtime": { + "analyzers": { + "clang-tidy": "clang-tidy", + "clangsa": "cc-bin/packaged-clang", + "cppcheck": "cppcheck", + "gcc": "g++" + }, + "clang-apply-replacements": "clang-apply-replacements", + "ld_lib_path_extra": [ + "internal_package_lib" + ], + "path_env_extra": [ + ] + } + } + """ + config_dir = os.path.join(data_files_dir, "config") + os.mkdir(config_dir) + layout_cfg_file = os.path.join(config_dir, "package_layout.json") + with open(layout_cfg_file, "w", encoding="utf-8") as text_file: + text_file.write(package_layout_json) + cc_bin_dir = os.path.join(data_files_dir, "cc-bin") + os.mkdir(cc_bin_dir) + packaged_clang_file = os.path.join(cc_bin_dir, "packaged-clang") + with open(packaged_clang_file, "w", encoding="utf-8") as text_file: + text_file.write("") + + context = analyzer_context.get_context() + context._data_files_dir_path = data_files_dir + + lcfg_dict = context._Context__get_package_layout() + context._data_files_dir_path = data_files_dir + context.pckg_layout = lcfg_dict['runtime'] + context._Context__init_env() + context._Context__populate_analyzers() + + # clang-19 is part of the codechecker package + # so the internal package lib should be in the ld_library_path + clang_env = context.get_env_for_bin( + context.analyzer_binaries["clangsa"]) + env_txt = str(clang_env) + self.assertTrue(env_txt.find("internal_package_lib") != -1) + + # clang-tidy is not part of the codechecker package + # so internal package lib should not be in the ld_library_path + clang_env = context.get_env_for_bin( + context.analyzer_binaries["clang-tidy"]) + env_txt = str(clang_env) + self.assertTrue(env_txt.find("internal_package_lib") == -1) + + os.remove(layout_cfg_file) + os.remove(packaged_clang_file) + os.rmdir(cc_bin_dir) + os.rmdir(config_dir) + os.rmdir(context._data_files_dir_path) diff --git a/analyzer/tests/unit/test_log_parser.py b/analyzer/tests/unit/test_log_parser.py index acab5cb9f8..86ed7bb21d 100644 --- a/analyzer/tests/unit/test_log_parser.py +++ b/analyzer/tests/unit/test_log_parser.py @@ -11,13 +11,11 @@ import json import os -import platform import shutil import tempfile import unittest from codechecker_analyzer.buildlog import log_parser -from codechecker_analyzer.env import get_log_env from codechecker_common.skiplist_handler import SkipListHandler, \ SkipListHandlers from codechecker_common.util import load_json @@ -664,24 +662,3 @@ def test_symlink(self): self.assertEqual(len(build_actions), 3) self.assertEqual(build_action.source, file_c_symdir) - - def test_get_log_env(self): - """ - Test if get_log_env returns the correct environment - with LD_PRELOAD set to pointing to the correct directory - of the ldlogger.so lib. - """ - log_file = os.path.join(self.tmp_dir, "compile_commands.json") - original_env = os.environ.copy() - # If this asset fails, make sure that you don't have LD_PRELOAD set - # in your environment. - self.assertNotIn("LD_PRELOAD", original_env) - env = get_log_env(log_file, original_env) - - # The new environment should contain the LD_PRELOAD variable. - self.assertIn("LD_PRELOAD", env) - - # Make sure that the test running machine architecture is in the - # LD_PRELOAD path. - machine = platform.uname().machine - self.assertTrue(env["LD_PRELOAD"].endswith(machine + "/ldlogger.so")) diff --git a/analyzer/tools/build-logger/README.md b/analyzer/tools/build-logger/README.md index 184feafb58..28b2a5a632 100644 --- a/analyzer/tools/build-logger/README.md +++ b/analyzer/tools/build-logger/README.md @@ -14,7 +14,8 @@ make all test Set the following environment variables: ~~~~~~~ -export LD_PRELOAD=`pwd`/build/lib/`uname -m`/ldlogger.so +export LD_PRELOAD=ldlogger.so +export LD_LIBRARY_PATH=`pwd`/build/lib:$LD_LIBRARY_PATH export CC_LOGGER_GCC_LIKE="gcc:g++:clang:clang++:/cc:c++" # The output compilation JSON file. export CC_LOGGER_FILE=`pwd`/compilation.json diff --git a/analyzer/tools/build-logger/tests/unit/__init__.py b/analyzer/tools/build-logger/tests/unit/__init__.py index 2e2364b141..e87a409252 100644 --- a/analyzer/tools/build-logger/tests/unit/__init__.py +++ b/analyzer/tools/build-logger/tests/unit/__init__.py @@ -106,13 +106,10 @@ def read_actual_json(self) -> str: return fd.read() def get_envvars(self) -> Mapping[str, str]: - machine = platform.uname().machine return { "PATH": os.getenv("PATH"), - "LD_PRELOAD": os.path.join(LOGGER_DIR, - "lib", - machine, - "ldlogger.so"), + "LD_PRELOAD": "ldlogger.so", + "LD_LIBRARY_PATH": os.path.join(LOGGER_DIR, "lib"), "CC_LOGGER_GCC_LIKE": "gcc:g++:clang:clang++:/cc:c++", "CC_LOGGER_FILE": self.logger_file, "CC_LOGGER_DEBUG_FILE": self.logger_debug_file, diff --git a/config/labels/analyzers/clang-tidy.json b/config/labels/analyzers/clang-tidy.json index 9a12792297..bb52fd319f 100644 --- a/config/labels/analyzers/clang-tidy.json +++ b/config/labels/analyzers/clang-tidy.json @@ -1798,7 +1798,9 @@ "guideline:sei-cert", "label-tool-skip:severity", "profile:default", + "profile:extreme", "profile:security", + "profile:sensitive", "sei-cert:exp33-c", "severity:HIGH" ], @@ -3190,8 +3192,8 @@ "doc_url:https://clang.llvm.org/docs/DiagnosticsReference.html#winvalid-noreturn", "guideline:sei-cert", "profile:security", - "severity:MEDIUM", - "sei-cert:msc53-cpp" + "sei-cert:msc53-cpp", + "severity:MEDIUM" ], "clang-diagnostic-invalid-offsetof": [ "doc_url:https://clang.llvm.org/docs/DiagnosticsReference.html#winvalid-offsetof", @@ -4166,10 +4168,10 @@ "guideline:sei-cert", "label-tool-skip:severity", "profile:default", - "sei-cert:exp45-c", "profile:extreme", "profile:security", "profile:sensitive", + "sei-cert:exp45-c", "severity:MEDIUM" ], "clang-diagnostic-parentheses-equality": [ diff --git a/config/labels/analyzers/clangsa.json b/config/labels/analyzers/clangsa.json index ec217740ef..a028a5f84a 100644 --- a/config/labels/analyzers/clangsa.json +++ b/config/labels/analyzers/clangsa.json @@ -875,7 +875,6 @@ "profile:default", "profile:extreme", "profile:security", - "profile:security", "profile:sensitive", "sei-cert:env31-c", "sei-cert:env34-c", @@ -1077,6 +1076,7 @@ ], "unix.cstring.NullArg": [ "doc_url:https://clang.llvm.org/docs/analyzer/checkers.html#unix-cstring-nullarg-c", + "guideline:sei-cert", "profile:default", "profile:extreme", "profile:security", diff --git a/config/labels/analyzers/cppcheck.json b/config/labels/analyzers/cppcheck.json index ea391198af..a8739ab65c 100644 --- a/config/labels/analyzers/cppcheck.json +++ b/config/labels/analyzers/cppcheck.json @@ -19,22 +19,32 @@ ], "cppcheck-IOWithoutPositioning": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-StlMissingComparison": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-accessForwarded": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-accessMoved": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-argumentSize": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-arithOperationsOnVoidPointer": [ @@ -42,10 +52,14 @@ ], "cppcheck-arrayIndexOutOfBounds": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-arrayIndexOutOfBoundsCond": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-arrayIndexThenCheck": [ @@ -53,6 +67,8 @@ ], "cppcheck-assertWithSideEffect": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-assignBoolToFloat": [ @@ -60,6 +76,8 @@ ], "cppcheck-assignBoolToPointer": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-assignIfError": [ @@ -67,6 +85,8 @@ ], "cppcheck-assignmentInAssert": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-assignmentInCondition": [ @@ -74,14 +94,20 @@ ], "cppcheck-autoVariables": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-autovarInvalidDeallocation": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-badBitmaskCheck": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-bitwiseOnBoolean": [ @@ -89,10 +115,14 @@ ], "cppcheck-boostForeachError": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-bufferAccessOutOfBounds": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-catchExceptionByValue": [ @@ -100,14 +130,20 @@ ], "cppcheck-charBitOp": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-charLiteralWithCharPtrCompare": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-checkCastIntToCharAndBack": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-clarifyCalculation": [ @@ -118,6 +154,8 @@ ], "cppcheck-clarifyStatement": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-commaSeparatedReturn": [ @@ -125,10 +163,14 @@ ], "cppcheck-compareBoolExpressionWithInt": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-comparePointers": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-compareValueOutOfTypeRangeError": [ @@ -139,6 +181,8 @@ ], "cppcheck-comparisonFunctionIsAlwaysTrueOrFalse": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-comparisonOfBoolWithBoolError": [ @@ -146,6 +190,8 @@ ], "cppcheck-comparisonOfBoolWithInvalidComparator": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-comparisonOfFuncReturningBoolError": [ @@ -168,6 +214,8 @@ ], "cppcheck-constStatement": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-constVariable": [ @@ -181,18 +229,26 @@ ], "cppcheck-containerOutOfBounds": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-copyCtorAndEqOperator": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-copyCtorPointerCopying": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-coutCerrMisusage": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-cppcheckLimit": [ @@ -203,10 +259,14 @@ ], "cppcheck-danglingLifetime": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-danglingReference": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-danglingTempReference": [ @@ -214,34 +274,50 @@ ], "cppcheck-danglingTemporaryLifetime": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-deallocDealloc": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-deallocret": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-deallocuse": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-derefInvalidIterator": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-divideSizeof": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-doubleFree": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-duplInheritedMember": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-duplicateAssignExpression": [ @@ -270,10 +346,14 @@ ], "cppcheck-eraseDereference": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-exceptDeallocThrow": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-exceptRethrowCopy": [ @@ -281,6 +361,8 @@ ], "cppcheck-exceptThrowInDestructor": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-fflushOnInputStream": [ @@ -288,6 +370,8 @@ ], "cppcheck-floatConversionOverflow": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-funcArgNamesDifferent": [ @@ -295,6 +379,8 @@ ], "cppcheck-funcArgOrderDifferent": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-functionConst": [ @@ -308,38 +394,56 @@ ], "cppcheck-identicalConditionAfterEarlyExit": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-identicalInnerCondition": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-ignoredReturnValue": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-incompatibleFileOpen": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-incompleteArrayFill": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-incorrectCharBooleanError": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-incorrectLogicOperator": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-incorrectStringBooleanError": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-incorrectStringCompare": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-incrementboolean": [ @@ -353,6 +457,8 @@ ], "cppcheck-integerOverflow": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-internalAstError": [ @@ -363,6 +469,8 @@ ], "cppcheck-invalidContainer": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-invalidContainerLoop": [ @@ -370,30 +478,44 @@ ], "cppcheck-invalidFree": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-invalidFunctionArg": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-invalidFunctionArgBool": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-invalidFunctionArgStr": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-invalidIterator1": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-invalidLengthModifierError": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-invalidLifetime": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-invalidPointerCast": [ @@ -401,74 +523,110 @@ ], "cppcheck-invalidPrintfArgType_float": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-invalidPrintfArgType_n": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-invalidPrintfArgType_p": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-invalidPrintfArgType_s": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-invalidPrintfArgType_sint": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-invalidPrintfArgType_uint": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-invalidScanfArgType_float": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-invalidScanfArgType_int": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-invalidScanfArgType_s": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-invalidScanfFormatWidth": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-invalidScanfFormatWidth_smaller": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-invalidTestForOverflow": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-invalidscanf": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-iterators1": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-iterators2": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-iterators3": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-iteratorsCmp1": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-iteratorsCmp2": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-knownArgument": [ @@ -488,18 +646,26 @@ ], "cppcheck-leakNoVarFunctionCall": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-leakReturnValNotUsed": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-leakUnsafeArgAlloc": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-literalWithCharPtrCompare": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-localMutex": [ @@ -507,22 +673,32 @@ ], "cppcheck-mallocOnClassError": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-mallocOnClassWarning": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-memleak": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-memleakOnRealloc": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-memsetClass": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-memsetClassFloat": [ @@ -530,6 +706,8 @@ ], "cppcheck-memsetClassReference": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-memsetFloat": [ @@ -537,18 +715,26 @@ ], "cppcheck-memsetValueOutOfRange": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-memsetZeroBytes": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-mismatchAllocDealloc": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-mismatchSize": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-mismatchingBitAnd": [ @@ -556,6 +742,8 @@ ], "cppcheck-mismatchingContainerExpression": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-mismatchingContainerIterator": [ @@ -563,6 +751,8 @@ ], "cppcheck-mismatchingContainers": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-missingInclude": [ @@ -573,6 +763,8 @@ ], "cppcheck-missingMemberCopy": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-missingOverride": [ @@ -580,10 +772,14 @@ ], "cppcheck-missingReturn": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-moduloAlwaysTrueFalse": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-moduloofone": [ @@ -594,6 +790,8 @@ ], "cppcheck-multiplySizeof": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-nanInArithmeticExpression": [ @@ -601,18 +799,26 @@ ], "cppcheck-negativeArraySize": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-negativeContainerIndex": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-negativeIndex": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-negativeMemoryAllocationSize": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-noConstructor": [ @@ -620,10 +826,14 @@ ], "cppcheck-noCopyConstructor": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-noDestructor": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-noExplicitConstructor": [ @@ -631,34 +841,50 @@ ], "cppcheck-noOperatorEq": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-nullPointer": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-nullPointerArithmetic": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-nullPointerArithmeticRedundantCheck": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-nullPointerDefaultArg": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-nullPointerRedundantCheck": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-objectIndex": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-operatorEqMissingReturnStatement": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-operatorEqRetRefThis": [ @@ -669,10 +895,14 @@ ], "cppcheck-operatorEqToSelf": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-operatorEqVarError": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-oppositeExpression": [ @@ -680,18 +910,26 @@ ], "cppcheck-oppositeInnerCondition": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-overlappingStrcmp": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-overlappingWriteFunction": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-overlappingWriteUnion": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-passedByValue": [ @@ -699,10 +937,14 @@ ], "cppcheck-pointerAdditionResultNotNull": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-pointerArithBool": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-pointerLessThanZero": [ @@ -719,6 +961,8 @@ ], "cppcheck-pointerSize": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-postfixOperator": [ @@ -729,10 +973,14 @@ ], "cppcheck-publicAllocationError": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-pureVirtualCall": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-purgedConfiguration": [ @@ -740,10 +988,14 @@ ], "cppcheck-raceAfterInterlockedDecrement": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-readWriteOnlyFile": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-reademptycontainer": [ @@ -751,6 +1003,8 @@ ], "cppcheck-redundantAssignInSwitch": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:STYLE" ], "cppcheck-redundantAssignment": [ @@ -758,6 +1012,8 @@ ], "cppcheck-redundantBitwiseOperationInSwitch": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:STYLE" ], "cppcheck-redundantCondition": [ @@ -768,6 +1024,8 @@ ], "cppcheck-redundantCopyInSwitch": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:STYLE" ], "cppcheck-redundantCopyLocalConst": [ @@ -784,26 +1042,38 @@ ], "cppcheck-resourceLeak": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-rethrowNoCurrentException": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-returnAddressOfAutoVariable": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-returnAddressOfFunctionParameter": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-returnDanglingLifetime": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-returnLocalVariable": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-returnNonBoolInBooleanFunction": [ @@ -811,6 +1081,8 @@ ], "cppcheck-returnReference": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-returnStdMoveLocal": [ @@ -818,6 +1090,8 @@ ], "cppcheck-returnTempReference": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-sameIteratorExpression": [ @@ -825,14 +1099,20 @@ ], "cppcheck-seekOnAppendedFile": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-selfAssignment": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-selfInitialization": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-shadowArgument": [ @@ -846,6 +1126,8 @@ ], "cppcheck-shiftNegative": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-shiftNegativeLHS": [ @@ -853,22 +1135,32 @@ ], "cppcheck-shiftTooManyBits": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-shiftTooManyBitsSigned": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-signConversion": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-signedCharArrayIndex": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-sizeofCalculation": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-sizeofDereferencedVoidPointer": [ @@ -876,10 +1168,14 @@ ], "cppcheck-sizeofDivisionMemfunc": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-sizeofFunctionCall": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-sizeofVoid": [ @@ -887,26 +1183,38 @@ ], "cppcheck-sizeofsizeof": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-sizeofwithnumericparameter": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-sizeofwithsilentarraypointer": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-sprintfOverlappingData": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-staticStringCompare": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-stlBoundaries": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-stlFindInsert": [ @@ -914,6 +1222,8 @@ ], "cppcheck-stlIfFind": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-stlIfStrFind": [ @@ -921,6 +1231,8 @@ ], "cppcheck-stlOutOfBounds": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-stlSize": [ @@ -928,6 +1240,8 @@ ], "cppcheck-stlcstr": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-stlcstrParam": [ @@ -938,26 +1252,38 @@ ], "cppcheck-stlcstrthrow": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-strPlusChar": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-stringCompare": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-stringLiteralWrite": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-suspiciousCase": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-suspiciousSemicolon": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-syntaxError": [ @@ -965,6 +1291,8 @@ ], "cppcheck-thisSubtraction": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-thisUseAfterFree": [ @@ -972,6 +1300,8 @@ ], "cppcheck-throwInNoexceptFunction": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-toomanyconfigs": [ @@ -991,38 +1321,56 @@ ], "cppcheck-uninitDerivedMemberVar": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-uninitDerivedMemberVarPrivate": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-uninitMemberVar": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-uninitMemberVarPrivate": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-uninitStructMember": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-uninitdata": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-uninitstring": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-uninitvar": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-unknownEvaluationOrder": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-unknownMacro": [ @@ -1045,6 +1393,8 @@ ], "cppcheck-unsafeClassRefMember": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-unsignedLessThanZero": [ @@ -1067,6 +1417,8 @@ ], "cppcheck-unusedLabelSwitch": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-unusedLabelSwitchConfiguration": [ @@ -1086,6 +1438,8 @@ ], "cppcheck-useClosedFile": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-useInitializationList": [ @@ -1102,18 +1456,26 @@ ], "cppcheck-uselessAssignmentPtrArg": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-uselessCallsCompare": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-uselessCallsEmpty": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-uselessCallsRemove": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-uselessCallsSubstr": [ @@ -1124,22 +1486,32 @@ ], "cppcheck-va_end_missing": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-va_list_usedBeforeStarted": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-va_start_referencePassed": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-va_start_subsequentCalls": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-va_start_wrongParameter": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-varFuncNullUB": [ @@ -1150,38 +1522,56 @@ ], "cppcheck-virtualCallInConstructor": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:STYLE" ], "cppcheck-virtualDestructor": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-writeReadOnlyFile": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-wrongPipeParameterSize": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-wrongPrintfScanfArgNum": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-wrongPrintfScanfParameterPositionError": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-wrongmathcall": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ], "cppcheck-zerodiv": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:HIGH" ], "cppcheck-zerodivcond": [ "profile:default", + "profile:extreme", + "profile:sensitive", "severity:MEDIUM" ] } diff --git a/docs/analyzer/user_guide.md b/docs/analyzer/user_guide.md index b19a90360f..b66a96e193 100644 --- a/docs/analyzer/user_guide.md +++ b/docs/analyzer/user_guide.md @@ -132,6 +132,7 @@ subcommand. ``` usage: CodeChecker check [-h] [-o OUTPUT_DIR] [-t {plist}] [-q] [--keep-gcc-include-fixed] [--keep-gcc-intrin] + [--add-gcc-include-dirs-with-isystem] (-b COMMAND | -l LOGFILE) [-j JOBS] [-c] [--compile-uniqueing COMPILE_UNIQUEING] [--report-hash {context-free,context-free-v2,diagnostic-message}] @@ -179,6 +180,11 @@ optional arguments: be kept among the implicit include paths. Use this flag if Clang analysis fails with error message related to __builtin symbols. (default: False) + --add-gcc-include-dirs-with-isystem + Implicit include directories are appended to the + analyzer command with -idirafter. If -isystem is needed + instead, as it was used before CodeChecker 6.24.1, this + flag can be used. (default: False) --compile-uniqueing COMPILE_UNIQUEING Specify the method the compilation actions in the compilation database are uniqued before analysis. CTU @@ -756,6 +762,16 @@ object files as input) should be captured. For further details see [this documentation](/analyzer/tools/build-logger/README.md). +If your build tool overrides `LD_LIBRARY_PATH` during the build process, then +`ldlogger.so` will not be found. The best solution is to making sure +that the LD_LIBRARY_PATH is not overridden, only extended. +If this is not possible, you can work around the situation by +specifying the absolute path of the `ldlogger.so` in the `LD_PRELOAD`: + +```sh +LD_PRELOAD=/ld_logger/lib/x86_64/ldlogger.so CodeChecker log -o compile_commands.json -b "make -j2" +``` + #### Change user inside the build command If we change user inside the build command of the CodeChecker log command before the actual compiler invocation, the compilation database will be empty: @@ -921,6 +937,7 @@ usage: CodeChecker analyze [-h] [-j JOBS] OUTPUT_PATH [--compiler-info-file COMPILER_INFO_FILE] [--keep-gcc-include-fixed] [--keep-gcc-intrin] + [--add-gcc-include-dirs-with-isystem] [-t {plist}] [-q] [-c] [--compile-uniqueing COMPILE_UNIQUEING] [--report-hash {context-free,context-free-v2,diagnostic-message}] @@ -980,6 +997,11 @@ optional arguments: be kept among the implicit include paths. Use this flag if Clang analysis fails with error message related to __builtin symbols. (default: False) + --add-gcc-include-dirs-with-isystem + Implicit include directories are appended to the + analyzer command with -idirafter. If -isystem is needed + instead, as it was used before CodeChecker 6.24.1, this + flag can be used. (default: False) -t {plist}, --type {plist}, --output-format {plist} Specify the format the analysis results should use. (default: plist) @@ -1374,6 +1396,14 @@ analysis unless `--keep-gcc-include-fixed` or `--keep-gcc-intrin` flag is given. For further information see [GCC incompatibilities](gcc_incompatibilities.md). +The GCC compiler's implicit include directories are appended to the analyzer +command with `-idirafter`. There are other flags which can be used instead of +`-idirafter`, such as , `-I`, `-isystem`, etc. They have a +[priority order](https://gcc.gnu.org/onlinedocs/gcc/Directory-Options.html) in +which compilers search header files. Prior to CodeChecker 6.24.1, the +`-isystem` flag was used instead of `-idirafter`. If you need to the `-isystem` +flag, you can use the `--add-gcc-include-dirs-with-isystem` flag. + #### Toggling checkers The list of checkers to be used in the analysis can be fine-tuned with the diff --git a/scripts/result_listing/compare_results.py b/scripts/result_listing/compare_results.py new file mode 100644 index 0000000000..87af114491 --- /dev/null +++ b/scripts/result_listing/compare_results.py @@ -0,0 +1,57 @@ +# ------------------------------------------------------------------------- +# +# 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 +# +# ------------------------------------------------------------------------- + +import argparse +import json +import sys + +def __sorted_results(result_file): + results = json.load(result_file) + results.sort( + key=lambda report: report['bugHash'] + str(report['reportId'])) + return results + +def __print_missing(needles, haystack, haystack_name): + for needle in needles: + if needle not in haystack: + print(f"Report not found in {haystack_name}:") + print(json.dumps(needle, indent=4)) + return True + return False + +def main(): + parser = argparse.ArgumentParser( + description="Compares two CodeChecker results. The results should be " + "generated by 'CodeChecker cmd results' command.") + + parser.add_argument( + "result1", + type=argparse.FileType("r"), + help="Path of the first result.") + + parser.add_argument( + "result2", + type=argparse.FileType("r"), + help="Path of the second result.") + + args = parser.parse_args() + + result1 = __sorted_results(args.result1) + result2 = __sorted_results(args.result2) + + found_missing = False + found_missing |= __print_missing(result1, result2, "result2") + found_missing |= __print_missing(result2, result1, "result1") + + args.result1.close() + args.result2.close() + + return 1 if found_missing else 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/scripts/result_listing/query_all_reports.py b/scripts/result_listing/query_all_reports.py new file mode 100644 index 0000000000..deb6298415 --- /dev/null +++ b/scripts/result_listing/query_all_reports.py @@ -0,0 +1,128 @@ +# ------------------------------------------------------------------------- +# +# 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 +# +# ------------------------------------------------------------------------- +import argparse +import functools +import json +import os +import subprocess +import sys +from multiprocess import Pool +from pathlib import Path +from typing import List, Tuple + + +def parse_args(): + """ + Initialize global variables based on command line arguments. These + variables are global because get_results() uses them. That function is used + as a callback which doesn't get this info as parameters. + """ + parser = argparse.ArgumentParser( + description="Fetch all reports of products") + + parser.add_argument( + '--url', + default='localhost:8001', + help='URL of a CodeChecker server.') + + parser.add_argument( + '--output', + required=True, + help='Output folder for generated JSON files.') + + parser.add_argument( + '-j', '--jobs', + default=1, + type=int, + help='Get reports in this many parallel jobs.') + + parser.add_argument( + '--products', + nargs='+', + help='List of products to fetch reports for. By default, all products ' + 'are fetched.') + + return parser.parse_args() + + +def __get_keys_from_list(out) -> List[str]: + """ + Get all keys from a JSON list. + """ + return map(lambda prod: next(iter(prod.keys())), json.loads(out)) + + +def result_getter(args: argparse.Namespace): + def get_results(product_run: Tuple[str, str]): + product, run = product_run + print(product, run) + + out, _ = subprocess.Popen([ + 'CodeChecker', 'cmd', 'results', run, + '-o', 'json', + '--url', f'{args.url}/{product}'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE).communicate() + + reports = sorted( + json.loads(out), key=lambda report: report['reportId']) + + run = run.replace('/', '_') + with open(Path(args.output) / f'{product}_{run}.json', 'w', + encoding='utf-8') as f: + json.dump(reports, f) + + return get_results + + +def get_all_products(url: str) -> List[str]: + """ + Get all products from a CodeChecker server. + + :param url: URL of a CodeChecker server. + :return: List of product names. + """ + out, _ = subprocess.Popen( + ['CodeChecker', 'cmd', 'products', 'list', '-o', 'json', '--url', url], + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL).communicate() + + return __get_keys_from_list(out) + + +def dump_products(args: argparse.Namespace): + for product in args.products: + f = functools.partial(lambda p, r: (p, r), product) + with subprocess.Popen([ + 'CodeChecker', 'cmd', 'runs', + '--url', f'{args.url}/{product}', + '-o', 'json'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) as proc: + runs = list(__get_keys_from_list(proc.stdout.read())) + + with Pool(args.jobs) as p: + p.map(result_getter(args), map(f, runs)) + + +def main(): + args = parse_args() + + os.makedirs(args.output, exist_ok=True) + + if not args.products: + args.products = get_all_products(args.url) + + dump_products(args) + + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/web/server/codechecker_server/api/mass_store_run.py b/web/server/codechecker_server/api/mass_store_run.py index 87ab4e2a52..71e21d9dc2 100644 --- a/web/server/codechecker_server/api/mass_store_run.py +++ b/web/server/codechecker_server/api/mass_store_run.py @@ -1319,11 +1319,12 @@ def get_skip_handler( .join(DBReport, DBReport.bug_id == ReviewStatusRule.bug_hash) \ .filter(sqlalchemy.and_( DBReport.run_id == run_id, - DBReport.review_status_is_in_source.is_(False), - ReviewStatusRule.bug_hash.in_(self.__new_report_hashes))) + DBReport.review_status_is_in_source.is_(False))) # Set the newly stored reports for review_status, db_report in reports_to_rs_rules: + if db_report.bug_id not in self.__new_report_hashes: + continue old_report = None if db_report.bug_id in report_to_report_id: old_report = report_to_report_id[db_report.bug_id][0] diff --git a/web/server/codechecker_server/api/report_server.py b/web/server/codechecker_server/api/report_server.py index 6d7b167f69..f3e2a7a6b5 100644 --- a/web/server/codechecker_server/api/report_server.py +++ b/web/server/codechecker_server/api/report_server.py @@ -20,7 +20,7 @@ import zlib from copy import deepcopy -from collections import OrderedDict, defaultdict +from collections import OrderedDict, defaultdict, namedtuple from datetime import datetime, timedelta from typing import Any, Dict, List, Optional, Set, Tuple @@ -406,7 +406,8 @@ def process_report_filter( if keep_all_annotations: OR.append(or_( ReportAnnotations.key != key, - ReportAnnotations.value.in_(values))) + *[ReportAnnotations.value.ilike(conv(v)) + for v in values])) else: OR.append(and_( ReportAnnotations.key == key, @@ -1962,117 +1963,114 @@ def getRunResults(self, run_ids, limit, offset, sort_types, ReportAnnotations.value)])).label(f"annotation_{col}") if report_filter.isUnique: + # A report annotation filter cannot be set in WHERE clause if + # we use annotation parameters in aggregate functions to + # create a pivot table. Instead of filtering report + # annotations in WHERE clause, we should use HAVING clause + # only for filtering aggregate functions. + # TODO: Fixing report annotation filter in every report server + # endpoint function. + annotations_backup = report_filter.annotations + report_filter.annotations = None filter_expression, join_tables = process_report_filter( - session, run_ids, report_filter, cmp_data, - keep_all_annotations=False) + session, run_ids, report_filter, cmp_data) sort_types, sort_type_map, order_type_map = \ get_sort_map(sort_types, True) - selects = [func.max(Report.id).label('id')] - for sort in sort_types: - sorttypes = sort_type_map.get(sort.type) - for sorttype in sorttypes: - if sorttype[0] != 'bug_path_length': - selects.append(func.max(sorttype[0]) - .label(sorttype[1])) - - unique_reports = session.query(*selects) - unique_reports = apply_report_filter(unique_reports, - filter_expression, - join_tables) - if report_filter.annotations is not None: - unique_reports = unique_reports.outerjoin( - ReportAnnotations, - Report.id == ReportAnnotations.report_id) - unique_reports = unique_reports \ - .group_by(Report.bug_id) \ - .subquery() - - # Sort the results. - sorted_reports = session.query(unique_reports.c.id) - sorted_reports = sort_results_query(sorted_reports, - sort_types, - sort_type_map, - order_type_map, - True) - sorted_reports = sorted_reports \ - .limit(limit).offset(offset).subquery() - - q = session.query(Report, - File.filename, - *annotation_cols.values()) \ - .join(Checker, - Report.checker_id == Checker.id) \ - .options(contains_eager(Report.checker)) \ - .outerjoin( - File, - Report.file_id == File.id) \ - .outerjoin( - ReportAnnotations, - Report.id == ReportAnnotations.report_id) \ - .outerjoin(sorted_reports, - sorted_reports.c.id == Report.id) \ - .filter(sorted_reports.c.id.isnot(None)) - - if report_filter.annotations is not None: + # TODO: Create a helper function for common section of unique + # and non unique modes. + sub_query = session.query(Report, + File.filename, + Checker.analyzer_name, + Checker.checker_name, + Checker.severity, + func.row_number().over( + partition_by=Report.bug_id, + order_by=desc(Report.id) + ).label("row_num"), + *annotation_cols.values()) \ + .join(Checker, + Report.checker_id == Checker.id) \ + .options(contains_eager(Report.checker)) \ + .outerjoin(File, + Report.file_id == File.id) \ + .outerjoin(ReportAnnotations, + Report.id == + ReportAnnotations.report_id) + + sub_query = apply_report_filter(sub_query, + filter_expression, + join_tables, + [File, Checker]) + + sub_query = sub_query.group_by(Report.id, File.id, Checker.id) + + if annotations_backup: annotations = defaultdict(list) - for annotation in report_filter.annotations: + for annotation in annotations_backup: annotations[annotation.first].append(annotation.second) OR = [] for key, values in annotations.items(): - OR.append(annotation_cols[key].in_(values)) - q = q.having(or_(*OR)) + OR.extend([annotation_cols[key].ilike(conv(v)) + for v in values]) + sub_query = sub_query.having(or_(*OR)) - # We have to sort the results again because an ORDER BY in a - # subtable is broken by the JOIN. - q = sort_results_query(q, - sort_types, - sort_type_map, - order_type_map) - q = q.group_by(Report.id, File.id, Checker.id) + sub_query = sort_results_query(sub_query, + sort_types, + sort_type_map, + order_type_map) - query_result = q.all() + sub_query = sub_query.subquery().alias() + + q = session.query(sub_query) \ + .filter(sub_query.c.row_num == 1) \ + .limit(limit).offset(offset) + + QueryResult = namedtuple('QueryResult', sub_query.c.keys()) + query_result = [QueryResult(*row) for row in q.all()] # Get report details if it is required. report_details = {} if get_details: - report_ids = [r[0].id for r in query_result] + report_ids = [r.id for r in query_result] report_details = get_report_details(session, report_ids) for row in query_result: - report, filename = row[0], row[1] annotations = { - k: v for k, v in zip(annotation_keys, row[2:]) - if v is not None} + k: v for k, v in zip( + annotation_keys, + [getattr(row, 'annotation_testcase', None), + getattr(row, 'annotation_timestamp', None)] + ) if v is not None} review_data = create_review_data( - report.review_status, - report.review_status_message, - report.review_status_author, - report.review_status_date, - report.review_status_is_in_source) + row.review_status, + row.review_status_message, + row.review_status_author, + row.review_status_date, + row.review_status_is_in_source) results.append( - ReportData(runId=report.run_id, - bugHash=report.bug_id, - checkedFile=filename, - checkerMsg=report.checker_message, - reportId=report.id, - fileId=report.file_id, - line=report.line, - column=report.column, - analyzerName=report.checker.analyzer_name, - checkerId=report.checker.checker_name, - severity=report.checker.severity, + ReportData(runId=row.run_id, + bugHash=row.bug_id, + checkedFile=row.filename, + checkerMsg=row.checker_message, + reportId=row.id, + fileId=row.file_id, + line=row.line, + column=row.column, + analyzerName=row.analyzer_name, + checkerId=row.checker_name, + severity=row.severity, reviewData=review_data, detectionStatus=detection_status_enum( - report.detection_status), - detectedAt=str(report.detected_at), - fixedAt=str(report.fixed_at), - bugPathLength=report.path_length, - details=report_details.get(report.id), + row.detection_status), + detectedAt=str(row.detected_at), + fixedAt=str(row.fixed_at), + bugPathLength=row.path_length, + details=report_details.get(row.id), annotations=annotations)) else: # not is_unique filter_expression, join_tables = process_report_filter( @@ -2129,7 +2127,8 @@ def getRunResults(self, run_ids, limit, offset, sort_types, OR = [] for key, values in annotations.items(): - OR.append(annotation_cols[key].in_(values)) + OR.extend([annotation_cols[key].ilike(conv(v)) + for v in values]) q = q.having(or_(*OR)) q = q.limit(limit).offset(offset) @@ -3072,7 +3071,7 @@ def getCheckerStatusVerificationDetails(self, run_ids, report_filter): checkerName=checker_name, analyzerName=analyzer_name, enabled=[], - disabled=all_run_id, + disabled=all_run_id.copy(), severity=severity, closed=0, outstanding=0 diff --git a/web/server/vue-cli/package-lock.json b/web/server/vue-cli/package-lock.json index 44b321a06c..3a4aacb0d5 100644 --- a/web/server/vue-cli/package-lock.json +++ b/web/server/vue-cli/package-lock.json @@ -64,7 +64,7 @@ "vue-style-loader": "^4.1.3", "vue-template-compiler": "^2.6.14", "vuetify-loader": "^1.7.3", - "webpack": "^5.76.0", + "webpack": "^5.94.0", "webpack-cli": "^4.9.1", "webpack-dev-server": "^4.7.3", "webpack-merge": "^5.8.0" @@ -3161,16 +3161,6 @@ "@types/json-schema": "*" } }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -3795,10 +3785,10 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", "dev": true, "peerDependencies": { "acorn": "^8" @@ -6380,9 +6370,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz", - "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -16407,21 +16397,20 @@ } }, "node_modules/webpack": { - "version": "5.91.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", - "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "dev": true, "dependencies": { - "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", "@webassemblyjs/wasm-parser": "^1.12.1", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", + "acorn-import-attributes": "^1.9.5", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.16.0", + "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -19616,16 +19605,6 @@ "@types/json-schema": "*" } }, - "@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "requires": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -20182,10 +20161,10 @@ "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true }, - "acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", "dev": true, "requires": {} }, @@ -22061,9 +22040,9 @@ } }, "enhanced-resolve": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz", - "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "dev": true, "requires": { "graceful-fs": "^4.2.4", @@ -29517,21 +29496,20 @@ } }, "webpack": { - "version": "5.91.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", - "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "dev": true, "requires": { - "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", "@webassemblyjs/wasm-parser": "^1.12.1", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", + "acorn-import-attributes": "^1.9.5", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.16.0", + "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", diff --git a/web/server/vue-cli/package.json b/web/server/vue-cli/package.json index f31789b897..db8c1d9b92 100644 --- a/web/server/vue-cli/package.json +++ b/web/server/vue-cli/package.json @@ -82,7 +82,7 @@ "vue-style-loader": "^4.1.3", "vue-template-compiler": "^2.6.14", "vuetify-loader": "^1.7.3", - "webpack": "^5.76.0", + "webpack": "^5.94.0", "webpack-cli": "^4.9.1", "webpack-dev-server": "^4.7.3", "webpack-merge": "^5.8.0" diff --git a/web/server/vue-cli/src/components/Statistics/CheckerCoverage/CheckerCoverageStatistics.vue b/web/server/vue-cli/src/components/Statistics/CheckerCoverage/CheckerCoverageStatistics.vue index 8bf49fca32..dda2d7a5b3 100644 --- a/web/server/vue-cli/src/components/Statistics/CheckerCoverage/CheckerCoverageStatistics.vue +++ b/web/server/vue-cli/src/components/Statistics/CheckerCoverage/CheckerCoverageStatistics.vue @@ -190,12 +190,6 @@ export default { async runIds() { this.noProperRun = false; - }, - - "$route"() { - if (this.runs && this.$route.query.run !== this.actualRunNames) { - this.$router.go(); - } } }, diff --git a/web/server/vue-cli/src/views/Statistics.vue b/web/server/vue-cli/src/views/Statistics.vue index cef90f95a0..cdf41c4c72 100644 --- a/web/server/vue-cli/src/views/Statistics.vue +++ b/web/server/vue-cli/src/views/Statistics.vue @@ -7,7 +7,8 @@ :show-remove-filtered-reports="false" :report-count="reportCount" :show-diff-type="false" - :show-compare-to="false" + :show-compare-to=" + $router.currentRoute.name != 'checker-coverage-statistics'" @refresh="refresh" /> diff --git a/web/tests/functional/authentication/test_permission_view.py b/web/tests/functional/authentication/test_permission_view.py index 43b16fa05b..778e5569f7 100644 --- a/web/tests/functional/authentication/test_permission_view.py +++ b/web/tests/functional/authentication/test_permission_view.py @@ -102,19 +102,19 @@ def test_get_access_control_with_permission_viewer(self): self.assertTrue(len(permissions), 1) self.assertIn("PERMISSION_VIEW", permissions) - self.assertDictContainsSubset( - {'permission_view_group': ['PERMISSION_VIEW']}, - global_permissions.group) + self.assertLessEqual( + {'permission_view_group': ['PERMISSION_VIEW']}.items(), + global_permissions.group.items()) product_permissions = access_control.productPermissions self.assertTrue(product_permissions) auth_product_permissions = product_permissions["authentication"] - self.assertDictContainsSubset( + self.assertLessEqual( {'cc': ['PRODUCT_STORE'], 'john': ['PRODUCT_STORE'], - 'admin': ['PRODUCT_ADMIN']}, - auth_product_permissions.user) + 'admin': ['PRODUCT_ADMIN']}.items(), + auth_product_permissions.user.items()) # Previously, the test files in this directory interfered at one # another, and the group permission dict wasn't empty. Check git blame. diff --git a/web/tests/functional/diff_remote/test_diff_remote.py b/web/tests/functional/diff_remote/test_diff_remote.py index 77814c51a4..711a8292ee 100644 --- a/web/tests/functional/diff_remote/test_diff_remote.py +++ b/web/tests/functional/diff_remote/test_diff_remote.py @@ -492,7 +492,7 @@ def test_get_diff_checker_counts_core_unresolved(self): # Unresolved core checkers. test_res = {'core.StackAddressEscape': 3, 'core.DivideZero': 10} - self.assertDictContainsSubset(test_res, diff_dict) + self.assertLessEqual(test_res.items(), diff_dict.items()) def test_get_diff_res_count_unresolved(self): """ @@ -574,7 +574,7 @@ def test_get_diff_checker_counts_all_unresolved(self): 'deadcode.DeadStores': 6, 'core.StackAddressEscape': 3, 'core.DivideZero': 10} - self.assertDictContainsSubset(diff_dict, test_res) + self.assertLessEqual(diff_dict.items(), test_res.items()) def test_get_diff_severity_counts_all_unresolved(self): """ diff --git a/web/tests/functional/dynamic_results/test_dynamic_results.py b/web/tests/functional/dynamic_results/test_dynamic_results.py index 14519dd3a3..d3b7e3bafc 100644 --- a/web/tests/functional/dynamic_results/test_dynamic_results.py +++ b/web/tests/functional/dynamic_results/test_dynamic_results.py @@ -134,12 +134,25 @@ def test_filter_by_attribute(self): results = self._cc_client.getRunResults( None, 500, 0, None, testcase_filter, None, False) - self.assertEqual(len(results), 3) + self.assertEqual(len(results), 2) self.assertTrue(all(map( lambda report: report.annotations['testcase'] == 'TC-1', results))) + testcase_filter = ReportFilter(annotations=[Pair( + first='testcase', + second='TC-*')]) + + results = self._cc_client.getRunResults( + None, 500, 0, None, testcase_filter, None, False) + + self.assertEqual(len(results), 3) + + self.assertTrue(all(map( + lambda report: report.annotations['testcase'].startswith('TC-'), + results))) + def test_count_by_attribute(self): """ Test the report count functions with the usage of report annotations. @@ -156,4 +169,4 @@ def test_count_by_attribute(self): num = self._cc_client.getRunResultCount( None, testcase_filter, None) - self.assertEqual(num, 3) + self.assertEqual(num, 2) diff --git a/web/tests/functional/report_viewer_api/test_report_counting.py b/web/tests/functional/report_viewer_api/test_report_counting.py index c0eb079622..9e00d1ad19 100644 --- a/web/tests/functional/report_viewer_api/test_report_counting.py +++ b/web/tests/functional/report_viewer_api/test_report_counting.py @@ -148,7 +148,7 @@ def test_run1_all_checkers(self): checkers_dict = dict((res.name, res.count) for res in checker_counts) self.assertGreaterEqual(len(checker_counts), len(self.run1_checkers)) - self.assertDictContainsSubset(self.run1_checkers, checkers_dict) + self.assertLessEqual(self.run1_checkers.items(), checkers_dict.items()) def test_run1_core_checkers(self): """ @@ -167,7 +167,7 @@ def test_run1_core_checkers(self): if "core." in k} self.assertGreaterEqual(len(checker_counts), len(core_checkers)) - self.assertDictContainsSubset(core_checkers, checkers_dict) + self.assertLessEqual(core_checkers.items(), checkers_dict.items()) def test_run2_all_checkers(self): """ @@ -182,7 +182,7 @@ def test_run2_all_checkers(self): checkers_dict = dict((res.name, res.count) for res in checker_counts) self.assertGreaterEqual(len(checker_counts), len(self.run2_checkers)) - self.assertDictContainsSubset(self.run2_checkers, checkers_dict) + self.assertLessEqual(self.run2_checkers.items(), checkers_dict.items()) def test_run1_run2_all_checkers(self): """ @@ -225,7 +225,7 @@ def test_run1_run2_core_checkers(self): all_core = dict(r1_core + r2_core) self.assertGreaterEqual(len(checker_counts), len(all_core)) - self.assertDictContainsSubset(all_core, checkers_dict) + self.assertLessEqual(all_core.items(), checkers_dict.items()) def test_run1_all_severity(self): """ @@ -368,7 +368,7 @@ def test_run1_run2_file_filters(self): Counter(stack_r1) + Counter(stack_r2)) self.assertGreaterEqual(len(res), len(test_res)) - self.assertDictContainsSubset(test_res, res) + self.assertLessEqual(test_res.items(), res.items()) def test_run1_run2_all_checker_msg(self): """ diff --git a/web/tests/projects/dynamic/main.c_cppcheck_0212cbc2c7194b7a5d431a18ff51bb1c.plist b/web/tests/projects/dynamic/main.c_cppcheck_0212cbc2c7194b7a5d431a18ff51bb1c.plist index a532d466b2..8bdabb75b9 100644 --- a/web/tests/projects/dynamic/main.c_cppcheck_0212cbc2c7194b7a5d431a18ff51bb1c.plist +++ b/web/tests/projects/dynamic/main.c_cppcheck_0212cbc2c7194b7a5d431a18ff51bb1c.plist @@ -20,7 +20,7 @@ testcase TS-1 testcase - TC-1 + TC-2 location