Index: clang/tools/scan-build-py/libscanbuild/analyze.py =================================================================== --- clang/tools/scan-build-py/libscanbuild/analyze.py +++ clang/tools/scan-build-py/libscanbuild/analyze.py @@ -356,11 +356,14 @@ yield name finally: if os.listdir(name): - if output_format != 'sarif': + if output_format not in ['sarif', 'sarif-html']: # FIXME: # 'scan-view' currently does not support sarif format. msg = "Run 'scan-view %s' to examine bug reports." + elif output_format == 'sarif-html': + msg = "Run 'scan-view %s' to examine bug reports or see " \ + "merged sarif results at %s/results-merged.sarif." else: - msg = "View result at %s/results-merged.sarif." + msg = "View merged sarif results at %s/results-merged.sarif." keep = True else: if keep: @@ -438,7 +441,7 @@ 'direct_args', # arguments from command line 'force_debug', # kill non debug macros 'output_dir', # where generated report files shall go - 'output_format', # it's 'plist', 'html', 'plist-html', 'plist-multi-file', or 'sarif' + 'output_format', # it's 'plist', 'html', 'plist-html', 'plist-multi-file', 'sarif', or 'sarif-html' 'output_failures', # generate crash reports or not 'ctu']) # ctu control options def run(opts): @@ -542,7 +545,9 @@ dir=opts['output_dir']) os.close(handle) return name - elif opts['output_format'] == 'sarif': + elif opts['output_format'] in { + 'sarif', + 'sarif-html'}: (handle, name) = tempfile.mkstemp(prefix='result-', suffix='.sarif', dir=opts['output_dir']) Index: clang/tools/scan-build-py/libscanbuild/arguments.py =================================================================== --- clang/tools/scan-build-py/libscanbuild/arguments.py +++ clang/tools/scan-build-py/libscanbuild/arguments.py @@ -252,6 +252,14 @@ default='html', action='store_const', help="""Cause the results as a result.sarif file.""") + format_group.add_argument( + '--sarif-html', + '-sarif-html', + dest='output_format', + const='sarif-html', + default='html', + action='store_const', + help="""Cause the results as a result.sarif file and .html files.""") advanced = parser.add_argument_group('advanced options') advanced.add_argument( Index: clang/tools/scan-build-py/libscanbuild/report.py =================================================================== --- clang/tools/scan-build-py/libscanbuild/report.py +++ clang/tools/scan-build-py/libscanbuild/report.py @@ -26,8 +26,8 @@ def document(args): """ Generates cover report and returns the number of bugs/crashes. """ - html_reports_available = args.output_format in {'html', 'plist-html'} - sarif_reports_available = args.output_format in {'sarif'} + html_reports_available = args.output_format in {'html', 'plist-html', 'sarif-html'} + sarif_reports_available = args.output_format in {'sarif', 'sarif-html'} logging.debug('count crashes and bugs') crash_count = sum(1 for _ in read_crashes(args.output)) Index: clang/tools/scan-build-py/tests/functional/cases/test_from_cdb.py =================================================================== --- clang/tools/scan-build-py/tests/functional/cases/test_from_cdb.py +++ clang/tools/scan-build-py/tests/functional/cases/test_from_cdb.py @@ -102,7 +102,11 @@ def get_plist_count(directory): return len(glob.glob(os.path.join(directory, 'report-*.plist'))) - def test_default_creates_html_report(self): + @staticmethod + def get_sarif_count(directory): + return len(glob.glob(os.path.join(directory, 'result-*.sarif'))) + + def test_default_only_creates_html_report(self): with libear.TemporaryDirectory() as tmpdir: cdb = prepare_cdb('regular', tmpdir) exit_code, reportdir = run_analyzer(tmpdir, cdb, []) @@ -110,8 +114,9 @@ os.path.exists(os.path.join(reportdir, 'index.html'))) self.assertEqual(self.get_html_count(reportdir), 2) self.assertEqual(self.get_plist_count(reportdir), 0) + self.assertEqual(self.get_sarif_count(reportdir), 0) - def test_plist_and_html_creates_html_report(self): + def test_plist_and_html_creates_html_and_plist_reports(self): with libear.TemporaryDirectory() as tmpdir: cdb = prepare_cdb('regular', tmpdir) exit_code, reportdir = run_analyzer(tmpdir, cdb, ['--plist-html']) @@ -119,8 +124,9 @@ os.path.exists(os.path.join(reportdir, 'index.html'))) self.assertEqual(self.get_html_count(reportdir), 2) self.assertEqual(self.get_plist_count(reportdir), 5) + self.assertEqual(self.get_sarif_count(reportdir), 0) - def test_plist_does_not_creates_html_report(self): + def test_plist_only_creates_plist_report(self): with libear.TemporaryDirectory() as tmpdir: cdb = prepare_cdb('regular', tmpdir) exit_code, reportdir = run_analyzer(tmpdir, cdb, ['--plist']) @@ -128,6 +134,31 @@ os.path.exists(os.path.join(reportdir, 'index.html'))) self.assertEqual(self.get_html_count(reportdir), 0) self.assertEqual(self.get_plist_count(reportdir), 5) + self.assertEqual(self.get_sarif_count(reportdir), 0) + + def test_sarif_only_creates_sarif_result(self): + with libear.TemporaryDirectory() as tmpdir: + cdb = prepare_cdb('regular', tmpdir) + exit_code, reportdir = run_analyzer(tmpdir, cdb, ['--sarif']) + self.assertFalse( + os.path.exists(os.path.join(reportdir, 'index.html'))) + self.assertTrue( + os.path.exists(os.path.join(reportdir, 'results-merged.sarif'))) + self.assertEqual(self.get_html_count(reportdir), 0) + self.assertEqual(self.get_plist_count(reportdir), 0) + self.assertEqual(self.get_sarif_count(reportdir), 5) + + def test_sarif_and_html_creates_sarif_and_html_reports(self): + with libear.TemporaryDirectory() as tmpdir: + cdb = prepare_cdb('regular', tmpdir) + exit_code, reportdir = run_analyzer(tmpdir, cdb, ['--sarif-html']) + self.assertTrue( + os.path.exists(os.path.join(reportdir, 'index.html'))) + self.assertTrue( + os.path.exists(os.path.join(reportdir, 'results-merged.sarif'))) + self.assertEqual(self.get_html_count(reportdir), 2) + self.assertEqual(self.get_plist_count(reportdir), 0) + self.assertEqual(self.get_sarif_count(reportdir), 5) class FailureReportTest(unittest.TestCase):