diff --git a/analyzer/codechecker_analyzer/analyzers/analyzer_base.py b/analyzer/codechecker_analyzer/analyzers/analyzer_base.py index 8d0eb4358f..125b2c68ae 100644 --- a/analyzer/codechecker_analyzer/analyzers/analyzer_base.py +++ b/analyzer/codechecker_analyzer/analyzers/analyzer_base.py @@ -17,7 +17,7 @@ import sys import shlex -from typing import Optional +from typing import List, Optional from codechecker_analyzer import analyzer_context from codechecker_common.logger import get_logger @@ -25,6 +25,20 @@ LOG = get_logger('analyzer') +class AnalyzerConfig: + def __init__(self, option: str, documentation: str, value_type: type): + self.option = option + self.documentation = documentation + self.value_type = value_type + + +class CheckerConfig: + def __init__(self, checker: str, option: str, documentation: str): + self.checker = checker + self.option = option + self.documentation = documentation + + class SourceAnalyzer(metaclass=ABCMeta): """ Base class for different source analyzers. @@ -138,6 +152,14 @@ def get_analyzer_checkers(cls): """ raise NotImplementedError("Subclasses should implement this!") + @classmethod + def get_analyzer_config(cls) -> List[AnalyzerConfig]: + return [] + + @classmethod + def get_checker_config(cls) -> List[CheckerConfig]: + return [] + def post_analyze(self, result_handler): """ Run immediately after the analyze function. diff --git a/analyzer/codechecker_analyzer/analyzers/clangsa/analyzer.py b/analyzer/codechecker_analyzer/analyzers/clangsa/analyzer.py index 7379c6374a..6ae05f168a 100644 --- a/analyzer/codechecker_analyzer/analyzers/clangsa/analyzer.py +++ b/analyzer/codechecker_analyzer/analyzers/clangsa/analyzer.py @@ -121,11 +121,13 @@ class ClangSA(analyzer_base.SourceAnalyzer): __ctu_autodetection = None - __additional_analyzer_config = { - 'cc-verbatim-args-file': + __additional_analyzer_config = [ + analyzer_base.AnalyzerConfig( + 'cc-verbatim-args-file', 'A file path containing flags that are forwarded verbatim to the ' - 'analyzer tool. E.g.: cc-verbatim-args-file=' - } + 'analyzer tool. E.g.: cc-verbatim-args-file=', + util.ExistingPath) + ] def __init__(self, cfg_handler, buildaction): super().__init__(cfg_handler, buildaction) @@ -290,7 +292,7 @@ def get_analyzer_checkers( return parse_clang_help_page(command, 'CHECKERS:') @classmethod - def get_checker_config(cls) -> List[str]: + def get_checker_config(cls) -> List[analyzer_base.CheckerConfig]: """ Return the list of checker config options. @@ -311,10 +313,14 @@ def get_checker_config(cls) -> List[str]: if version_info.major_version >= 9: command.append("-analyzer-checker-option-help-developer") - return parse_clang_help_page(command, 'OPTIONS:') + result = [] + for cfg, doc in parse_clang_help_page(command, 'OPTIONS:'): + result.append(analyzer_base.CheckerConfig(*cfg.split(':', 1), doc)) + + return result @classmethod - def get_analyzer_config(cls) -> List[Tuple[str, str]]: + def get_analyzer_config(cls) -> List[analyzer_base.AnalyzerConfig]: """Return the list of analyzer config options.""" command = [cls.analyzer_binary(), "-cc1"] @@ -323,8 +329,11 @@ def get_analyzer_config(cls) -> List[Tuple[str, str]]: command.append("-analyzer-config-help") native_config = parse_clang_help_page(command, 'OPTIONS:') + native_config = map( + lambda cfg: analyzer_base.AnalyzerConfig(cfg[0], cfg[1], str), + native_config) - return native_config + list(cls.__additional_analyzer_config.items()) + return list(native_config) + list(cls.__additional_analyzer_config) def post_analyze(self, result_handler): """ diff --git a/analyzer/codechecker_analyzer/analyzers/clangtidy/analyzer.py b/analyzer/codechecker_analyzer/analyzers/clangtidy/analyzer.py index cb88a6c7b9..b4e32617db 100644 --- a/analyzer/codechecker_analyzer/analyzers/clangtidy/analyzer.py +++ b/analyzer/codechecker_analyzer/analyzers/clangtidy/analyzer.py @@ -256,11 +256,19 @@ class ClangTidy(analyzer_base.SourceAnalyzer): # Cache object for get_analyzer_checkers(). __analyzer_checkers = None - __additional_analyzer_config = { - 'cc-verbatim-args-file': + __additional_analyzer_config = [ + analyzer_base.AnalyzerConfig( + 'cc-verbatim-args-file', 'A file path containing flags that are forwarded verbatim to the ' - 'analyzer tool. E.g.: cc-verbatim-args-file=' - } + 'analyzer tool. E.g.: cc-verbatim-args-file=', + util.ExistingPath), + analyzer_base.AnalyzerConfig( + 'take-config-from-directory', + 'The .clang-tidy config file should be taken into account when ' + 'analysis is executed through CodeChecker. Possible values: true, ' + 'false. Default: false', + str) + ] @classmethod def analyzer_binary(cls): @@ -334,24 +342,29 @@ def get_analyzer_checkers(cls): return [] @classmethod - def get_checker_config(cls): + def get_checker_config(cls) -> List[analyzer_base.CheckerConfig]: """ Return the checker configuration of the all of the supported checkers. """ try: - result = subprocess.check_output( + help_page = subprocess.check_output( [cls.analyzer_binary(), "-dump-config", "-checks=*"], env=analyzer_context.get_context() .get_env_for_bin(cls.analyzer_binary()), universal_newlines=True, encoding="utf-8", errors="ignore") - return parse_checker_config(result) except (subprocess.CalledProcessError, OSError): return [] + result = [] + for cfg, doc in parse_checker_config(help_page): + result.append(analyzer_base.CheckerConfig(*cfg.split(':', 1), doc)) + + return result + @classmethod - def get_analyzer_config(cls): + def get_analyzer_config(cls) -> List[analyzer_base.AnalyzerConfig]: """ Return the analyzer configuration with all checkers enabled. """ @@ -370,7 +383,11 @@ def get_analyzer_config(cls): except (subprocess.CalledProcessError, OSError): native_config = [] - return native_config + list(cls.__additional_analyzer_config.items()) + native_config = map( + lambda cfg: analyzer_base.AnalyzerConfig(cfg[0], cfg[1], str), + native_config) + + return list(native_config) + list(cls.__additional_analyzer_config) def get_checker_list(self, config) -> Tuple[List[str], List[str]]: """ diff --git a/analyzer/codechecker_analyzer/analyzers/cppcheck/analyzer.py b/analyzer/codechecker_analyzer/analyzers/cppcheck/analyzer.py index 77cc41a9c6..e2de4fa84c 100644 --- a/analyzer/codechecker_analyzer/analyzers/cppcheck/analyzer.py +++ b/analyzer/codechecker_analyzer/analyzers/cppcheck/analyzer.py @@ -11,6 +11,7 @@ from collections import defaultdict import sys +from typing import List from packaging.version import Version from pathlib import Path import os @@ -299,27 +300,34 @@ def get_analyzer_checkers(cls): return [] @classmethod - def get_analyzer_config(cls): + def get_analyzer_config(cls) -> List[analyzer_base.AnalyzerConfig]: """ Config options for cppcheck. """ return [ - ("addons", "A list of cppcheck addon files."), - ("libraries", "A list of cppcheck library definiton files."), - ("platform", "The platform configuration .xml file."), - ("inconclusive", "Enable inconclusive reports."), - ("cc-verbatim-args-file", - "A file path containing flags that are forwarded verbatim to the " - "analyzer tool. E.g.: cc-verbatim-args-file=") + analyzer_base.AnalyzerConfig( + "addons", + "A list of cppcheck addon files.", + str), + analyzer_base.AnalyzerConfig( + "libraries", + "A list of cppcheck library definiton files.", + str), + analyzer_base.AnalyzerConfig( + "platform", + "The platform configuration .xml file.", + str), + analyzer_base.AnalyzerConfig( + "inconclusive", + "Enable inconclusive reports.", + str), + analyzer_base.AnalyzerConfig( + "cc-verbatim-args-file", + "A file path containing flags that are forwarded verbatim to " + "the analyzer tool. E.g.: cc-verbatim-args-file=", + util.ExistingPath) ] - @classmethod - def get_checker_config(cls): - """ - TODO add config options for cppcheck checkers. - """ - return [] - def post_analyze(self, result_handler): """ Post process the reuslts after the analysis. diff --git a/analyzer/codechecker_analyzer/analyzers/gcc/analyzer.py b/analyzer/codechecker_analyzer/analyzers/gcc/analyzer.py index 6e5e4808c7..caf2d6d444 100644 --- a/analyzer/codechecker_analyzer/analyzers/gcc/analyzer.py +++ b/analyzer/codechecker_analyzer/analyzers/gcc/analyzer.py @@ -5,6 +5,7 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # # ------------------------------------------------------------------------- +from typing import List from packaging.version import Version import shlex import subprocess @@ -124,25 +125,19 @@ def get_analyzer_checkers(cls): return [] @classmethod - def get_analyzer_config(cls): + def get_analyzer_config(cls) -> List[analyzer_base.AnalyzerConfig]: """ Config options for gcc. """ # TODO return [ - ('cc-verbatim-args-file', - 'A file path containing flags that are forwarded verbatim to the ' - 'analyzer tool. E.g.: cc-verbatim-args-file=') + analyzer_base.AnalyzerConfig( + 'cc-verbatim-args-file', + 'A file path containing flags that are forwarded verbatim to ' + 'the analyzer tool. E.g.: cc-verbatim-args-file=', + util.ExistingPath) ] - @classmethod - def get_checker_config(cls): - """ - TODO add config options for gcc checkers. - """ - # TODO - return [] - def post_analyze(self, result_handler: GccResultHandler): """ Post process the reuslts after the analysis. diff --git a/analyzer/codechecker_analyzer/analyzers/infer/analyzer.py b/analyzer/codechecker_analyzer/analyzers/infer/analyzer.py index 5d0c3ca0e5..a94c782801 100644 --- a/analyzer/codechecker_analyzer/analyzers/infer/analyzer.py +++ b/analyzer/codechecker_analyzer/analyzers/infer/analyzer.py @@ -15,6 +15,7 @@ import json from pathlib import Path import sys +from typing import List from codechecker_common import util from codechecker_common.logger import get_logger @@ -138,23 +139,18 @@ def get_analyzer_checkers(cls): return [] @classmethod - def get_analyzer_config(cls): + def get_analyzer_config(cls) -> List[analyzer_base.AnalyzerConfig]: """ Config options for infer. """ return [ - ('cc-verbatim-args-file', - 'A file path containing flags that are forwarded verbatim to the ' - 'analyzer tool. E.g.: cc-verbatim-args-file=') + analyzer_base.AnalyzerConfig( + 'cc-verbatim-args-file', + 'A file path containing flags that are forwarded verbatim to ' + 'the analyzer tool. E.g.: cc-verbatim-args-file=', + util.ExistingPath) ] - @classmethod - def get_checker_config(cls): - """ - Config options for infer checkers. - """ - return [] - def analyze(self, analyzer_cmd, res_handler, proc_callback=None, env=None): env = analyzer_context.get_context().get_env_for_bin( diff --git a/analyzer/codechecker_analyzer/arg.py b/analyzer/codechecker_analyzer/arg.py index 22c5469b39..7899c3ff29 100644 --- a/analyzer/codechecker_analyzer/arg.py +++ b/analyzer/codechecker_analyzer/arg.py @@ -14,10 +14,10 @@ import re -AnalyzerConfig = collections.namedtuple( - 'AnalyzerConfig', ["analyzer", "option", "value"]) -CheckerConfig = collections.namedtuple( - "CheckerConfig", ["analyzer", "checker", "option", "value"]) +AnalyzerConfigArg = collections.namedtuple( + "AnalyzerConfigArg", ["analyzer", "option", "value"]) +CheckerConfigArg = collections.namedtuple( + "CheckerConfigArg", ["analyzer", "checker", "option", "value"]) AnalyzerBinary = collections.namedtuple( "AnalyzerBinary", ["analyzer", "path"]) @@ -106,7 +106,7 @@ def existing_abspath(path: str) -> str: return path -def analyzer_config(arg: str) -> AnalyzerConfig: +def analyzer_config(arg: str) -> AnalyzerConfigArg: """ This function can be used at "type" argument of argparse.add_argument(). It checks the format of --analyzer-config flag: @@ -120,11 +120,11 @@ def analyzer_config(arg: str) -> AnalyzerConfig: f"Analyzer option in wrong format: {arg}, should be " ":