-
Notifications
You must be signed in to change notification settings - Fork 475
Add report-converter --example flag and EXAMPLE_CMD interface (#4992) #4996
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,6 +22,7 @@ a CodeChecker server. | |
| * [TSLint](#tslint) | ||
| * [Golint](#golint) | ||
| * [Pyflakes](#pyflakes) | ||
| * [Ruff](#ruff) | ||
| * [PVS-Studio](#PVS-Studio) | ||
| * [Markdownlint](#markdownlint) | ||
| * [Coccinelle](#coccinelle) | ||
|
|
@@ -132,6 +133,13 @@ Supported analyzers: | |
| </details> | ||
|
|
||
| ## Supported analyzer outputs | ||
| The list below can go out of sync with the actual code. For an always | ||
| up-to-date usage example for a given analyzer straight from the source, run: | ||
| ```sh | ||
| report-converter --example <TYPE> | ||
| # e.g. report-converter --example ruff | ||
| ``` | ||
|
|
||
| ### Sanitizers | ||
| #### Undefined Behaviour Sanitizer | ||
| [UndefinedBehaviorSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html) | ||
|
|
@@ -409,6 +417,27 @@ report-converter -t pyflakes -o ./codechecker_pyflakes_reports ./pyflakes_report | |
| CodeChecker store ./codechecker_pyflakes_reports -n pyflakes | ||
| ``` | ||
|
|
||
| ### Ruff | ||
| [Ruff](https://docs.astral.sh/ruff/) is an extremely fast static analysis | ||
| tool (linter) for `Python` code. | ||
|
|
||
| The recommended way of running Ruff is to generate a `json` output file. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. report-converter accepts only |
||
|
|
||
| The following example shows you how to run Ruff and store the results | ||
| found by Ruff to the CodeChecker database. | ||
|
|
||
| ```sh | ||
| # Run ruff and generate a json output file. | ||
| ruff check --output-format json /path/to/my/project > ./ruff_reports.json | ||
|
|
||
| # Use 'report-converter' to create a CodeChecker report directory from the | ||
| # analyzer result of ruff. | ||
| report-converter -t ruff -o ./codechecker_ruff_reports ./ruff_reports.json | ||
|
|
||
| # Store the ruff reports with CodeChecker. | ||
| CodeChecker store ./codechecker_ruff_reports -n ruff | ||
| ``` | ||
|
|
||
| ### PVS-Studio | ||
| [PVS-Studio](https://pvs-studio.com/en) is a static analyzer on guard of code | ||
| quality, security (SAST), and code safety for C, C++, C# and Java. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| # ------------------------------------------------------------------------- | ||
| # | ||
| # Part of the CodeChecker project, under the Apache License v2.0 with | ||
| # LLVM Exceptions. See LICENSE for license information. | ||
| # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| # | ||
| # ------------------------------------------------------------------------- | ||
|
|
||
| """ | ||
| Tests for the report-converter CLI, specifically the '--example' flag and | ||
| the EXAMPLE_CMD interface it relies on (see | ||
| https://github.com/Ericsson/codechecker/issues/4992). | ||
| """ | ||
|
|
||
| import io | ||
| import unittest | ||
| from contextlib import redirect_stdout | ||
| from unittest.mock import patch | ||
|
|
||
| from codechecker_report_converter import cli | ||
|
|
||
|
|
||
| # TOOL_NAMEs of analyzers that do not yet provide an EXAMPLE_CMD. | ||
| # | ||
| # This list must not grow: any newly added parser is required to define a | ||
| # non-empty EXAMPLE_CMD (see AnalyzerResultBase.EXAMPLE_CMD). Existing | ||
| # parsers on this list predate that requirement and should have one added | ||
| # as a follow-up; when a parser is updated, remove it from this list. | ||
| LEGACY_ANALYZERS_WITHOUT_EXAMPLE_CMD = { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should include the examples for all analyzer tools in this PR. |
||
| 'clang-tidy', 'clang-tidy-yaml', 'coccinelle', 'cppcheck', 'cpplint', | ||
| 'eslint', 'gcc', 'golint', 'fbinfer', 'jscpd', 'kernel-doc', 'mdl', | ||
| 'pmd', 'pvs-studio', 'pyflakes', 'pylint', 'roslynator', 'smatch', | ||
| 'sparse', 'sphinx', 'spotbugs', 'tslint', | ||
| 'asan', 'lsan', 'msan', 'tsan', 'ubsan', | ||
| } | ||
|
|
||
|
|
||
| class ExampleCmdInterfaceTest(unittest.TestCase): | ||
| """ | ||
| Ensures new analyzer parsers can't be added without a usage example, | ||
| while allowing existing ones to be migrated gradually. | ||
| """ | ||
|
|
||
| def test_new_analyzers_must_define_example_cmd(self): | ||
| for tool_name, analyzer_result in cli.supported_converters.items(): | ||
| if tool_name in LEGACY_ANALYZERS_WITHOUT_EXAMPLE_CMD: | ||
| continue | ||
|
|
||
| self.assertTrue( | ||
| analyzer_result.EXAMPLE_CMD, | ||
| f"'{tool_name}' must define a non-empty EXAMPLE_CMD class " | ||
| "attribute (see AnalyzerResultBase.EXAMPLE_CMD) so users " | ||
| "can discover how to produce compatible input via " | ||
| "'report-converter --example {tool_name}'.") | ||
|
|
||
| def test_legacy_exemption_list_has_no_stale_entries(self): | ||
| """ | ||
| Keeps the exemption list itself honest: every entry must still | ||
| correspond to a supported analyzer that genuinely lacks an | ||
| EXAMPLE_CMD. Once a legacy parser is updated, it must be removed | ||
| from this list rather than left behind. | ||
| """ | ||
| for tool_name in LEGACY_ANALYZERS_WITHOUT_EXAMPLE_CMD: | ||
| self.assertIn(tool_name, cli.supported_converters) | ||
| self.assertFalse( | ||
| cli.supported_converters[tool_name].EXAMPLE_CMD, | ||
| f"'{tool_name}' now defines an EXAMPLE_CMD - please remove " | ||
| "it from LEGACY_ANALYZERS_WITHOUT_EXAMPLE_CMD.") | ||
|
|
||
|
|
||
| class ExampleFlagTest(unittest.TestCase): | ||
| """ Tests for the 'report-converter --example TYPE' flag. """ | ||
|
|
||
| def test_example_flag_prints_example_and_exits_zero(self): | ||
| """ | ||
| For an analyzer with an EXAMPLE_CMD, '--example' should print it | ||
| and exit successfully, without requiring the tool's other | ||
| required arguments (input, --output, --type). | ||
| """ | ||
| out = io.StringIO() | ||
| with patch('sys.argv', ['report-converter', '--example', 'ruff']), \ | ||
| redirect_stdout(out): | ||
| with self.assertRaises(SystemExit) as ctx: | ||
| cli.main() | ||
|
|
||
| self.assertEqual(ctx.exception.code, 0) | ||
| self.assertIn('ruff check', out.getvalue()) | ||
| self.assertIn('report-converter -t ruff', out.getvalue()) | ||
|
|
||
| def test_example_flag_on_legacy_analyzer_does_not_crash(self): | ||
| """ | ||
| For an analyzer that doesn't have an EXAMPLE_CMD yet, '--example' | ||
| should exit cleanly (with a warning) instead of crashing. | ||
| """ | ||
| with patch('sys.argv', ['report-converter', '--example', 'pylint']): | ||
| with self.assertRaises(SystemExit) as ctx: | ||
| cli.main() | ||
|
|
||
| self.assertEqual(ctx.exception.code, 0) | ||
|
|
||
| def test_example_flag_rejects_unknown_analyzer(self): | ||
| """ | ||
| An unsupported TYPE should be rejected the same way argparse | ||
| rejects any other invalid 'choices' value. | ||
| """ | ||
| argv = ['report-converter', '--example', 'not_a_real_analyzer'] | ||
| with patch('sys.argv', argv): | ||
| with self.assertRaises(SystemExit) as ctx: | ||
| cli.main() | ||
|
|
||
| self.assertEqual(ctx.exception.code, 2) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unittest.main() | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think, the first sentence is unnecessary here.