Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bugfix] Don't trigger analyzer without enabled checkers #3970

Merged
merged 2 commits into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions analyzer/codechecker_analyzer/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from . import analyzer_context, analysis_manager, pre_analysis_manager, \
checkers
from .analyzers import analyzer_types
from .analyzers.config_handler import CheckerState
from .analyzers.config_handler import AnalyzerConfigHandler, CheckerState
from .analyzers.clangsa.analyzer import ClangSA

from .makefile import MakeFileCreator
Expand Down Expand Up @@ -124,6 +124,15 @@ def __get_ctu_data(ctu_dir):
'ctu_temp_fnmap_folder': 'tmpExternalFnMaps'}


def __has_enabled_checker(ch: AnalyzerConfigHandler):
"""
Returns True if at least one checker is enabled in the given config
handler.
"""
return any(state == CheckerState.enabled
for _, (state, _) in ch.checks().items())


def perform_analysis(args, skip_handlers, actions, metadata_tool,
compile_cmd_count):
"""
Expand Down Expand Up @@ -160,9 +169,16 @@ def perform_analysis(args, skip_handlers, actions, metadata_tool,
"the Clang Static Analyzer.")
return

actions = prepare_actions(actions, analyzers)
config_map = analyzer_types.build_config_handlers(args, analyzers)

no_checker_analyzers = \
[a for a in analyzers if not __has_enabled_checker(config_map[a])]
for analyzer in no_checker_analyzers:
LOG.info("No checkers enabled for %s", analyzer)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we tell if the user explicitly enabled an analyzer? If so, we should hard error, shouldn't we?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can be convinced, but I would feel a hard error a little strong here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll give you that one.

analyzers.remove(analyzer)

actions = prepare_actions(actions, analyzers)

available_checkers = set()
# Add profile names to the checkers list so we will not warn
# if a profile is enabled but there is no checker with that name.
Expand Down
13 changes: 13 additions & 0 deletions analyzer/codechecker_analyzer/cmd/analyze.py
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,15 @@ def add_arguments_to_parser(parser):
"the analysis. USE WISELY AND AT YOUR "
"OWN RISK!")

checkers_opts.add_argument('--disable-all',
dest="disable_all",
required=False,
default=argparse.SUPPRESS,
action='store_true',
help="Disable all checkers of all analyzers. "
"It is equivalent to using \"--disable "
"default\" as the first argument.")

checkers_opts.add_argument('--no-missing-checker-error',
dest="no_missing_checker_error",
action='store_true',
Expand Down Expand Up @@ -916,6 +925,10 @@ def main(args):

if 'enable_all' in args:
LOG.info("'--enable-all' was supplied for this analysis.")
if 'disable_all' in args:
if 'ordered_checkers' not in args:
args.ordered_checkers = []
args.ordered_checkers.insert(0, ('default', False))

# Enable alpha uniqueing by default if ctu analysis is used.
if 'none' in args.compile_uniqueing and 'ctu_phases' in args:
Expand Down
10 changes: 10 additions & 0 deletions analyzer/codechecker_analyzer/cmd/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,15 @@ def add_arguments_to_parser(parser):
"the analysis. USE WISELY AND AT YOUR "
"OWN RISK!")

checkers_opts.add_argument('--disable-all',
dest="disable_all",
required=False,
default=argparse.SUPPRESS,
action='store_true',
help="Disable all checkers of all analyzers. "
"It is equivalent to using \"--disable "
"default\" as the first argument.")

checkers_opts.add_argument('--no-missing-checker-error',
dest="no_missing_checker_error",
action='store_true',
Expand Down Expand Up @@ -860,6 +869,7 @@ def __update_if_key_exists(source, target, key):
'stats_relevance_threshold',
'stats_min_sample_count',
'enable_all',
'disable_all',
'ordered_checkers', # --enable and --disable.
'timeout',
'compile_uniqueing',
Expand Down
41 changes: 41 additions & 0 deletions analyzer/tests/functional/analyze/test_analyze.py
Original file line number Diff line number Diff line change
Expand Up @@ -1093,6 +1093,47 @@ def test_makefile_generation(self):
plist_files = glob.glob(os.path.join(self.report_dir, '*.plist'))
self.assertEqual(len(plist_files), 4)

def test_disable_all_checkers(self):
"""
If all checkers of an analyzer are disabled then the analysis shouldn't
trigger for that analyzer.
"""
build_json = os.path.join(self.test_workspace, "build.json")
build_log = [{"directory": self.test_workspace,
"command": "gcc -c main.c",
"file": "main.c"}]

with open(build_json, 'w',
encoding="utf-8", errors="ignore") as outfile:
json.dump(build_log, outfile)

for analyzer in ['clangsa', 'clang-tidy']:
analyze_cmd = [self._codechecker_cmd, "check", "-l", build_json,
"--analyzers", analyzer,
"--disable", "default"]
process = subprocess.Popen(
analyze_cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
errors="ignore")
out, _ = process.communicate()

self.assertIn(f"No checkers enabled for {analyzer}", out)

analyze_cmd = [self._codechecker_cmd, "check", "-l", build_json,
"--disable-all"]
process = subprocess.Popen(
analyze_cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
errors="ignore")
out, _ = process.communicate()

# Checkers of all 3 analyzers are disabled.
self.assertEqual(out.count("No checkers enabled for"), 3)

def test_analyzer_and_checker_config(self):
"""Test analyzer configuration through command line flags."""
build_json = os.path.join(self.test_workspace, "build_success.json")
Expand Down
20 changes: 19 additions & 1 deletion docs/analyzer/user_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
- [Toggling compiler warnings](#toggling-compiler-warnings)
- [Checker profiles](#checker-profiles)
- [`--enable-all`](#--enable-all)
- [`--disable-all`](#--disable-all)
- [Cross Translation Unit (CTU) analysis mode](#cross-translation-unit-ctu-analysis-mode)
- [Taint analysis configuration](#taint-analysis-configuration)
- [Statistical analysis mode](#statistical-analysis-mode)
Expand Down Expand Up @@ -145,7 +146,8 @@ usage: CodeChecker check [-h] [-o OUTPUT_DIR] [-t {plist}] [-q]
[--ctu-reanalyze-on-failure]
[--ctu-ast-mode {load-from-pch,parse-on-demand}]
[-e checker/group/profile] [-d checker/group/profile]
[--enable-all] [--print-steps] [--suppress SUPPRESS]
[--enable-all] [--disable-all] [--print-steps]
[--suppress SUPPRESS]
[--review-status [REVIEW_STATUS [REVIEW_STATUS ...]]]
[--verbose {info,debug,debug_analyzer}]

Expand Down Expand Up @@ -443,6 +445,8 @@ checker configuration:
and stability, and could even result in a total
failure of the analysis. USE WISELY AND AT YOUR OWN
RISK!
--disable-all Disable all checkers of all analyzers. It is equivalent
to using "--disable default" as the first argument.

output arguments:
--print-steps Print the steps the analyzers took in finding the
Expand Down Expand Up @@ -922,6 +926,7 @@ usage: CodeChecker analyze [-h] [-j JOBS]
[--ctu-reanalyze-on-failure]
[-e checker/group/profile]
[-d checker/group/profile] [--enable-all]
[--disable-all]
[--verbose {info,debug,debug_analyzer}]
input

Expand Down Expand Up @@ -1390,6 +1395,8 @@ checker configuration:
and stability, and could even result in a total
failure of the analysis. USE WISELY AND AT YOUR OWN
RISK!
--disable-all Disable all checkers of all analyzers. It is equivalent
to using "--disable default" as the first argument.
```

Both `--enable` and `--disable` take individual checkers, checker groups or
Expand Down Expand Up @@ -1501,6 +1508,17 @@ special checker groups: `alpha.`, `debug.`, `osx.`, `abseil-`, `android-`,

can be used to "further" enable `alpha.` checkers, and disable `misc` ones.

##### `--disable-all`

Some checkers are always enabled by default, because they are categorized in
`default` profile. This flag is equivalent to using `--disable default` which
technically disables all checkers. The motivation behind `--disable-all` is to
enable one specific checker and disable everything else:

```sh
CodeChecker check -l compile_commands.json --disable-all --enable core.DivideZero
```

#### Cross Translation Unit (CTU) analysis mode

If the `clang` static analyzer binary in your installation supports
Expand Down
Loading