Repository: ericsson/codechecker
Issue: Config file not applied

CodeChecker config file does not seem to be applied. I have created a json file according to documentation and help text, to configure my call to CodeChecker. However, regardless of what I put in there it does not seem to be applied.

CodeChecker analyzer version:
```
---------------------------------------------------------------
Kind                 | Version                                 
---------------------------------------------------------------
Base package version | 6.16.0                                  
Package build date   | 2021-06-10T12:34                        
Git commit ID (hash) | 8880695af939838b2e1e16b018c51ed66c6a5366
Git tag information  | 6.16                                    
---------------------------------------------------------------
```

Minimal config file (Note: I have also tried replacing "analyzer" with "analyze" as it says in the example config file in the documentation)
```
$ cat ccc.json 
{
  "analyzer": [
    "--disable=valist"
  ]
}
```

Invoke check
`$ CodeChecker check -b "ninja" -o ./reports --config ccc.json`

Info printout includes the disabled check (Full debug output, [codechecker_config_file_debug_output.txt](https://github.com/Ericsson/codechecker/files/6853925/codechecker_config_file_debug_output.txt))
```
[INFO 2021-07-21 09:57] - Enabled checkers:
clangsa: ..., valist.CopyToSelf, valist.Uninitialized, valist.Unterminated, valist.ValistBase
...
```

Disabling the same checker group using the disable parameter works fine
`CodeChecker check -b "ninja" -o ./reports -d=valist`

This is just an example, I cannot see that any configuration from file seem to be applied.



Changed files:
analyzer/codechecker_analyzer/cmd/check.py, analyzer/tests/functional/config/test_config.py, codechecker_common/cmd_config.py

diff --git a/analyzer/codechecker_analyzer/cmd/check.py b/analyzer/codechecker_analyzer/cmd/check.py
index 3e445216..d2e8e00f 100644
--- a/analyzer/codechecker_analyzer/cmd/check.py
+++ b/analyzer/codechecker_analyzer/cmd/check.py
@@ -22,7 +22,7 @@ from codechecker_analyzer import analyzer_context
 from codechecker_analyzer.analyzers import analyzer_types
 from codechecker_analyzer.arg import OrderedCheckersAction
 
-from codechecker_common import arg, logger
+from codechecker_common import arg, cmd_config, logger
 from codechecker_common.source_code_comment_handler import REVIEW_STATUS_VALUES
 
 from codechecker_analyzer.cmd.analyze import \\
@@ -706,6 +706,18 @@ output of "CodeChecker checkers --guideline" command.""")
                                   "analyzers which is experimental, take "
                                   "care when relying on it.")
 
+    output_opts.add_argument(
+        '--trim-path-prefix',
+        type=str,
+        nargs='*',
+        dest="trim_path_prefix",
+        required=False,
+        default=argparse.SUPPRESS,
+        help="Removes leading path from files which will be printed. So if "
+             "you have /a/b/c/x.cpp and /a/b/c/y.cpp then by removing "
+             "\\"/a/b/\\" prefix will print files like c/x.cpp and c/y.cpp. "
+             "If multiple prefix is given, the longest match will be removed.")
+
     parser.add_argument('--review-status',
                         nargs='*',
                         dest="review_status",
@@ -717,7 +729,8 @@ output of "CodeChecker checkers --guideline" command.""")
                             ', '.join(REVIEW_STATUS_VALUES)))
 
     logger.add_verbose_arguments(parser)
-    parser.set_defaults(func=main)
+    parser.set_defaults(
+        func=main, func_process_config_file=cmd_config.process_config_file)
 
 
 def main(args):
@@ -838,6 +851,7 @@ def main(args):
             input_format='plist'
         )
         __update_if_key_exists(args, parse_args, 'print_steps')
+        __update_if_key_exists(args, parse_args, 'trim_path_prefix')
         __update_if_key_exists(args, parse_args, 'review_status')
         __update_if_key_exists(args, parse_args, 'verbose')
         __update_if_key_exists(args, parse_args, 'skipfile')
diff --git a/analyzer/tests/functional/config/test_config.py b/analyzer/tests/functional/config/test_config.py
index 46769364..56ac8982 100644
--- a/analyzer/tests/functional/config/test_config.py
+++ b/analyzer/tests/functional/config/test_config.py
@@ -52,7 +52,7 @@ class TestConfig(unittest.TestCase):
             json.dump(build_log, outfile)
 
         # Test file contents
-        simple_file_content = "int main() { return 0; }"
+        simple_file_content = "int main() { return 1/0; }"
 
         # Write content to the test file
         with open(self.source_file, 'w',
@@ -203,4 +203,42 @@ class TestConfig(unittest.TestCase):
 
         out, returncode = self.__run_parse()
         print(out)
-        self.assertEqual(returncode, 0)
+        self.assertEqual(returncode, 2)
+
+    def test_check_config(self):
+        """
+        Run check command with a config file which enables the clangsa
+        analyzer only and parse the results with a parse command
+        config.
+        """
+        with open(self.config_file, 'w+',
+                  encoding="utf-8", errors="ignore") as config_f:
+            json.dump({
+                'analyzer': ['--analyzers', 'clangsa'],
+                'parse': ['--trim-path-prefix', '${HOME}']},
+                config_f)
+
+        check_cmd = [self._codechecker_cmd, "check",
+                     "-l", self.build_json,
+                     "-o", self.reports_dir,
+                     "--config", self.config_file]
+
+        # Run analyze.
+        process = subprocess.Popen(
+            check_cmd,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            encoding="utf-8",
+            errors="ignore")
+        out, _ = process.communicate()
+
+        print(out)
+        self.assertEqual(process.returncode, 2)
+        self.assertIn("clangsa analyzed simple.cpp", out)
+        self.assertNotIn("clang-tidy analyzed simple.cpp", out)
+
+        self.assertNotIn(self.source_file, out)
+
+        trimmed_file_path = self.source_file.replace(
+            os.path.expanduser("~") + os.path.sep, "")
+        self.assertIn(trimmed_file_path, out)
diff --git a/codechecker_common/cmd_config.py b/codechecker_common/cmd_config.py
index 5c7752e4..a625bd0b 100644
--- a/codechecker_common/cmd_config.py
+++ b/codechecker_common/cmd_config.py
@@ -7,12 +7,32 @@
 # -------------------------------------------------------------------------
 import os
 
+from typing import List
+
 from codechecker_common.util import load_json_or_empty
 from codechecker_common import logger
 
 LOG = logger.get_logger('system')
 
 
+def get_analyze_options(cfg) -> List[str]:
+    """ Get analyze related options. """
+    # The config value can be 'analyze' or 'analyzer'
+    # for backward compatibility.
+    analyze_cfg = cfg.get("analyze", [])
+    analyzer_cfg = cfg.get("analyzer", [])
+    if analyze_cfg:
+        if analyzer_cfg:
+            LOG.warning("There is an 'analyze' and an 'analyzer' "
+                        "config configuration option in the config "
+                        "file. Please use the 'analyze' value to be "
+                        "in sync with the subcommands.\\n"
+                        "Using the 'analyze' configuration.")
+        return analyze_cfg
+
+    return analyzer_cfg
+
+
 def process_config_file(args, subcommand_name):
     """ Handler to get config file options. """
     if 'config_file' not in args:
@@ -25,20 +45,12 @@ def process_config_file(args, subcommand_name):
         # configuration section name is analyzer.
         options = None
         if subcommand_name == 'analyze':
-            # The config value can be 'analyze' or 'analyzer'
-            # for backward compatibility.
-            analyze_cfg = cfg.get("analyze", [])
-            analyzer_cfg = cfg.get("analyzer", [])
-            if analyze_cfg:
-                if analyzer_cfg:
-                    LOG.warning("There is an 'analyze' and an 'analyzer' "
-                                "config configuration option in the config "
-                                "file. Please use the 'analyze' value to be "
-                                "in sync with the subcommands.\\n"
-                                "Using the 'analyze' configuration.")
-                options = analyze_cfg
-            elif analyzer_cfg:
-                options = analyzer_cfg
+            options = get_analyze_options(cfg)
+        elif subcommand_name == 'check':
+            options = [
+                *get_analyze_options(cfg),
+                *cfg.get("parse", [])
+            ]
         else:
             options = cfg.get(subcommand_name, [])