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))
 |