File: check-analyzer-fixit.py

package info (click to toggle)
llvm-toolchain-17 1%3A17.0.6-22
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,799,624 kB
  • sloc: cpp: 6,428,607; ansic: 1,383,196; asm: 793,408; python: 223,504; objc: 75,364; f90: 60,502; lisp: 33,869; pascal: 15,282; sh: 9,684; perl: 7,453; ml: 4,937; awk: 3,523; makefile: 2,889; javascript: 2,149; xml: 888; fortran: 619; cs: 573
file content (144 lines) | stat: -rw-r--r-- 4,363 bytes parent folder | download | duplicates (10)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#!/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
        ).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()