Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[analyzer] FixItHint: Apply and test hints with the Clang-Tidy's script
Summary: This patch introduces a way to apply the fix-its by the Analyzer: `-analyzer-config apply-fixits=true`. The fix-its should be testable, therefore I have copied the well-tested `check_clang_tidy.py` script. The idea is that the Analyzer's workflow is different so it would be very difficult to use only one script for both Tidy and the Analyzer, the script would diverge a lot. Example test: `// RUN: %check-analyzer-fixit %s %t -analyzer-checker=core` When the copy-paste happened the original authors were: @alexfh, @zinovy.nis, @JonasToth, @hokein, @gribozavr, @lebedev.ri Reviewed By: NoQ, alexfh, zinovy.nis Differential Revision: https://reviews.llvm.org/D69746
- Loading branch information
Showing
7 changed files
with
211 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
#!/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 <source-file> <temp-file> [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) | ||
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// RUN: %check_analyzer_fixit %s %t \ | ||
// RUN: -analyzer-checker=core,optin.cplusplus.VirtualCall \ | ||
// RUN: -analyzer-config optin.cplusplus.VirtualCall:ShowFixIts=true | ||
|
||
struct S { | ||
virtual void foo(); | ||
S() { | ||
foo(); | ||
// expected-warning@-1 {{Call to virtual method 'S::foo' during construction bypasses virtual dispatch}} | ||
// CHECK-FIXES: S::foo(); | ||
} | ||
~S(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters