Skip to content

Commit

Permalink
[cmd] Further developments of checker labels
Browse files Browse the repository at this point in the history
  • Loading branch information
bruntib committed Aug 31, 2021
1 parent 3cd4003 commit d380745
Show file tree
Hide file tree
Showing 19 changed files with 1,266 additions and 1,248 deletions.
2 changes: 1 addition & 1 deletion analyzer/codechecker_analyzer/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ def perform_analysis(args, skip_handler, context, actions, metadata_tool,
# if a profile is enabled but there is no checker with that name.

available_checkers.update(
context.checker_labels.get_constraint('profile', 'choice'))
context.checker_labels.get_description('profile'))

# Collect all the available checkers from the enabled analyzers.
for analyzer in config_map.items():
Expand Down
27 changes: 8 additions & 19 deletions analyzer/codechecker_analyzer/analyzer_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,20 @@ def __init__(self):
self._lib_dir_path = os.environ.get('CC_LIB_DIR', '')
self._data_files_dir_path = os.environ.get('CC_DATA_FILES_DIR', '')

# Use this environment variable for testing purposes only. This
# variable helps to configure which labels to use in this context.
labels_dir = os.path.join(self._data_files_dir_path,
'config', 'labels')
if 'CC_TEST_LABELS_DIR' in os.environ:
labels_dir = os.environ['CC_TEST_LABELS_DIR']

cfg_dict = self.__get_package_config()
self.env_vars = cfg_dict['environment_variables']

lcfg_dict = self.__get_package_layout()
self.pckg_layout = lcfg_dict['runtime']

self._checker_labels = CheckerLabels(self.checker_labels_map_file)
self._checker_labels = CheckerLabels(labels_dir)
self.__package_version = None
self.__package_build_date = None
self.__package_git_hash = None
Expand Down Expand Up @@ -268,24 +275,6 @@ def checker_plugin(self):

return os.path.join(self._data_files_dir_path, 'plugin')

@property
def checker_labels_map_file(self):
"""
Returns the path of checker-labels mapping config file. This file may
come from a custom location provided by CC_CHECKER_LABELS_FILE
environment variable.
"""
labels_map_file = os.environ.get('CC_CHECKER_LABELS_FILE')
if labels_map_file:
LOG.warning("Labels map file set through the "
"'CC_CHECKER_LABELS_FILE' environment variable: %s",
labels_map_file)

return labels_map_file

return os.path.join(self._data_files_dir_path, 'config',
'checker_labels.json')

@property
def checker_labels(self):
return self._checker_labels
Expand Down
6 changes: 3 additions & 3 deletions analyzer/codechecker_analyzer/analyzers/config_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,14 +160,14 @@ def initialize_checkers(self,
if 'profile:list' in map(itemgetter(0), cmdline_enable):
LOG.error("'list' is a reserved profile keyword. ")
LOG.error("Please choose another profile name in "
"%s/config/checker_labels.json and rebuild.",
"%s/config/descriptions.json and rebuild.",
analyzer_context.data_files_dir_path)
sys.exit(1)

if 'guideline:list' in map(itemgetter(0), cmdline_enable):
LOG.error("'list' is a reserved guideline keyword. ")
LOG.error("Please choose another guideline name in "
"%s/config/checker_labels.json and rebuild.",
"%s/config/descriptions.json and rebuild.",
analyzer_context.data_files_dir_path)
sys.exit(1)

Expand Down Expand Up @@ -211,7 +211,7 @@ def initialize_checkers(self,
# Construct a list of reserved checker names.
# (It is used to check if a profile name is valid.)
reserved_names = self.__gen_name_variations()
profiles = checker_labels.get_constraint('profile', 'choice')
profiles = checker_labels.get_description('profile')
guidelines = checker_labels.occurring_values('guideline')

for identifier, enabled in cmdline_enable:
Expand Down
2 changes: 1 addition & 1 deletion analyzer/codechecker_analyzer/cmd/analyze.py
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ def add_arguments_to_parser(parser):
------------------------------------------------
In CodeChecker there is a manual grouping of checkers. These groups are
determined by labels. The collection of labels is found in
config/checker_labels.json file. The goal of these labels is that you can
config/labels directory. The goal of these labels is that you can
enable or disable checkers by these labels. See the --label flag of
"CodeChecker checkers" command.
Expand Down
2 changes: 1 addition & 1 deletion analyzer/codechecker_analyzer/cmd/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ def add_arguments_to_parser(parser):
------------------------------------------------
In CodeChecker there is a manual grouping of checkers. These groups are
determined by labels. The collection of labels is found in
config/checker_labels.json file. The goal of these labels is that you can
config/labels directory. The goal of these labels is that you can
enable or disable checkers by these labels. See the --label flag of
"CodeChecker checkers" command.
Expand Down
24 changes: 9 additions & 15 deletions analyzer/codechecker_analyzer/cmd/checkers.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def get_argparser_ctor_args():
"""

data_files_dir_path = analyzer_context.get_context().data_files_dir_path
config_dir_path = os.path.join(data_files_dir_path, 'config')
labels_dir_path = os.path.join(data_files_dir_path, 'config', 'labels')
return {
'prog': 'CodeChecker checkers',
'formatter_class': arg.RawDescriptionDefaultHelpFormatter,
Expand All @@ -94,13 +94,8 @@ def get_argparser_ctor_args():
# directly.
'epilog': """
The list of checkers that are enabled or disabled by default can be edited by
editing "profile:default" labels in the file '{0}'.
Environment variables
------------------------------------------------
CC_CHECKER_LABELS_FILE Path of the checker-label mapping config file.
Default: '{0}'
""".format(os.path.join(config_dir_path, 'checker_labels.json')),
editing "profile:default" labels in the directory '{}'.
""".format(os.path.join(labels_dir_path)),

# Help is shown when the "parent" CodeChecker command lists the
# individual subcommands.
Expand Down Expand Up @@ -239,7 +234,7 @@ def match_guideline(checker_name, selected_guidelines):
selected_guidelines -- A list of guideline names or guideline rule IDs.
"""
labels = context.checker_labels.labels_of_checker(checker_name)
choices = context.checker_labels.get_constraint('guideline', 'choice')
choices = context.checker_labels.get_description('guideline')

for label, value in labels:
if (label == 'guideline' or label in choices) and \
Expand All @@ -265,8 +260,7 @@ def guideline_rules_for_checker(checker):
"""
result = defaultdict(list)
labels = context.checker_labels.labels_of_checker(checker)
guidelines = \
context.checker_labels.get_constraint('guideline', 'choice')
guidelines = context.checker_labels.get_description('guideline')

for label in labels:
if label[0] in guidelines:
Expand All @@ -285,10 +279,10 @@ def guideline_rules_for_checker(checker):

if 'details' in args:
header = ['Profile name', 'Description']
rows = cl.get_constraint('profile', 'choice').items()
rows = cl.get_description('profile').items()
else:
header = ['Profile name']
rows = [(key,) for key in cl.get_constraint('profile', 'choice')]
rows = [(key,) for key in cl.get_description('profile')]

if args.output_format in ['csv', 'json']:
header = list(map(uglify, header))
Expand Down Expand Up @@ -336,7 +330,7 @@ def guideline_rules_for_checker(checker):
if args.guideline is not None and len(args.guideline) == 0:
result = {}

for guideline in cl.get_constraint('guideline', 'choice'):
for guideline in cl.get_description('guideline'):
result[guideline] = set(cl.occurring_values(guideline))

header = ['Guideline', 'Rules']
Expand Down Expand Up @@ -376,7 +370,7 @@ def guideline_rules_for_checker(checker):

profile_checkers = []
if 'profile' in args:
available_profiles = cl.get_constraint('profile', 'choice')
available_profiles = cl.get_description('profile')

if args.profile not in available_profiles:
LOG.error("Checker profile '%s' does not exist!",
Expand Down
20 changes: 17 additions & 3 deletions analyzer/tests/unit/test_checker_handling.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@ def checkers_by_labels(self, labels):
return ['alpha.security']
elif labels[0] == 'guideline:sei-cert':
return ['alpha.core.CastSize', 'alpha.core.CastToStruct']
elif labels[0] == 'severity:LOW':
return ['security.insecureAPI.bcmp', 'alpha.llvm.Conventions']

def get_constraint(self, label, constraint):
if label == 'profile' and constraint == 'choice':
def get_description(self, label):
if label == 'profile':
return ['default', 'sensitive', 'security', 'portability',
'extreme']

Expand Down Expand Up @@ -146,6 +148,11 @@ def f(checks, checkers):
'alpha.core.CastSize',
'alpha.core.CastToStruct']

# Checkers covering some LOW severity rules.
low_severity = [
'security.insecureAPI.bcmp',
'alpha.llvm.Conventions']

checkers = []
checkers.extend(map(add_description, security_profile_alpha))
checkers.extend(map(add_description, default_profile))
Expand Down Expand Up @@ -223,13 +230,20 @@ def f(checks, checkers):
self.assertTrue(all_with_status(CheckerState.disabled)
(cfg_handler.checks(), cert_guideline))

# Enable "LOW" severity checkers.
cfg_handler = ClangSA.construct_config_handler(args, context)
cfg_handler.initialize_checkers(context, checkers,
[('severity:LOW', True)])
self.assertTrue(all_with_status(CheckerState.enabled)
(cfg_handler.checks(), low_severity))


class MockContextTidy:
class CheckerLabels:
def checkers_by_labels(self, labels):
return []

def get_constraint(self, checker, constraint):
def get_description(self, checker):
return []

def occurring_values(self, checker):
Expand Down
Loading

0 comments on commit d380745

Please sign in to comment.