From e36874cb5478dbb96ef149d700cf7bcfb50ad6c3 Mon Sep 17 00:00:00 2001 From: Michael Hohn Date: Mon, 15 Nov 2021 14:16:23 -0800 Subject: [PATCH] sarif-results-summary: underline affected code region Using sarif-results-summary -s data/linux-small data/torvalds_linux__2021-10-21_10_07_00__export.sarif |less now underscores the indicated regions, e.g. tools/cgroup/iocost_monitor.py:64:5:64:27: Normal methods should have 'self', rather than 'blkcg', as their first parameter. def blkcg_name(blkcg): ^^^^^^^^^^^^^^^^^^^^^^ --- bin/sarif-results-summary | 14 ++++++++++++-- sarif_cli/__init__.py | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/bin/sarif-results-summary b/bin/sarif-results-summary index 6bb4c2c..c4e6677 100755 --- a/bin/sarif-results-summary +++ b/bin/sarif-results-summary @@ -2,6 +2,7 @@ import argparse import json import sarif_cli as S +import re import sys import collections @@ -20,7 +21,7 @@ for runi in S.indices(sarif_struct, 'runs'): 'semmle.sourceLanguage') S.msg("Found %d results for %s\n" % (num_results, language)) if num_results == 0: continue - + # for resi in S.indices(sarif_struct, 'runs', runi, 'results'): message = S.get(sarif_struct, 'runs', runi, 'results', resi, 'message', 'text') artifact = S.get(sarif_struct, 'runs', runi, 'results', resi, 'locations', 0, @@ -32,7 +33,16 @@ for runi in S.indices(sarif_struct, 'runs'): S.msg("%s: %s\n\n" % (filepath, message)) if args.list_source: lines = S.load_lines(args.list_source, artifact['uri'], l1, l2) - for line in lines: + for line, line_num in zip(lines, range(l1, l2+1)): + # Display the line S.msg("%s" % (line)) S.msg("\n") + # Print the underline + underline = S.underline_for_result(l1, c1, l2, c2, line, line_num) + S.msg(underline) + # Next result + S.msg("\n") S.msg("\n") + + + diff --git a/sarif_cli/__init__.py b/sarif_cli/__init__.py index 511407d..9ba930a 100644 --- a/sarif_cli/__init__.py +++ b/sarif_cli/__init__.py @@ -1,10 +1,49 @@ import sys import os +import re MIN_PYTHON = (3, 7) if sys.version_info < MIN_PYTHON: sys.exit("Python %s.%s or later is required.\n" % MIN_PYTHON) +def underline_for_result(first_line, first_column, last_line, last_column, line, line_num): + """Provide the underline for a result line. + + first_line, first_column, last_line, last_column : + the region from S.lineinfo(region) + line: + the line of source + line_num: + the index of line, must satisfy first_line <= line_num <= last_line + """ + # Underline the affected region + # col_* use the [start, end) indexing + # From the first non-whitespace char + match = re.search("([^\s])+", line) + if match: + col_from = match.span()[0] + else: + col_from = 0 + # To the last non-whitespace char + match = re.search("(\s)+$", line) + if match: + col_to = match.span()[0] + else: + col_to = len(line) + # Use 1-indexing + col_from += 1 ; col_to += 1 + # Adjust first line + if line_num == first_line: + col_from = max(col_from, first_column) + # Adjust last line + if line_num == last_line: + col_to = min(col_to, last_column) + # Use 0-indexing + col_from -= 1 ; col_to -= 1 + # Return the underline + return " " * col_from + "^" * (col_to - col_from) + + def load_lines(root, path, line_from, line_to): """Load the line range [line_from, line_to], including both, from the file at root/path.