From fe19dc0b6e74dd268d752a8a30b5870487295b2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tibor=20Cserv=C3=A1k?= Date: Fri, 29 Nov 2024 16:23:00 +0100 Subject: [PATCH] [fix] Enable and disable checkers This PR fixes the enabling and disabling checker options, focusing on resolving ambiguities in option processing. Key updates: Ambiguity handling: When ambiguous checker options are provided with `-e` or `-d` flags, an error is now raised. The user receives suggestions for specifying the option by using namespaces (checker, prefix, guideline, etc.). If a checker name matches multiple checkers as a prefix, it triggers an ambiguity error. Suggestions are provided to help users choose between the checker name or the prefix. Additional namespaces: A new `checker` namespace is created. All label types can be a namespace as well, specified with a `:` separator before the checker option. Default profile settings improvements: Only those checkers enabled that has profile:default label in the label files. Prefix-based configuration is no longer applied for the default profile, ensuring precise and predictable behavior. Getting rid of `W` prefix: It is no longer available to enable or disable a warrning with W. --- .../analyzers/clangtidy/analyzer.py | 3 + .../analyzers/clangtidy/config_handler.py | 10 - .../analyzers/config_handler.py | 124 +++++++---- analyzer/codechecker_analyzer/checkers.py | 7 +- analyzer/codechecker_analyzer/cmd/analyze.py | 14 +- analyzer/codechecker_analyzer/cmd/check.py | 38 ++-- .../tests/functional/analyze/test_analyze.py | 2 +- .../compiler_warning_wno_group.output | 4 +- .../compiler_warning_wno_simple2.output | 4 +- .../compiler_warning_wunused.output | 4 +- analyzer/tests/unit/test_checker_handling.py | 199 ++++++++++++++++-- config/labels/analyzers/clang-tidy.json | 5 +- docs/analyzer/user_guide.md | 129 ++++++++---- web/server/vue-cli/src/views/NewFeatures.vue | 12 +- .../functional/report_viewer_api/__init__.py | 5 +- 15 files changed, 397 insertions(+), 163 deletions(-) diff --git a/analyzer/codechecker_analyzer/analyzers/clangtidy/analyzer.py b/analyzer/codechecker_analyzer/analyzers/clangtidy/analyzer.py index cbc803edfc..7f9a8b53df 100644 --- a/analyzer/codechecker_analyzer/analyzers/clangtidy/analyzer.py +++ b/analyzer/codechecker_analyzer/analyzers/clangtidy/analyzer.py @@ -288,6 +288,9 @@ def get_analyzer_checkers(cls): ("clang-diagnostic-" + warning, "") for warning in get_warnings()) + checker_description.append(("clang-diagnostic-error", + "Indicates compiler errors.")) + cls.__analyzer_checkers = checker_description return checker_description diff --git a/analyzer/codechecker_analyzer/analyzers/clangtidy/config_handler.py b/analyzer/codechecker_analyzer/analyzers/clangtidy/config_handler.py index 68625e126a..31a2a8ca0f 100644 --- a/analyzer/codechecker_analyzer/analyzers/clangtidy/config_handler.py +++ b/analyzer/codechecker_analyzer/analyzers/clangtidy/config_handler.py @@ -41,13 +41,3 @@ def add_checker(self, checker_name, description='', return super().add_checker(checker_name, description, state) - - def set_checker_enabled(self, checker_name, enabled=True): - """ - Enable checker, keep description if already set. - """ - if checker_name.startswith('W') or \ - checker_name.startswith('clang-diagnostic'): - self.add_checker(checker_name) - - super().set_checker_enabled(checker_name, enabled) diff --git a/analyzer/codechecker_analyzer/analyzers/config_handler.py b/analyzer/codechecker_analyzer/analyzers/config_handler.py index e701bd96af..8c602f863e 100644 --- a/analyzer/codechecker_analyzer/analyzers/config_handler.py +++ b/analyzer/codechecker_analyzer/analyzers/config_handler.py @@ -12,7 +12,6 @@ from abc import ABCMeta from enum import Enum -from string import Template import collections import platform import sys @@ -86,7 +85,7 @@ def add_checker(self, checker_name, description='', """ self.__available_checkers[checker_name] = (state, description) - def set_checker_enabled(self, checker_name, enabled=True): + def set_checker_enabled(self, checker_name, enabled=True, is_strict=False): """ Explicitly handle checker state, keep description if already set. """ @@ -94,7 +93,8 @@ def set_checker_enabled(self, checker_name, enabled=True): regex = "^" + re.escape(str(checker_name)) + "\\b.*$" for ch_name, values in self.__available_checkers.items(): - if re.match(regex, ch_name): + if (is_strict and ch_name == checker_name) \ + or (not is_strict and re.match(regex, ch_name)): _, description = values state = CheckerState.ENABLED if enabled \ else CheckerState.DISABLED @@ -118,7 +118,7 @@ def checks(self): """ return self.__available_checkers - def __gen_name_variations(self): + def __gen_name_variations(self, only_prefix=False): """ Generate all applicable name variations from the given checker list. """ @@ -134,9 +134,9 @@ def __gen_name_variations(self): # ['misc', 'misc-dangling', 'misc-dangling-handle'] # from 'misc-dangling-handle'. v = [delim.join(parts[:(i + 1)]) for i in range(len(parts))] - reserved_names += v + reserved_names += v[:-1] if only_prefix else v - return reserved_names + return list(set(reserved_names)) def initialize_checkers(self, checkers, @@ -184,7 +184,7 @@ def initialize_checkers(self, else: # Turn default checkers on. for checker in default_profile_checkers: - self.set_checker_enabled(checker) + self.set_checker_enabled(checker, is_strict=True) self.enable_all = enable_all # If enable_all is given, almost all checkers should be enabled. @@ -207,48 +207,82 @@ def initialize_checkers(self, self.set_checker_enabled(checker_name) # Set user defined enabled or disabled checkers from the command line. + for identifier, enabled in cmdline_enable: + labels = checker_labels.labels() \ + if callable(getattr(checker_labels, 'labels', None)) \ + else ["guideline", "profile", "severity", "sei-cert"] - # Construct a list of reserved checker names. - # (It is used to check if a profile name is valid.) - reserved_names = self.__gen_name_variations() - profiles = checker_labels.get_description('profile') - guidelines = checker_labels.occurring_values('guideline') + all_namespaces = ["checker", "prefix"] + labels - templ = Template("The ${entity} name '${identifier}' conflicts with a " - "checker name prefix '${identifier}'. Please use -e " - "${entity}:${identifier} to enable checkers of the " - "${identifier} ${entity} or use -e " - "prefix:${identifier} to select checkers which have " - "a name starting with '${identifier}'.") + all_options = dict(zip(labels, map( + checker_labels.occurring_values, labels))) - for identifier, enabled in cmdline_enable: - if "prefix:" in identifier: - identifier = identifier.replace("prefix:", "") - self.set_checker_enabled(identifier, enabled) - - elif ':' in identifier: - for checker in checker_labels.checkers_by_labels([identifier]): - self.set_checker_enabled(checker, enabled) - - elif identifier in profiles: - if identifier in reserved_names: - LOG.error(templ.substitute(entity="profile", - identifier=identifier)) + all_options["prefix"] = list(set(self.__gen_name_variations( + only_prefix=True))) + + all_options["checker"] = self.__available_checkers + + if ":" in identifier: + identifier_namespace = identifier.split(":")[0] + identifier = identifier.split(":", 1)[1] + + if identifier_namespace not in all_namespaces: + LOG.error("The %s namespace is not known. Please select" + "one of these existing namespace options: %s.", + identifier_namespace, ", ".join(all_namespaces)) sys.exit(1) - else: - for checker in checker_labels.checkers_by_labels( - [f'profile:{identifier}']): - self.set_checker_enabled(checker, enabled) - - elif identifier in guidelines: - if identifier in reserved_names: - LOG.error(templ.substitute(entity="guideline", - identifier=identifier)) + + # TODO: Each analyzer has its own config handler and is unaware + # of other checkers. To avoid not reliable error, we pass + # checker:'any_options' and prefix:'any_options'. This ensures + # enabling a checker doesn't inadvertently cause an error in a + # different analyzer. Ideally, there should be a centralized + # main configuration accessible to all analyzers. + if identifier not in all_options[identifier_namespace] \ + and identifier_namespace not in ("checker", "prefix"): + LOG.error("The %s identifier does not exist in the %s " + "namespace. Please select one of these " + "existing options: %s.", identifier, + identifier_namespace, ", ".join( + all_options[identifier_namespace])) sys.exit(1) - else: - for checker in checker_labels.checkers_by_labels( - [f'guideline:{identifier}']): - self.set_checker_enabled(checker, enabled) + + self.initialize_checkers_by_namespace( + identifier_namespace, identifier, enabled) else: - self.set_checker_enabled(identifier, enabled) + possible_options = {} + for label, options in all_options.items(): + if identifier in options: + possible_options[label] = identifier + + if len(possible_options) == 1: + self.initialize_checkers_by_namespace( + *list(possible_options.items())[0], enabled) + elif len(possible_options) > 1: + error_options = ", ".join(f"{label}:{option}" + for label, option + in possible_options.items()) + + LOG.error("The %s is ambigous. Please select one of these" + " options to clarify the checker list: %s.", + identifier, error_options) + sys.exit(1) + else: + # The identifier is not known but we just pass it + # and handle it in a different section. + continue + + def initialize_checkers_by_namespace(self, + identifier_namespace, + identifier, + enabled): + if identifier_namespace == "checker": + self.set_checker_enabled(identifier, enabled, is_strict=True) + elif identifier_namespace == "prefix": + self.set_checker_enabled(identifier, enabled) + else: + checker_labels = analyzer_context.get_context().checker_labels + for checker in checker_labels.checkers_by_labels( + [f"{identifier_namespace}:{identifier}"]): + self.set_checker_enabled(checker, enabled, is_strict=True) diff --git a/analyzer/codechecker_analyzer/checkers.py b/analyzer/codechecker_analyzer/checkers.py index 23f0c401c3..8e150f05d3 100644 --- a/analyzer/codechecker_analyzer/checkers.py +++ b/analyzer/codechecker_analyzer/checkers.py @@ -19,12 +19,7 @@ def available(ordered_checkers, available_checkers): """ missing_checkers = set() for checker_name, _ in ordered_checkers: - # TODO: This label list shouldn't be hard-coded here. - if checker_name.startswith('profile:') or \ - checker_name.startswith('guideline:') or \ - checker_name.startswith('severity:') or \ - checker_name.startswith('sei-cert:') or \ - checker_name.startswith('prefix:'): + if ":" in checker_name: continue name_match = False diff --git a/analyzer/codechecker_analyzer/cmd/analyze.py b/analyzer/codechecker_analyzer/cmd/analyze.py index 440018e243..a2d78c9a01 100644 --- a/analyzer/codechecker_analyzer/cmd/analyze.py +++ b/analyzer/codechecker_analyzer/cmd/analyze.py @@ -773,8 +773,11 @@ def add_arguments_to_parser(parser): "between the checker prefix " "group/profile/guideline name, the use of " "one of the following labels is " - "mandatory: 'prefix:', 'profile:', " - "'guideline:'.") + "mandatory: 'checker:', 'prefix:', " + "'profile:', 'guideline:'. If a checker " + "name matches multiple checkers as a " + "prefix, 'checker:' or 'prefix:' " + "namespace is required") checkers_opts.add_argument('-d', '--disable', dest="disable", @@ -792,8 +795,11 @@ def add_arguments_to_parser(parser): "between the checker prefix " "group/profile/guideline name, the use of " "one of the following labels is " - "mandatory: 'prefix:', 'profile:', " - "'guideline:'.") + "mandatory: 'checker:', 'prefix:', " + "'profile:', 'guideline:'. If a checker " + "name matches multiple checkers as a " + "prefix, 'checker:' or 'prefix:' " + "namespace is required") checkers_opts.add_argument('--enable-all', dest="enable_all", diff --git a/analyzer/codechecker_analyzer/cmd/check.py b/analyzer/codechecker_analyzer/cmd/check.py index fa2ef4e505..b6812beab3 100644 --- a/analyzer/codechecker_analyzer/cmd/check.py +++ b/analyzer/codechecker_analyzer/cmd/check.py @@ -707,35 +707,43 @@ def add_arguments_to_parser(parser): metavar='checker/group/profile', default=argparse.SUPPRESS, action=OrderedCheckersAction, - help="Set a checker (or checker group), " - "profile or guideline " - "to BE USED in the analysis. In case of " - "ambiguity the priority order is profile, " - "guideline, checker name (e.g. security " - "means the profile, not the checker " - "group). Moreover, labels can also be " + help="Set a checker (or checker prefix group), " + "profile or guideline to BE USED in the " + "analysis. Labels can also be " "used for selecting checkers, for example " "profile:extreme or severity:STYLE. See " "'CodeChecker checkers --label' for " - "further details.") + "further details. In case of a name clash " + "between the checker prefix " + "group/profile/guideline name, the use of " + "one of the following labels is " + "mandatory: 'checker:', 'prefix:', " + "'profile:', 'guideline:'. If a checker " + "name matches multiple checkers as a " + "prefix, 'checker:' or 'prefix:' " + "namespace is required") checkers_opts.add_argument('-d', '--disable', dest="disable", metavar='checker/group/profile', default=argparse.SUPPRESS, action=OrderedCheckersAction, - help="Set a checker (or checker group), " + help="Set a checker (or checker prefix group), " "profile or guideline " "to BE PROHIBITED from use in the " - "analysis. In case of " - "ambiguity the priority order is profile, " - "guideline, checker name (e.g. security " - "means the profile, not the checker " - "group). Moreover, labels can also be " + "analysis. Labels can also be " "used for selecting checkers, for example " "profile:extreme or severity:STYLE. See " "'CodeChecker checkers --label' for " - "further details.") + "further details. In case of a name clash " + "between the checker prefix " + "group/profile/guideline name, the use of " + "one of the following labels is " + "mandatory: 'checker:', 'prefix:', " + "'profile:', 'guideline:'. If a checker " + "name matches multiple checkers as a " + "prefix, 'checker:' or 'prefix:' " + "namespace is required") checkers_opts.add_argument('--enable-all', dest="enable_all", diff --git a/analyzer/tests/functional/analyze/test_analyze.py b/analyzer/tests/functional/analyze/test_analyze.py index be90f4cbd2..8e21b7d5c5 100644 --- a/analyzer/tests/functional/analyze/test_analyze.py +++ b/analyzer/tests/functional/analyze/test_analyze.py @@ -886,7 +886,7 @@ def test_disable_all_warnings(self): analyze_cmd = [self._codechecker_cmd, "check", "-l", build_json, "--analyzers", "clang-tidy", "-d", "clang-diagnostic", - "-e", "clang-diagnostic-unused"] + "-e", "prefix:clang-diagnostic-unused"] source_file = os.path.join(self.test_dir, "compiler_warning.c") build_log = [{"directory": self.test_workspace, diff --git a/analyzer/tests/functional/analyze_and_parse/test_files/compiler_warning_wno_group.output b/analyzer/tests/functional/analyze_and_parse/test_files/compiler_warning_wno_group.output index a559c8c8da..f08ce4d96e 100644 --- a/analyzer/tests/functional/analyze_and_parse/test_files/compiler_warning_wno_group.output +++ b/analyzer/tests/functional/analyze_and_parse/test_files/compiler_warning_wno_group.output @@ -1,7 +1,7 @@ NORMAL#CodeChecker log --output $LOGFILE$ --build "make compiler_warning_wno_group" --quiet -NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --analyzers clang-tidy -e clang-diagnostic-unused +NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --analyzers clang-tidy -e prefix:clang-diagnostic-unused NORMAL#CodeChecker parse $OUTPUT$ -CHECK#CodeChecker check --build "make compiler_warning_wno_group" --output $OUTPUT$ --quiet --analyzers clang-tidy -e clang-diagnostic-unused +CHECK#CodeChecker check --build "make compiler_warning_wno_group" --output $OUTPUT$ --quiet --analyzers clang-tidy -e prefix:clang-diagnostic-unused -------------------------------------------------------------------------------- [] - Starting build... [] - Using CodeChecker ld-logger. diff --git a/analyzer/tests/functional/analyze_and_parse/test_files/compiler_warning_wno_simple2.output b/analyzer/tests/functional/analyze_and_parse/test_files/compiler_warning_wno_simple2.output index 1704a5155a..d659feafbe 100644 --- a/analyzer/tests/functional/analyze_and_parse/test_files/compiler_warning_wno_simple2.output +++ b/analyzer/tests/functional/analyze_and_parse/test_files/compiler_warning_wno_simple2.output @@ -1,7 +1,7 @@ NORMAL#CodeChecker log --output $LOGFILE$ --build "make compiler_warning_unused" --quiet -NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --analyzers clang-tidy -d clang-diagnostic-unused +NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --analyzers clang-tidy -d prefix:clang-diagnostic-unused NORMAL#CodeChecker parse $OUTPUT$ -CHECK#CodeChecker check --build "make compiler_warning_unused" --output $OUTPUT$ --quiet --analyzers clang-tidy -d clang-diagnostic-unused +CHECK#CodeChecker check --build "make compiler_warning_unused" --output $OUTPUT$ --quiet --analyzers clang-tidy -d prefix:clang-diagnostic-unused -------------------------------------------------------------------------------- [] - Starting build... [] - Using CodeChecker ld-logger. diff --git a/analyzer/tests/functional/analyze_and_parse/test_files/compiler_warning_wunused.output b/analyzer/tests/functional/analyze_and_parse/test_files/compiler_warning_wunused.output index 1704a5155a..d659feafbe 100644 --- a/analyzer/tests/functional/analyze_and_parse/test_files/compiler_warning_wunused.output +++ b/analyzer/tests/functional/analyze_and_parse/test_files/compiler_warning_wunused.output @@ -1,7 +1,7 @@ NORMAL#CodeChecker log --output $LOGFILE$ --build "make compiler_warning_unused" --quiet -NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --analyzers clang-tidy -d clang-diagnostic-unused +NORMAL#CodeChecker analyze $LOGFILE$ --output $OUTPUT$ --analyzers clang-tidy -d prefix:clang-diagnostic-unused NORMAL#CodeChecker parse $OUTPUT$ -CHECK#CodeChecker check --build "make compiler_warning_unused" --output $OUTPUT$ --quiet --analyzers clang-tidy -d clang-diagnostic-unused +CHECK#CodeChecker check --build "make compiler_warning_unused" --output $OUTPUT$ --quiet --analyzers clang-tidy -d prefix:clang-diagnostic-unused -------------------------------------------------------------------------------- [] - Starting build... [] - Using CodeChecker ld-logger. diff --git a/analyzer/tests/unit/test_checker_handling.py b/analyzer/tests/unit/test_checker_handling.py index 81e36f09ef..7746b4916d 100644 --- a/analyzer/tests/unit/test_checker_handling.py +++ b/analyzer/tests/unit/test_checker_handling.py @@ -23,7 +23,7 @@ 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 + import is_compiler_warning, ClangTidyConfigHandler from codechecker_analyzer.arg import AnalyzerConfig, CheckerConfig from codechecker_analyzer.cmd.analyze import \ is_analyzer_config_valid, is_checker_config_valid @@ -37,14 +37,15 @@ class MockClangsaCheckerLabels: def checkers_by_labels(self, labels): if labels[0] == 'profile:default': - return ['core', 'deadcode', 'security.FloatLoopCounter'] + return ['deadcode.DeadStores', 'security.FloatLoopCounter'] if labels[0] == 'prefix:security': return ['security.insecureAPI.bzero', 'security.insecureAPI.getpw'] if labels[0] == 'profile:security': - return ['alpha.security'] + return ['alpha.security.ArrayBound', + 'alpha.security.MallocOverflow'] if labels[0] == 'profile:sensitive': return ['alpha.core.BoolAssignment', @@ -60,16 +61,33 @@ def checkers_by_labels(self, labels): def get_description(self, label): if label == 'profile': - return ['default', 'sensitive', 'security', 'portability', - 'extreme'] - return [] + return { + "default": "", + "sensitive": "", + "security": "", + "portability": "", + "extreme": "" + } + return {} def occurring_values(self, label): if label == 'guideline': return ['sei-cert'] - - if label == 'sei-cert': + elif label == 'sei-cert': return ['rule1', 'rule2'] + elif label == 'profile': + return ['default', + 'sensitive', + 'security', + 'portability', + 'extreme'] + elif label == 'severity': + return ['CRITICAL', + 'HIGH', + 'MEDIUM', + 'LOW', + 'STYLE', + 'UNSPECIFIED'] return [] @@ -141,7 +159,7 @@ def test_no_disabled_checks(self): any(arg.startswith('-analyzer-disable-checker') for arg in self.__class__.cmd)) - def test_checker_initializer(self): + def test_clangsa_checker_initializer(self): """ Test initialize_checkers() function. """ @@ -384,22 +402,35 @@ def checkers_by_labels(self, labels): return [ 'bugprone-assert-side-effect', 'bugprone-dangling-handle', - 'bugprone-inaccurate-erase'] + 'bugprone-inaccurate-erase', + 'clang-diagnostic-format', + 'clang-diagnostic-format-nonliteral', + 'clang-diagnostic-format-security'] return [] def get_description(self, label): if label == 'profile': - return ['default', 'sensitive', 'security', 'portability', - 'extreme'] - - return [] + return { + "default": "", + "sensitive": "", + "security": "", + "portability": "", + "extreme": "" + } + return {} def occurring_values(self, label): if label == 'guideline': return ['sei-cert'] elif label == 'sei-cert': return ['rule1', 'rule2'] + elif label == 'profile': + return ['default', + 'sensitive', + 'security', + 'portability', + 'extreme'] return [] @@ -479,6 +510,121 @@ def _is_disabled(self, checker, analyzer_cmd): return enable < disable + def test_clangtidy_checker_initializer(self): + """ + Test initialize_checkers() function. + """ + def all_with_status(status): + def f(checks, checkers): + result = set(check for check, data in checks.items() + if data[0] == status) + return set(checkers) <= result + return f + + checkers = ClangTidy.get_analyzer_checkers() + + format_prefix = "clang-diagnostic-format" + + format_matched_default_checkers = [ + "clang-diagnostic-format-nonliteral", + "clang-diagnostic-format-security" + ] + + format_matched_not_default_checkers = [ + "clang-diagnostic-format-non-iso", + "clang-diagnostic-format-pedantic", + ] + + cfg_handler = ClangTidyConfigHandler() + + # Check the ambigous option handling. + with self.assertLogs(level='ERROR') as log: + with self.assertRaises(SystemExit) as e: + cfg_handler.initialize_checkers(checkers, + [("clang-diagnostic-format", + True)]) + + err_ambigous_checker = re.compile(r"ERROR:.*?is ambigous\. Please " + r"select one of these options to " + r"clarify the checker list:.*$") + + match = err_ambigous_checker.search(log.output[0]) + + self.assertIsNotNone(match) + self.assertEqual(e.exception.code, 1) + + # Check if the specified checker and the default checkers are enabled + # when the clang-diagnostic-format is enabled by 'checker:' namespace. + cfg_handler.initialize_checkers(checkers, + [(f"checker:{format_prefix}", True)]) + self.assertIn(format_prefix, cfg_handler.checks()) + self.assertTrue(all_with_status(CheckerState.ENABLED) + (cfg_handler.checks(), [format_prefix])) + self.assertTrue(all_with_status(CheckerState.ENABLED) + (cfg_handler.checks(), + format_matched_default_checkers)) + self.assertTrue(all_with_status(CheckerState.DISABLED) + (cfg_handler.checks(), + format_matched_not_default_checkers)) + + # Check if the specified checker is the only one that enabled when the + # clang-diagnostic-format is enabled by 'checker:' namespace and the + # default profile is disabled. + cfg_handler.initialize_checkers(checkers, + [("default", False), + (f"checker:{format_prefix}", True)]) + self.assertIn(format_prefix, cfg_handler.checks()) + self.assertTrue(all_with_status(CheckerState.ENABLED) + (cfg_handler.checks(), [format_prefix])) + self.assertTrue(all_with_status(CheckerState.DISABLED) + (cfg_handler.checks(), + format_matched_default_checkers)) + self.assertTrue(all_with_status(CheckerState.DISABLED) + (cfg_handler.checks(), + format_matched_not_default_checkers)) + + # Check if the specified checker is disabled by 'checker:' namespace + # but the default profile is enabled. + cfg_handler.initialize_checkers(checkers, + [(f"checker:{format_prefix}", False)]) + self.assertIn(format_prefix, cfg_handler.checks()) + self.assertTrue(all_with_status(CheckerState.DISABLED) + (cfg_handler.checks(), [format_prefix])) + self.assertTrue(all_with_status(CheckerState.ENABLED) + (cfg_handler.checks(), + format_matched_default_checkers)) + self.assertTrue(all_with_status(CheckerState.DISABLED) + (cfg_handler.checks(), + format_matched_not_default_checkers)) + + # Check the prefix matched chackers when the 'prefix:' namespace + # enables them. + cfg_handler.initialize_checkers(checkers, + [(f"prefix:{format_prefix}", True)]) + self.assertIn(format_prefix, cfg_handler.checks()) + self.assertTrue(all_with_status(CheckerState.ENABLED) + (cfg_handler.checks(), [format_prefix])) + self.assertTrue(all_with_status(CheckerState.ENABLED) + (cfg_handler.checks(), + format_matched_default_checkers)) + self.assertTrue(all_with_status(CheckerState.ENABLED) + (cfg_handler.checks(), + format_matched_not_default_checkers)) + + # Check the prefix matched chackers when the 'prefix:' namespace + # disables them. + cfg_handler.initialize_checkers(checkers, + [(f"prefix:{format_prefix}", False)]) + self.assertIn(format_prefix, cfg_handler.checks()) + self.assertTrue(all_with_status(CheckerState.DISABLED) + (cfg_handler.checks(), [format_prefix])) + self.assertTrue(all_with_status(CheckerState.DISABLED) + (cfg_handler.checks(), + format_matched_default_checkers)) + self.assertTrue(all_with_status(CheckerState.DISABLED) + (cfg_handler.checks(), + format_matched_not_default_checkers)) + def test_disable_clangsa_checkers(self): """ Test that checker config still disables clang-analyzer-*. @@ -510,9 +656,8 @@ def test_disable_clangsa_checkers(self): for arg in analyzer.construct_analyzer_cmd(result_handler): self.assertFalse(arg.startswith('-checks')) - self.assertEqual( - analyzer.config_handler.checks()['Wreserved-id-macro'][0], - CheckerState.ENABLED) + self.assertNotIn("Wreserved-id-macro", + analyzer.config_handler.checks().keys()) def test_analyze_wrong_parameters(self): """ @@ -603,7 +748,7 @@ def test_clang_diags_as_compiler_warnings(self): analyzer = create_analyzer_tidy([ # This should enable -Wvla and -Wvla-extension. - '--enable', 'clang-diagnostic-vla', + '--enable', 'prefix:clang-diagnostic-vla', '--disable', 'clang-diagnostic-unused-value']) result_handler = create_result_handler(analyzer) @@ -645,16 +790,26 @@ def checkers_by_labels(self, labels): def get_description(self, label): if label == 'profile': - return ['default', 'sensitive', 'security', 'portability', - 'extreme'] - - return [] + return { + "default": "", + "sensitive": "", + "security": "", + "portability": "", + "extreme": "" + } + return {} def occurring_values(self, label): if label == 'guideline': return ['sei-cert'] elif label == 'sei-cert': return ['rule1', 'rule2'] + elif label == 'profile': + return ['default', + 'sensitive', + 'security', + 'portability', + 'extreme'] return [] diff --git a/config/labels/analyzers/clang-tidy.json b/config/labels/analyzers/clang-tidy.json index bb52fd319f..077c267a3f 100644 --- a/config/labels/analyzers/clang-tidy.json +++ b/config/labels/analyzers/clang-tidy.json @@ -2345,7 +2345,10 @@ "severity:MEDIUM" ], "clang-diagnostic-error": [ - "severity:CRITICAL" + "severity:CRITICAL", + "profile:default", + "profile:extreme", + "profile:sensitive" ], "clang-diagnostic-exceptions": [ "doc_url:https://clang.llvm.org/docs/DiagnosticsReference.html#wexceptions", diff --git a/docs/analyzer/user_guide.md b/docs/analyzer/user_guide.md index 55b07a4ed7..fa0e78b38a 100644 --- a/docs/analyzer/user_guide.md +++ b/docs/analyzer/user_guide.md @@ -449,22 +449,40 @@ checker configuration: -e checker/group/profile, --enable checker/group/profile - Set a checker (or checker group), profile or guideline - to BE USED in the analysis. In case of ambiguity the - priority order is profile, guideline, checker name - (e.g. security means the profile, not the checker - group). Moreover, labels can also be used for - selecting checkers, for example profile:extreme or - severity:STYLE. See 'CodeChecker checkers --label' for + Enable a checker or checker group to BE USED in the + analysis. Exact checker name, prefix, profile, + guideline or any label can be used for selecting + checkers. These namespace can be specified by + 'checker:' 'prefix:', 'profile:', 'guideline:', + 'severity:', etc. Normally, It is not necessary to + give a namespace but in case of ambiguity, the + CodeChecker returns with an error and suggests options + for clarification. For example the extreme profile can + be set by 'profile:extreme' or 'extreme' but security + can be also a prefix and in this case the namespace + must be given like 'profile:security' to set security + profile. If an exact checker name matches multiple + checkers as a prefix, 'checker:' or 'prefix:' + namespace is required. Any labels can be used for set + checker group. See 'CodeChecker checkers --label' for further details. -d checker/group/profile, --disable checker/group/profile - Set a checker (or checker group), profile or guideline - to BE PROHIBITED from use in the analysis. In case of - ambiguity the priority order is profile, guideline, - checker name (e.g. security means the profile, not the - checker group). Moreover, labels can also be used for - selecting checkers, for example profile:extreme or - severity:STYLE. See 'CodeChecker checkers --label' for + Disable a checker or checker group to BE PROHIBITED in + the analysis. Exact checker name, prefix, profile, + guideline or any label can be used for selecting + checkers. These namespace can be specified by + 'checker:' 'prefix:', 'profile:', 'guideline:', + 'severity:', etc. Normally, It is not necessary to + give a namespace but in case of ambiguity, the + CodeChecker returns with an error and suggests options + for clarification. For example the extreme profile can + be set by 'profile:extreme' or 'extreme' but security + can be also a prefix and in this case the namespace + must be given like 'profile:security' to set security + profile. If an exact checker name matches multiple + checkers as a prefix, 'checker:' or 'prefix:' + namespace is required. Any labels can be used for set + checker group. See 'CodeChecker checkers --label' for further details. --enable-all Force the running analyzers to use almost every checker available. The checker groups 'alpha.', @@ -1438,23 +1456,41 @@ available checkers in the binaries installed on your system. ``` checker configuration: - -e checker/group/profile, --enable checker/group/profile - Set a checker (or checker group or checker profile) - to BE USED in the analysis. In case of ambiguity the - priority order is profile, guideline, checker name - (e.g. security means the profile, not the checker - group). Moreover, labels can also be used for - selecting checkers, for example profile:extreme or - severity:STYLE. See 'CodeChecker checkers --label' for + -e checker/group/profile, --enable checker/group/profile + Enable a checker or checker group to BE USED in the + analysis. Exact checker name, prefix, profile, + guideline or any label can be used for selecting + checkers. These namespace can be specified by + 'checker:' 'prefix:', 'profile:', 'guideline:', + 'severity:', etc. Normally, It is not necessary to + give a namespace but in case of ambiguity, the + CodeChecker returns with an error and suggests options + for clarification. For example the extreme profile can + be set by 'profile:extreme' or 'extreme' but security + can be also a prefix and in this case the namespace + must be given like 'profile:security' to set security + profile. If an exact checker name matches multiple + checkers as a prefix, 'checker:' or 'prefix:' + namespace is required. Any labels can be used for set + checker group. See 'CodeChecker checkers --label' for further details. -d checker/group/profile, --disable checker/group/profile - Set a checker (or checker group or checker profile) - to BE PROHIBITED from use in the analysis. In case of - ambiguity the priority order is profile, guideline, - checker name (e.g. security means the profile, not the - checker group). Moreover, labels can also be used for - selecting checkers, for example profile:extreme or - severity:STYLE. See 'CodeChecker checkers --label' for + Disable a checker or checker group to BE PROHIBITED in + the analysis. Exact checker name, prefix, profile, + guideline or any label can be used for selecting + checkers. These namespace can be specified by + 'checker:' 'prefix:', 'profile:', 'guideline:', + 'severity:', etc. Normally, It is not necessary to + give a namespace but in case of ambiguity, the + CodeChecker returns with an error and suggests options + for clarification. For example the extreme profile can + be set by 'profile:extreme' or 'extreme' but security + can be also a prefix and in this case the namespace + must be given like 'profile:security' to set security + profile. If an exact checker name matches multiple + checkers as a prefix, 'checker:' or 'prefix:' + namespace is required. Any labels can be used for set + checker group. See 'CodeChecker checkers --label' for further details. --enable-all Force the running analyzers to use almost every checker available. The checker groups 'alpha.', @@ -1492,10 +1528,11 @@ Checkers are taken into account based on the following order: "debug" checker groups. `osx` checker group is also not included unless the target platform is Darwin. - Command line `--enable/--disable` flags. - - Their arguments may start with `profile:` of `guideline:` prefix which - makes the choice explicit. - - Without prefix it means a profile name, a guideline name or a checker - group/name in this priority order. + - Their arguments may start with `checker:`, `prefix:`, `profile:`, + `guideline:` or any existing label type as a namespace which makes the + choice explicit. + - Without namespace it can be a checker name, a checker prefix, a profile + name or a guideline name. in case of ambiguity, namespace is expected. Disabling certain checkers - such as the `core` group - is unsupported by the LLVM/Clang community, and thus discouraged. @@ -1511,24 +1548,24 @@ and disabled flags starting from the bigger groups and going inwards. For example ```sh ---enable Wunused --disable Wno-unused-parameter +--enable prefix:clang-diagnostic-unused +--disable checker:clang-diagnostic-unused-parameter ``` or ```sh ---enable Wunused --disable Wunused-parameter -``` -will enable every `unused` warnings except `unused-parameter`. These flags -should start with a capital `W` or `Wno-` prefix followed by the warning name -(E.g.: `-e Wliteral-conversion`, `-d Wno-literal-conversion` or -`-d Wliteral-conversion`). To turn off a compiler warning you can use the -negative form beginning with `Wno-` (e.g.: `--disable Wno-literal-conversion`) -or you can use the positive form beginning with `W` (e.g.: -`--enable Wliteral-conversion`). For more information see: +--enable prefix:clang-diagnostic-unused +--disable clang-diagnostic-unused-parameter +``` +will enable every `unused` warnings except `unused-parameter`. To turn off a +compiler warning you should use `clang-diagnostic-` instead of `W` or `Wno` +followed by the warning name. These flags may start with `checker:`, +`prefix:`, `profile:`, `guideline:` or any existing label type as a namespace +which makes the choice explicit. Namespace is only required when the given +flag is ambiguity. (E.g.: `clang-diagnostic-unused` is both a checker name +that represents unused warning and a prefix that is the group of the unused +warrnings). For more information see: https://clang.llvm.org/docs/DiagnosticsReference.html. -A warning can be referred in both formats: `-d Wunused-parameter` and -`-d clang-diagnostic-unused-parameter` are the same. - `clang-diagnostic-error` is a special one, since it doesn't refer a warning but a compilation error. This is enabled by default and will be stored as a critical severity bug. diff --git a/web/server/vue-cli/src/views/NewFeatures.vue b/web/server/vue-cli/src/views/NewFeatures.vue index 8a98c713b5..b4832e5ccb 100644 --- a/web/server/vue-cli/src/views/NewFeatures.vue +++ b/web/server/vue-cli/src/views/NewFeatures.vue @@ -1314,10 +1314,12 @@ analyzer: There is a new syntax extended with guideline support which can be used to enable checker sets. With the new syntax the checkers, profiles and guideline can be enabled or disabled even if there is - a conflict in their name. The arguments may start with - profile: of guideline: prefix which makes the choice - explicit. Without prefix it means a profile name, a guideline - name or a checker group/name in this priority order.
+ a conflict in their name. Their arguments may start with + checker:, prefix:, profile:, + guideline: or any existing label type as a namespace which + makes the choice explicit. Without namespace it can be a checker + name, a checker prefix, a profile name or a guideline name. in + case of ambiguity, namespace is expected.
CodeChecker analyze -o reports -e profile:sensitive -e guideline:sei-cert compile_command.json

@@ -1328,7 +1330,7 @@ analyzer: -