Skip to content

Commit

Permalink
Add functionality to validate analyzer and checker options
Browse files Browse the repository at this point in the history
When specifying invalid checker and analyzer config,
the CodeChecker analyze command used to give a warning
but continued running.
This functionality throws an error and prevents the
analysis from running in this case.
  • Loading branch information
Nora Zinaeddin committed Apr 10, 2024
1 parent 42afcee commit 895a846
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 3 deletions.
88 changes: 85 additions & 3 deletions analyzer/codechecker_analyzer/cmd/analyze.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
Execute analysis over an already existing build.json compilation database.
"""


import argparse
import collections
import json
Expand All @@ -35,7 +34,6 @@
SkipListHandlers
from codechecker_common.util import load_json


LOG = logger.get_logger('system')

header_file_extensions = (
Expand Down Expand Up @@ -797,6 +795,81 @@ def add_arguments_to_parser(parser):
func=main, func_process_config_file=cmd_config.process_config_file)


def validate_analyzer_parameter(analyzer_conf):
"""
Ensure that the analyzer_config parameter is set to a valid value
by verifying if it belongs to the set of allowed values.
"""
analyzer = analyzer_conf.__getitem__(0).analyzer

if analyzer in analyzer_types.supported_analyzers:
analyzer_class = analyzer_types.supported_analyzers[analyzer]
else:
LOG.error(f"Invalid --analyzer-config parameter: {analyzer} is not "
f"a supported analyzer. The supported analyzers are "
f"{', '.join(a for a in analyzer_types.supported_analyzers)}"
f".")
return False

if not isinstance(analyzer_conf, list):
return False

analyzers = [analyzer[0] for analyzer in
analyzer_class.get_analyzer_config()]

# Make sure "[clang-tidy] Allow to override checker list #3203" works
if analyzer_class.ANALYZER_NAME == 'clang-tidy':
analyzers.append('take-config-from-directory')

for cfg in analyzer_conf:
if cfg.analyzer == analyzer_class.ANALYZER_NAME and \
cfg.option not in analyzers:
LOG.error(f"Invalid --analyzer-config parameter: "
f"{analyzer_class.ANALYZER_NAME} has no config named "
f"{cfg.option}. Use the 'CodeChecker analyzers "
f"--analyzer-config {analyzer_class.ANALYZER_NAME}' "
f"command to check available configs.")
return False
else:
return True


def validate_checker_parameter(checker_conf):
"""
Ensure that the checker_config parameter is set to a valid value
by verifying if it belongs to the set of allowed values.
"""
analyzer = checker_conf.__getitem__(0).analyzer

if analyzer in analyzer_types.supported_analyzers:
analyzer_class = analyzer_types.supported_analyzers[analyzer]
else:
LOG.error(f"Invalid --checker-config parameter: {analyzer} is not "
f"a supported analyzer. The supported analyzers are "
f"{', '.join(a for a in analyzer_types.supported_analyzers)}"
f".")
return False

if not isinstance(checker_conf, list):
return False

checkers = [checker[0] for checker in
analyzer_class.get_analyzer_checkers()]

for cfg in checker_conf:
if cfg.analyzer == analyzer_class.ANALYZER_NAME and \
cfg.checker not in checkers:
LOG.error(
f"Invalid --checker-config parameter: "
f"{analyzer_class.ANALYZER_NAME} has no checker named "
f"{cfg.checker}. Use the 'CodeChecker checkers "
f"--checker-config' command to see available checkers."
)
return False
else:
return True


def get_affected_file_paths(
file_filters: List[str],
compile_commands: tu_collector.CompilationDB
Expand All @@ -810,7 +883,7 @@ def get_affected_file_paths(
file_paths.append(file_filter)

if os.path.exists(file_filter) and \
file_filter.endswith(header_file_extensions):
file_filter.endswith(header_file_extensions):
LOG.info("Get dependent source files for '%s'...", file_filter)
dependent_sources = tu_collector.get_dependent_sources(
compile_commands, file_filter)
Expand Down Expand Up @@ -934,6 +1007,15 @@ def main(args):
"""
logger.setup_logger(args.verbose if 'verbose' in args else None)

# Validate analyzer and checker config (if any)
config_validator = {
'analyzer_config': validate_analyzer_parameter,
'checker_config': validate_checker_parameter
}
for conf, validate_func in config_validator.items():
if conf in args and not validate_func(getattr(args, conf)):
sys.exit(1)

if 'tidy_config' in args:
LOG.warning(
"--tidy-config is deprecated and will be removed in the next "
Expand Down
17 changes: 17 additions & 0 deletions analyzer/tests/unit/test_checker_handling.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
from codechecker_analyzer.analyzers.config_handler import CheckerState
from codechecker_analyzer.analyzers.clangtidy.config_handler \
import is_compiler_warning
from codechecker_analyzer.arg import AnalyzerConfig, CheckerConfig
from codechecker_analyzer.cmd.analyze import \
validate_analyzer_parameter, validate_checker_parameter

from codechecker_analyzer import analyzer_context
from codechecker_analyzer.buildlog import log_parser
Expand Down Expand Up @@ -396,6 +399,20 @@ def test_disable_clangsa_checkers(self):
analyzer.config_handler.checks()['Wreserved-id-macro'][0],
CheckerState.enabled)

def test_analyze_wrong_parameters(self):
"""
This test checks whether the analyze command detects if a wrong
--analyzer-config or --checker-config parameter is specified.
"""

analyzer_cfg = [AnalyzerConfig('asd', '123', 'value')]
checker_cfg = [CheckerConfig('clangsa', 'asd', '123', 'value')]

analyzer_func = validate_analyzer_parameter(analyzer_cfg)
checker_func = validate_checker_parameter(checker_cfg)

[self.assertFalse(func) for func in [analyzer_func, checker_func]]

def test_enable_all_disable_warning(self):
"""
If --enable-all is used and a warning is disabled, then the proper
Expand Down

0 comments on commit 895a846

Please sign in to comment.