Skip to content

Commit

Permalink
[feat] ClangTidy enables only selected checkers
Browse files Browse the repository at this point in the history
Due to the previous commit every checker is either enabled or disabled.
This resulted very long analyzer commands in ClangTidy because every
checker was explicitly enabled or disabled in the clang-tidy invocation.
ClangTidy has a lot of checkers and also all compiler warnings listed by
diagtool are also considered as checkers.

The goal of this commit is to simplify the clang-tidy invocation command.
First we disable all checkers and enable only those that are necessary to
run:

clang-tidy -checks=-*,enabled1,enabled2...

This patch takes into account that clang-diagnostic-... checkers don't
turn on the corresponding warnings. They have to be enabled by -W...
compiler flag.
  • Loading branch information
bruntib committed Sep 8, 2023
1 parent cbdfecc commit f2949d2
Show file tree
Hide file tree
Showing 19 changed files with 410 additions and 72 deletions.
102 changes: 81 additions & 21 deletions analyzer/codechecker_analyzer/analyzers/clangtidy/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import re
import shlex
import subprocess
from typing import List, Tuple
from typing import Iterable, List, Set, Tuple

import yaml

Expand Down Expand Up @@ -165,13 +165,55 @@ def get_warnings(env=None):
raise


def _add_asterisk_for_group(
subset_checkers: Iterable[str],
all_checkers: Set[str]
) -> List[str]:
"""
Since CodeChecker interprets checker name prefixes as checker groups, they
have to be added a '*' joker character when using them at clang-tidy
-checks flag. This function adds a '*' for each item in "checkers" if it's
a checker group, i.e. identified as a prefix for any checker name in
"all_checkers".
For example "readability-container" is a prefix of multiple checkers, so
this is converted to "readability-container-*". On the other hand
"performance-trivially-destructible" is a full checker name, so it remains
as is.
"""
def is_group_prefix_of(prefix: str, long: str) -> bool:
"""
Returns True if a checker(-group) name is prefix of another
checker name. For example bugprone-string is prefix of
bugprone-string-constructor but not of
bugprone-stringview-nullptr.
"""
prefix_split = prefix.split('-')
long_split = long.split('-')
return prefix_split == long_split[:len(prefix_split)]

def need_asterisk(checker: str) -> bool:
return any(
is_group_prefix_of(checker, long) and checker != long
for long in all_checkers)

result = []

for checker in subset_checkers:
result.append(checker + ('*' if need_asterisk(checker) else ''))

return result


class ClangTidy(analyzer_base.SourceAnalyzer):
"""
Constructs the clang tidy analyzer commands.
"""

ANALYZER_NAME = 'clang-tidy'

# Cache object for get_analyzer_checkers().
__analyzer_checkers = None

@classmethod
def analyzer_binary(cls):
return analyzer_context.get_context() \
Expand Down Expand Up @@ -204,6 +246,9 @@ def get_analyzer_checkers(cls):
Return the list of the all of the supported checkers.
"""
try:
if cls.__analyzer_checkers:
return cls.__analyzer_checkers

environ = analyzer_context.get_context().analyzer_env
result = subprocess.check_output(
[cls.analyzer_binary(), "-list-checks", "-checks=*"],
Expand All @@ -217,6 +262,8 @@ def get_analyzer_checkers(cls):
("clang-diagnostic-" + warning, "")
for warning in get_warnings(environ))

cls.__analyzer_checkers = checker_description

return checker_description
except (subprocess.CalledProcessError, OSError):
return []
Expand Down Expand Up @@ -266,24 +313,30 @@ def get_checker_list(self, config) -> Tuple[List[str], List[str]]:
clang-tidy and in some cases that would cause analysis error due to
some ClangSA bug.
"""
checkers = []
# Usage of a set will remove compiler warnings and clang-diagnostics
# which are the same.
# clang-tidy emits reports from its check in the same format as
# compiler diagnostics (like unused variables, etc). This makes it a
# little difficult to distinguish compiler warnings and clang-tidy
# check warnings. The only clue is that compiler warnings are emitted
# as if they came from a check called clang-diagnostic- (e.g.
# -Wunused-variable will emit a warning under the name
# clang-diagnostic-unused-variable).

# There are two ways to disable a compiler warning in clang-tidy,
# either by -Wno- or -checks=-clang-diagnostic- (note the dash before
# clang-diagnostic!). However, there is only one way to enable them:
# through -W. Using -checks=clang-diagnostic- does not enable the
# warning, but undoes -checks=-clang-diagnostic-.

# Since we disable all checks by default via -checks=-*, in order to
# enable a compiler warning, we first have to undo the -checks level
# disable and then enable it, so we need both
# -checks=compiler-diagnostic- and -W.
compiler_warnings = set()
enabled_checkers = set()

has_checker_config = \
config.checker_config and config.checker_config != '{}'

# Do not disable any clang-tidy checks explicitly, but don't run
# ClangSA checkers. ClangSA checkers are driven by an other
# analyzer in CodeChecker.
checkers.append('-clang-analyzer-*')

# For clang compiler warnings a correspoding
# clang-diagnostic error is generated by Clang tidy.
# They can be disabled by this glob -clang-diagnostic-*
checkers.append('clang-diagnostic-*')

# Config handler stores which checkers are enabled or disabled.
for checker_name, value in config.checks().items():
state, _ = value
Expand All @@ -307,28 +360,34 @@ def get_checker_list(self, config) -> Tuple[List[str], List[str]]:
"instead.")
if state == CheckerState.enabled:
compiler_warnings.add('-W' + warning_name)
enabled_checkers.add(checker_name)
elif state == CheckerState.disabled:
if config.enable_all:
LOG.warning("Disabling compiler warning with "
f"compiler flag '-d W{warning_name}' "
"is not supported.")
compiler_warnings.add('-Wno-' + warning_name)
# If a clang-diagnostic-... is enabled add it as a compiler
# warning as -W..., if it is disabled, tidy can suppress when
# specified in the -checks parameter list, so we add it there
# as -clang-diagnostic-... .
elif warning_type == CheckerType.analyzer:
if state == CheckerState.enabled:
compiler_warnings.add('-W' + warning_name)
elif state == CheckerState.disabled:
checkers.append('-' + checker_name)
enabled_checkers.add(checker_name)

continue

if state == CheckerState.enabled:
checkers.append(checker_name)
elif state == CheckerState.disabled:
checkers.append('-' + checker_name)
enabled_checkers.add(checker_name)

# By default all checkers are disabled and the enabled ones are added
# explicitly.
checkers = ['-*']

checkers += _add_asterisk_for_group(
enabled_checkers,
set(x[0] for x in ClangTidy.get_analyzer_checkers()))

# -checks=-clang-analyzer-* option is added to the analyzer command by
# default except when all analyzer config options come from .clang-tidy
# file. The content of this file overrides every other custom config
Expand Down Expand Up @@ -385,7 +444,8 @@ def construct_analyzer_cmd(self, result_handler):
analyzer_cmd.append('-Qunused-arguments')

# Enable these compiler warnings by default.
analyzer_cmd.extend(['-Wall', '-Wextra'])
if not config.enable_all:
analyzer_cmd.extend(['-Wno-everything'])

compile_lang = self.buildaction.lang

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,40 @@ CHECK#CodeChecker check --build "make compiler_warning_simple" --output $OUTPUT$
[] - To store results use the "CodeChecker store" command.
[] - See --help and the user guide for further options about parsing and storing the reports.
[] - ----=================----
Found no defects in compiler_warning.cpp
[MEDIUM] compiler_warning.cpp:3:7: unused variable 'i' [clang-diagnostic-unused-variable]
int i;
^

Found 1 defect(s) in compiler_warning.cpp


----==== Severity Statistics ====----
----------------------------
Severity | Number of reports
----------------------------
MEDIUM | 1
----------------------------
----=================----

----==== Checker Statistics ====----
---------------------------------------------------------------
Checker name | Severity | Number of reports
---------------------------------------------------------------
clang-diagnostic-unused-variable | MEDIUM | 1
---------------------------------------------------------------
----=================----

----==== File Statistics ====----
----------------------------------------
File name | Number of reports
----------------------------------------
compiler_warning.cpp | 1
----------------------------------------
----=================----

----======== Summary ========----
---------------------------------------------
Number of processed analyzer result files | 1
Number of analyzer reports | 0
Number of analyzer reports | 1
---------------------------------------------
----=================----
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
NORMAL#CodeChecker log --output $LOGFILE$ --build "make context_hash" --quiet
NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --report-hash=context-free --analyzers clang-tidy
NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --report-hash=context-free --analyzers clang-tidy --disable clang-diagnostic-unused-but-set-variable --disable clang-diagnostic-unused-but-set-parameter
NORMAL#CodeChecker parse $OUTPUT$ --print-steps
CHECK#CodeChecker check --build "make context_hash" --output $OUTPUT$ --quiet --print-steps --report-hash=context-free --analyzers clang-tidy
CHECK#CodeChecker check --build "make context_hash" --output $OUTPUT$ --quiet --print-steps --report-hash=context-free --analyzers clang-tidy --disable clang-diagnostic-unused-but-set-variable --disable clang-diagnostic-unused-but-set-parameter
--------------------------------------------------------------------------------
[] - Starting build...
[] - Using CodeChecker ld-logger.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
NORMAL#CodeChecker log --output $LOGFILE$ --build "make context_hash" --quiet
NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --report-hash=context-free-v2 --analyzers clang-tidy
NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --report-hash=context-free-v2 --analyzers clang-tidy --disable clang-diagnostic-unused-but-set-variable --disable clang-diagnostic-unused-but-set-parameter
NORMAL#CodeChecker parse $OUTPUT$ --print-steps
CHECK#CodeChecker check --build "make context_hash" --output $OUTPUT$ --quiet --print-steps --report-hash=context-free-v2 --analyzers clang-tidy
CHECK#CodeChecker check --build "make context_hash" --output $OUTPUT$ --quiet --print-steps --report-hash=context-free-v2 --analyzers clang-tidy --disable clang-diagnostic-unused-but-set-variable --disable clang-diagnostic-unused-but-set-parameter
--------------------------------------------------------------------------------
[] - Starting build...
[] - Using CodeChecker ld-logger.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
NORMAL#CodeChecker log --output $LOGFILE$ --build "make context_hash" --quiet
NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --analyzers clang-tidy
NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --analyzers clang-tidy --disable clang-diagnostic-unused-but-set-variable --disable clang-diagnostic-unused-but-set-parameter
NORMAL#CodeChecker parse $OUTPUT$ --print-steps
CHECK#CodeChecker check --build "make context_hash" --output $OUTPUT$ --quiet --print-steps --analyzers clang-tidy
CHECK#CodeChecker check --build "make context_hash" --output $OUTPUT$ --quiet --print-steps --analyzers clang-tidy --disable clang-diagnostic-unused-but-set-variable --disable clang-diagnostic-unused-but-set-parameter
--------------------------------------------------------------------------------
[] - Starting build...
[] - Using CodeChecker ld-logger.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
NORMAL#CodeChecker log --output $LOGFILE$ --build "make context_hash" --quiet
NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --report-hash=diagnostic-message --analyzers clang-tidy
NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --report-hash=diagnostic-message --analyzers clang-tidy --disable clang-diagnostic-unused-but-set-variable --disable clang-diagnostic-unused-but-set-parameter
NORMAL#CodeChecker parse $OUTPUT$ --print-steps
CHECK#CodeChecker check --build "make context_hash" --output $OUTPUT$ --quiet --print-steps --report-hash=diagnostic-message --analyzers clang-tidy
CHECK#CodeChecker check --build "make context_hash" --output $OUTPUT$ --quiet --print-steps --report-hash=diagnostic-message --analyzers clang-tidy --disable clang-diagnostic-unused-but-set-variable --disable clang-diagnostic-unused-but-set-parameter
--------------------------------------------------------------------------------
[] - Starting build...
[] - Using CodeChecker ld-logger.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
NORMAL#CodeChecker log --output $LOGFILE$ --build "make source_code_comments" --quiet
NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --analyzers clang-tidy --enable bugprone-sizeof-expression
NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --analyzers clang-tidy --enable bugprone-sizeof-expression --disable clang-diagnostic-unused-value
NORMAL#CodeChecker parse $OUTPUT$
CHECK#CodeChecker check --build "make source_code_comments" --output $OUTPUT$ --quiet --analyzers clang-tidy --enable bugprone-sizeof-expression
CHECK#CodeChecker check --build "make source_code_comments" --output $OUTPUT$ --quiet --analyzers clang-tidy --enable bugprone-sizeof-expression --disable clang-diagnostic-unused-value
--------------------------------------------------------------------------------
[] - Starting build...
[] - Using CodeChecker ld-logger.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
NORMAL#CodeChecker log --output $LOGFILE$ --build "make source_code_comments" --quiet
NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --analyzers clang-tidy --enable bugprone-sizeof-expression
NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --analyzers clang-tidy --enable bugprone-sizeof-expression --disable clang-diagnostic-unused-value
NORMAL#CodeChecker parse $OUTPUT$ --review-status unreviewed confirmed false_positive intentional
CHECK#CodeChecker check --build "make source_code_comments" --output $OUTPUT$ --quiet --analyzers clang-tidy --enable bugprone-sizeof-expression --review-status unreviewed confirmed false_positive intentional
CHECK#CodeChecker check --build "make source_code_comments" --output $OUTPUT$ --quiet --analyzers clang-tidy --enable bugprone-sizeof-expression --disable clang-diagnostic-unused-value --review-status unreviewed confirmed false_positive intentional
--------------------------------------------------------------------------------
[] - Starting build...
[] - Using CodeChecker ld-logger.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
NORMAL#CodeChecker log --output $LOGFILE$ --build "make source_code_comments" --quiet
NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --analyzers clang-tidy --enable bugprone-sizeof-expression
NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --analyzers clang-tidy --enable bugprone-sizeof-expression --disable clang-diagnostic-unused-value
NORMAL#CodeChecker parse $OUTPUT$ --review-status
CHECK#CodeChecker check --build "make source_code_comments" --output $OUTPUT$ --quiet --analyzers clang-tidy --enable bugprone-sizeof-expression --review-status
CHECK#CodeChecker check --build "make source_code_comments" --output $OUTPUT$ --quiet --analyzers clang-tidy --enable bugprone-sizeof-expression --disable clang-diagnostic-unused-value --review-status
--------------------------------------------------------------------------------
[] - Starting build...
[] - Using CodeChecker ld-logger.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
NORMAL#CodeChecker log --output $LOGFILE$ --build "make source_code_comments" --quiet
NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --analyzers clang-tidy --enable bugprone-sizeof-expression
NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --analyzers clang-tidy --enable bugprone-sizeof-expression --disable clang-diagnostic-unused-value
NORMAL#CodeChecker parse $OUTPUT$ --review-status confirmed
CHECK#CodeChecker check --build "make source_code_comments" --output $OUTPUT$ --quiet --analyzers clang-tidy --enable bugprone-sizeof-expression --review-status confirmed
CHECK#CodeChecker check --build "make source_code_comments" --output $OUTPUT$ --quiet --analyzers clang-tidy --enable bugprone-sizeof-expression --disable clang-diagnostic-unused-value --review-status confirmed
--------------------------------------------------------------------------------
[] - Starting build...
[] - Using CodeChecker ld-logger.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
NORMAL#CodeChecker log --output $LOGFILE$ --build "make source_code_comments" --quiet
NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --analyzers clang-tidy --enable bugprone-sizeof-expression
NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --analyzers clang-tidy --enable bugprone-sizeof-expression --disable clang-diagnostic-unused-value
NORMAL#CodeChecker parse $OUTPUT$ --review-status false_positive
CHECK#CodeChecker check --build "make source_code_comments" --output $OUTPUT$ --quiet --analyzers clang-tidy --enable bugprone-sizeof-expression --review-status false_positive
CHECK#CodeChecker check --build "make source_code_comments" --output $OUTPUT$ --quiet --analyzers clang-tidy --enable bugprone-sizeof-expression --disable clang-diagnostic-unused-value --review-status false_positive
--------------------------------------------------------------------------------
[] - Starting build...
[] - Using CodeChecker ld-logger.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
NORMAL#CodeChecker log --output $LOGFILE$ --build "make tidy_check" --quiet
NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --analyzers clang-tidy --disable misc --enable bugprone-sizeof-expression
NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --analyzers clang-tidy --disable misc --enable bugprone-sizeof-expression --disable clang-diagnostic-unused-variable
NORMAL#CodeChecker parse $OUTPUT$
CHECK#CodeChecker check --build "make tidy_check" --output $OUTPUT$ --quiet --analyzers clang-tidy --disable misc --enable bugprone-sizeof-expression
CHECK#CodeChecker check --build "make tidy_check" --output $OUTPUT$ --quiet --analyzers clang-tidy --disable misc --enable bugprone-sizeof-expression --disable clang-diagnostic-unused-variable
--------------------------------------------------------------------------------
[] - Starting build...
[] - Using CodeChecker ld-logger.
Expand Down
Loading

0 comments on commit f2949d2

Please sign in to comment.