diff --git a/analyzer/codechecker_analyzer/analyzers/cppcheck/analyzer.py b/analyzer/codechecker_analyzer/analyzers/cppcheck/analyzer.py index 8d4559bb81..8f4d962fbb 100644 --- a/analyzer/codechecker_analyzer/analyzers/cppcheck/analyzer.py +++ b/analyzer/codechecker_analyzer/analyzers/cppcheck/analyzer.py @@ -16,13 +16,14 @@ import os import pickle import re +import shlex import shutil import subprocess import xml.etree.ElementTree as ET from codechecker_common.logger import get_logger -from codechecker_analyzer import analyzer_context +from codechecker_analyzer import analyzer_context, env from codechecker_analyzer.env import get_binary_in_path from .. import analyzer_base @@ -225,6 +226,8 @@ def construct_analyzer_cmd(self, result_handler): analyzer_cmd.append('--plist-output=' + str(output_dir)) + analyzer_cmd.extend(config.analyzer_extra_arguments) + analyzer_cmd.append(self.source_file) return analyzer_cmd @@ -389,6 +392,21 @@ def construct_config_handler(cls, args): handler.analyzer_config = analyzer_config + try: + with open(args.cppcheck_args_cfg_file, 'r', encoding='utf8', + errors='ignore') as sa_cfg: + handler.analyzer_extra_arguments = \ + re.sub(r'\$\((.*?)\)', + env.replace_env_var(args.cppcheck_args_cfg_file), + sa_cfg.read().strip()) + handler.analyzer_extra_arguments = \ + shlex.split(handler.analyzer_extra_arguments) + except IOError as ioerr: + LOG.debug_analyzer(ioerr) + except AttributeError as aerr: + # 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. diff --git a/analyzer/codechecker_analyzer/cmd/analyze.py b/analyzer/codechecker_analyzer/cmd/analyze.py index 16303980c7..dd3fe1f8fe 100644 --- a/analyzer/codechecker_analyzer/cmd/analyze.py +++ b/analyzer/codechecker_analyzer/cmd/analyze.py @@ -379,6 +379,13 @@ def add_arguments_to_parser(parser): cmd_config.add_option(analyzer_opts) + analyzer_opts.add_argument('--cppcheckargs', + dest="cppcheck_args_cfg_file", + required=False, + default=argparse.SUPPRESS, + help="File containing argument which will be " + "forwarded verbatim for Cppcheck.") + analyzer_opts.add_argument('--saargs', dest="clangsa_args_cfg_file", required=False, diff --git a/analyzer/tests/unit/test_checker_handling.py b/analyzer/tests/unit/test_checker_handling.py index 19824cbffd..2189b30128 100644 --- a/analyzer/tests/unit/test_checker_handling.py +++ b/analyzer/tests/unit/test_checker_handling.py @@ -14,11 +14,13 @@ from distutils import util import os import re +import tempfile import unittest from argparse import Namespace from codechecker_analyzer.analyzers.clangsa.analyzer import ClangSA from codechecker_analyzer.analyzers.clangtidy.analyzer import ClangTidy +from codechecker_analyzer.analyzers.cppcheck.analyzer import Cppcheck from codechecker_analyzer.analyzers.config_handler import CheckerState from codechecker_analyzer.analyzers.clangtidy.config_handler \ import is_compiler_warning @@ -49,6 +51,9 @@ def occurring_values(self, label): elif label == 'sei-cert': return ['rule1', 'rule2'] + def checkers(self, analyzer=None): + return [] + def create_analyzer_sa(): args = [] @@ -434,3 +439,37 @@ def test_clang_diags_as_compiler_warnings(self): # -Wno-unused-value. self.assertEqual(cmd.count('-Wvla'), 1) self.assertEqual(cmd.count('-Wvla-extension'), 1) + + +def create_analyzer_cppcheck(args, workspace): + cfg_handler = Cppcheck.construct_config_handler(args) + + action = { + 'file': 'main.cpp', + 'command': "g++ -o main main.cpp", + 'directory': workspace} + build_action = log_parser.parse_options(action) + + return Cppcheck(cfg_handler, build_action) + + +class CheckerHandlingCppcheckTest(unittest.TestCase): + def test_cppcheckargs(self): + """ + Check if the content of --cppcheckargs config file is properly passed + to the analyzer command. + """ + with tempfile.TemporaryDirectory() as tmp_ws: + cppcheckargs = os.path.join(tmp_ws, 'cppcheckargs') + with open(cppcheckargs, 'w', + encoding='utf-8', errors='ignore') as f: + f.write('--max-ctu-depth=42') + + args = Namespace() + args.cppcheck_args_cfg_file = cppcheckargs + + analyzer = create_analyzer_cppcheck(args, tmp_ws) + result_handler = create_result_handler(analyzer) + cmd = analyzer.construct_analyzer_cmd(result_handler) + + self.assertIn('--max-ctu-depth=42', cmd) diff --git a/docs/analyzer/user_guide.md b/docs/analyzer/user_guide.md index 96c814995a..00b6a576ba 100644 --- a/docs/analyzer/user_guide.md +++ b/docs/analyzer/user_guide.md @@ -917,7 +917,7 @@ usage: CodeChecker analyze [-h] [-j JOBS] [-n NAME] [--analyzers ANALYZER [ANALYZER ...]] [--capture-analysis-output] [--generate-reproducer] [--config CONFIG_FILE] - [--cppcheck-args CPPCHECK_ARGS_CFG_FILE] + [--cppcheckargs CPPCHECK_ARGS_CFG_FILE] [--saargs CLANGSA_ARGS_CFG_FILE] [--tidyargs TIDY_ARGS_CFG_FILE] [--timeout TIMEOUT] @@ -1122,9 +1122,9 @@ analyzer arguments: For more information see the docs: https://github.com/ Ericsson/codechecker/tree/master/docs/config_file.md (default: None) - --cppcheck-args CPPCHECK_ARGS_CFG_FILE - Configuration file to pass cppcheck command line - arguments. + --cppcheckargs CPPCHECK_ARGS_CFG_FILE + File containing argument which will be forwarded + verbatim for Cppcheck. --saargs CLANGSA_ARGS_CFG_FILE File containing argument which will be forwarded verbatim for the Clang Static Analyzer.