1#!/usr/bin/env python 2# 3# ===- check-analyzer-fixit.py - Static Analyzer test helper ---*- python -*-===# 4# 5# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 6# See https://llvm.org/LICENSE.txt for license information. 7# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 8# 9# ===------------------------------------------------------------------------===# 10# 11# This file copy-pasted mostly from the Clang-Tidy's 'check_clang_tidy.py'. 12# 13# ===------------------------------------------------------------------------===# 14 15r""" 16Clang Static Analyzer test helper 17================================= 18 19This script runs the Analyzer in fix-it mode and verify fixes, warnings, notes. 20 21Usage: 22 check-analyzer-fixit.py <source-file> <temp-file> [analyzer arguments] 23 24Example: 25 // RUN: %check-analyzer-fixit %s %t -analyzer-checker=core 26""" 27 28import argparse 29import os 30import re 31import subprocess 32import sys 33 34 35def write_file(file_name, text): 36 with open(file_name, "w") as f: 37 f.write(text) 38 39 40def run_test_once(args, extra_args): 41 input_file_name = args.input_file_name 42 temp_file_name = args.temp_file_name 43 clang_analyzer_extra_args = extra_args 44 45 file_name_with_extension = input_file_name 46 _, extension = os.path.splitext(file_name_with_extension) 47 if extension not in [".c", ".hpp", ".m", ".mm"]: 48 extension = ".cpp" 49 temp_file_name = temp_file_name + extension 50 51 with open(input_file_name, "r") as input_file: 52 input_text = input_file.read() 53 54 # Remove the contents of the CHECK lines to avoid CHECKs matching on 55 # themselves. We need to keep the comments to preserve line numbers while 56 # avoiding empty lines which could potentially trigger formatting-related 57 # checks. 58 cleaned_test = re.sub("// *CHECK-[A-Z0-9\-]*:[^\r\n]*", "//", input_text) 59 write_file(temp_file_name, cleaned_test) 60 61 original_file_name = temp_file_name + ".orig" 62 write_file(original_file_name, cleaned_test) 63 64 try: 65 builtin_include_dir = subprocess.check_output( 66 ["clang", "-print-file-name=include"], stderr=subprocess.STDOUT 67 ).decode() 68 except subprocess.CalledProcessError as e: 69 print("Cannot print Clang include directory: " + e.output.decode()) 70 71 builtin_include_dir = os.path.normpath(builtin_include_dir) 72 73 args = ( 74 [ 75 "clang", 76 "-cc1", 77 "-internal-isystem", 78 builtin_include_dir, 79 "-nostdsysteminc", 80 "-analyze", 81 "-analyzer-constraints=range", 82 "-analyzer-config", 83 "apply-fixits=true", 84 ] 85 + clang_analyzer_extra_args 86 + ["-verify", temp_file_name] 87 ) 88 89 print("Running " + str(args) + "...") 90 91 try: 92 clang_analyzer_output = subprocess.check_output( 93 args, stderr=subprocess.STDOUT 94 ).decode() 95 except subprocess.CalledProcessError as e: 96 print("Clang Static Analyzer test failed:\n" + e.output.decode()) 97 raise 98 99 print( 100 "----------------- Clang Static Analyzer output -----------------\n" 101 + clang_analyzer_output 102 + "\n--------------------------------------------------------------" 103 ) 104 105 try: 106 diff_output = subprocess.check_output( 107 ["diff", "-u", original_file_name, temp_file_name], stderr=subprocess.STDOUT 108 ) 109 except subprocess.CalledProcessError as e: 110 diff_output = e.output 111 112 print( 113 "----------------------------- Fixes ----------------------------\n" 114 + diff_output.decode() 115 + "\n--------------------------------------------------------------" 116 ) 117 118 try: 119 subprocess.check_output( 120 [ 121 "FileCheck", 122 "-input-file=" + temp_file_name, 123 input_file_name, 124 "-check-prefixes=CHECK-FIXES", 125 "-strict-whitespace", 126 ], 127 stderr=subprocess.STDOUT, 128 ) 129 except subprocess.CalledProcessError as e: 130 print("FileCheck failed:\n" + e.output.decode()) 131 raise 132 133 134def main(): 135 parser = argparse.ArgumentParser() 136 parser.add_argument("input_file_name") 137 parser.add_argument("temp_file_name") 138 139 args, extra_args = parser.parse_known_args() 140 run_test_once(args, extra_args) 141 142 143if __name__ == "__main__": 144 main() 145