From 51cbf72b2ca29d64c17ced80f34a6a2095a2be53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Csord=C3=A1s?= Date: Fri, 30 Apr 2021 10:07:31 +0200 Subject: [PATCH] [analyzer] Handle getting options for old analyzer version In case of Clang Static Analyzer we try to get analyzer / checker configuration options by using the `-analyzer-config-help` / `-analyzer-checker-option-help` options which were introduced in Clang 8. With this patch we will handle the use case when someone is using an older version of clang which doesn't support these options. --- .../analyzers/clangsa/analyzer.py | 28 ++++++++++++++++--- .../codechecker_analyzer/cmd/analyzers.py | 7 +++++ analyzer/codechecker_analyzer/cmd/checkers.py | 15 +++++++++- 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/analyzer/codechecker_analyzer/analyzers/clangsa/analyzer.py b/analyzer/codechecker_analyzer/analyzers/clangsa/analyzer.py index 370e813e72..8aaafe7dd1 100644 --- a/analyzer/codechecker_analyzer/analyzers/clangsa/analyzer.py +++ b/analyzer/codechecker_analyzer/analyzers/clangsa/analyzer.py @@ -15,6 +15,8 @@ import shlex import subprocess +from typing import Dict, List + from codechecker_common.logger import get_logger from codechecker_analyzer import env @@ -33,7 +35,11 @@ LOG = get_logger('analyzer') -def parse_clang_help_page(command, start_label, environ): +def parse_clang_help_page( + command: List[str], + start_label: str, + environ: Dict[str, str] +) -> List[str]: """ Parse the clang help page starting from a specific label. Returns a list of (flag, description) tuples. @@ -41,11 +47,13 @@ def parse_clang_help_page(command, start_label, environ): try: help_page = subprocess.check_output( command, + stderr=subprocess.STDOUT, env=environ, universal_newlines=True, encoding="utf-8", errors="ignore") except (subprocess.CalledProcessError, OSError): + LOG.debug("Failed to run '%s' command!", command) return [] help_page = help_page[help_page.index(start_label) + len(start_label):] @@ -131,7 +139,11 @@ def add_checker_config(self, checker_cfg): self.__checker_configs.append(checker_cfg) @classmethod - def get_analyzer_checkers(cls, cfg_handler, environ): + def get_analyzer_checkers( + cls, + cfg_handler: config_handler.ClangSAConfigHandler, + environ: Dict[str, str] + ) -> List[str]: """Return the list of the supported checkers.""" checker_list_args = clang_options.get_analyzer_checkers_cmd( cfg_handler, @@ -139,7 +151,11 @@ def get_analyzer_checkers(cls, cfg_handler, environ): return parse_clang_help_page(checker_list_args, 'CHECKERS:', environ) @classmethod - def get_checker_config(cls, cfg_handler, environ): + def get_checker_config( + cls, + cfg_handler: config_handler.ClangSAConfigHandler, + environ: Dict[str, str] + ) -> List[str]: """Return the list of checker config options.""" checker_config_args = clang_options.get_checker_config_cmd( cfg_handler, @@ -147,7 +163,11 @@ def get_checker_config(cls, cfg_handler, environ): return parse_clang_help_page(checker_config_args, 'OPTIONS:', environ) @classmethod - def get_analyzer_config(cls, cfg_handler, environ): + def get_analyzer_config( + cls, + cfg_handler: config_handler.ClangSAConfigHandler, + environ: Dict[str, str] + ) -> List[str]: """Return the list of analyzer config options.""" analyzer_config_args = clang_options.get_analyzer_config_cmd( cfg_handler) diff --git a/analyzer/codechecker_analyzer/cmd/analyzers.py b/analyzer/codechecker_analyzer/cmd/analyzers.py index f20137a2a7..d878617b9e 100644 --- a/analyzer/codechecker_analyzer/cmd/analyzers.py +++ b/analyzer/codechecker_analyzer/cmd/analyzers.py @@ -13,6 +13,7 @@ import argparse import subprocess +import sys from codechecker_analyzer import analyzer_context from codechecker_analyzer import env @@ -173,6 +174,12 @@ def uglify(text): configs = analyzer_class.get_analyzer_config(config_handler, analyzer_environment) + if not configs: + LOG.error("Failed to get analyzer configuration options for '%s' " + "analyzer! Please try to upgrade your analyzer version " + "to use this feature.", analyzer) + sys.exit(1) + rows = [(':'.join((analyzer, c[0])), c[1]) if 'details' in args else (':'.join((analyzer, c[0])),) for c in configs] diff --git a/analyzer/codechecker_analyzer/cmd/checkers.py b/analyzer/codechecker_analyzer/cmd/checkers.py index ae20588011..d258b70b89 100644 --- a/analyzer/codechecker_analyzer/cmd/checkers.py +++ b/analyzer/codechecker_analyzer/cmd/checkers.py @@ -282,16 +282,29 @@ def format_guideline(guideline): header = list(map(uglify, header)) rows = [] + analyzer_failures = [] for analyzer in working_analyzers: config_handler = analyzer_config_map.get(analyzer) analyzer_class = analyzer_types.supported_analyzers[analyzer] configs = analyzer_class.get_checker_config(config_handler, analyzer_environment) + if not configs: + analyzer_failures.append(analyzer) + continue + rows.extend((':'.join((analyzer, c[0])), c[1]) if 'details' in args else (':'.join((analyzer, c[0])),) for c in configs) - print(twodim.to_str(args.output_format, header, rows)) + if rows: + print(twodim.to_str(args.output_format, header, rows)) + + if analyzer_failures: + LOG.error("Failed to get checker configuration options for '%s' " + "analyzer(s)! Please try to upgrade your analyzer version " + "to use this feature.", ', '.join(analyzer_failures)) + sys.exit(1) + return if args.guideline is not None and len(args.guideline) == 0: