#!/usr/bin/env python # #===- check-analyzer-fixit.py - Static Analyzer test helper ---*- python -*-===# # # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. # See https://llvm.org/LICENSE.txt for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # #===------------------------------------------------------------------------===# # # This file copy-pasted mostly from the Clang-Tidy's 'check_clang_tidy.py'. # #===------------------------------------------------------------------------===# r""" Clang Static Analyzer test helper ================================= This script runs the Analyzer in fix-it mode and verify fixes, warnings, notes. Usage: check-analyzer-fixit.py [analyzer arguments] Example: // RUN: %check-analyzer-fixit %s %t -analyzer-checker=core """ import argparse import os import re import subprocess import sys def write_file(file_name, text): with open(file_name, 'w') as f: f.write(text) def run_test_once(args, extra_args): input_file_name = args.input_file_name temp_file_name = args.temp_file_name clang_analyzer_extra_args = extra_args file_name_with_extension = input_file_name _, extension = os.path.splitext(file_name_with_extension) if extension not in ['.c', '.hpp', '.m', '.mm']: extension = '.cpp' temp_file_name = temp_file_name + extension with open(input_file_name, 'r') as input_file: input_text = input_file.read() # Remove the contents of the CHECK lines to avoid CHECKs matching on # themselves. We need to keep the comments to preserve line numbers while # avoiding empty lines which could potentially trigger formatting-related # checks. cleaned_test = re.sub('// *CHECK-[A-Z0-9\-]*:[^\r\n]*', '//', input_text) write_file(temp_file_name, cleaned_test) original_file_name = temp_file_name + ".orig" write_file(original_file_name, cleaned_test) try: builtin_include_dir = subprocess.check_output( ['clang', '-print-file-name=include'], stderr=subprocess.STDOUT).decode() except subprocess.CalledProcessError as e: print('Cannot print Clang include directory: ' + e.output.decode()) builtin_include_dir = os.path.normpath(builtin_include_dir) args = (['clang', '-cc1', '-internal-isystem', builtin_include_dir, '-nostdsysteminc', '-analyze', '-analyzer-constraints=range', '-analyzer-config', 'apply-fixits=true'] + clang_analyzer_extra_args + ['-verify', temp_file_name]) print('Running ' + str(args) + '...') try: clang_analyzer_output = \ subprocess.check_output(args, stderr=subprocess.STDOUT).decode() except subprocess.CalledProcessError as e: print('Clang Static Analyzer test failed:\n' + e.output.decode()) raise print('----------------- Clang Static Analyzer output -----------------\n' + clang_analyzer_output + '\n--------------------------------------------------------------') try: diff_output = subprocess.check_output( ['diff', '-u', original_file_name, temp_file_name], stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: diff_output = e.output print('----------------------------- Fixes ----------------------------\n' + diff_output.decode() + '\n--------------------------------------------------------------') try: subprocess.check_output( ['FileCheck', '-input-file=' + temp_file_name, input_file_name, '-check-prefixes=CHECK-FIXES', '-strict-whitespace'], stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: print('FileCheck failed:\n' + e.output.decode()) raise def main(): parser = argparse.ArgumentParser() parser.add_argument('input_file_name') parser.add_argument('temp_file_name') args, extra_args = parser.parse_known_args() run_test_once(args, extra_args) if __name__ == '__main__': main()