78 lines
2.4 KiB
Python
Executable file
78 lines
2.4 KiB
Python
Executable file
#!/usr/bin/python3
|
|
|
|
# Copyright (C) 2020 Kevin Croft <krcroft@gmail.com>
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
"""
|
|
Count the number of issues found in an PVS-Studio report.
|
|
|
|
Usage: count-pvs-issues.py REPORT [MAX-ISSUES]
|
|
Where:
|
|
- REPORT is a file in CSV-format
|
|
- MAX-ISSUES is as a positive integer indicating the maximum
|
|
issues that should be permitted before returning failure
|
|
to the shell. Default is non-limit.
|
|
|
|
"""
|
|
|
|
# pylint: disable=invalid-name
|
|
# pylint: disable=missing-docstring
|
|
|
|
import collections
|
|
import csv
|
|
import os
|
|
import sys
|
|
|
|
def parse_issues(filename):
|
|
"""
|
|
Returns a dict of int keys and a list of string values, where the:
|
|
- keys are V### PVS-Studio error codes
|
|
- values are the message of the issue as found in a specific file
|
|
|
|
"""
|
|
issues = collections.defaultdict(list)
|
|
with open(filename) as csvfile:
|
|
reader = csv.DictReader(csvfile)
|
|
for row in reader:
|
|
full = row['ErrorCode'] # extract the full code as an URL string
|
|
code = full[full.rfind('V'):full.rfind('"')] # get the trailing "V###" code
|
|
if code.startswith('V'):
|
|
# Convert the V### string into an integer for easy sorting
|
|
issues[int(code[1:])].append(row['Message'])
|
|
return issues
|
|
|
|
|
|
def main(argv):
|
|
# assume success until proven otherwise
|
|
rcode = 0
|
|
|
|
# Get the issues and the total tally
|
|
issues = parse_issues(argv[1])
|
|
tally = sum(len(messages) for messages in issues.values())
|
|
|
|
if tally > 0:
|
|
# Step through the codes and summarize
|
|
print("Issues are tallied and sorted by code:\n")
|
|
print(" code | issue-string in common to all instances | tally")
|
|
print(" ----- --------------------------------------------- -----")
|
|
|
|
for code in sorted(issues.keys()):
|
|
messages = issues[code]
|
|
in_common = os.path.commonprefix(messages)[:45]
|
|
if len(in_common.split(' ')) < 4:
|
|
in_common = 'N/A (too little in-common between issues)'
|
|
print(f' [{code:4}] {in_common:45} : {len(messages)}')
|
|
|
|
# Print the tally against the desired maximum
|
|
if len(sys.argv) == 3:
|
|
max_issues = int(sys.argv[2])
|
|
print(f'\nTotal: {tally} issues (out of {max_issues} allowed)')
|
|
if tally > max_issues:
|
|
rcode = 1
|
|
else:
|
|
print(f'\nTotal: {tally} issues')
|
|
|
|
return rcode
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main(sys.argv))
|