1
+ #!/usr/bin/env python3
2
+ import sys
3
+ from pathlib import Path
4
+ import argparse
5
+ import json
6
+
7
+
8
+ def extract_json_objects (compiler_output : str ) -> list [dict ]:
9
+
10
+ return json_objects
11
+
12
+
13
+ def extract_warnings_from_compiler_output (compiler_output : str ) -> list [dict ]:
14
+ """
15
+ Extracts warnings from the compiler output when using -fdiagnostics-format=json
16
+
17
+ Compiler output as a whole is not a valid json document, but includes many json
18
+ objects and may include other output that is not json.
19
+ """
20
+ # Extract JSON objects from the raw compiler output
21
+ compiler_output_json_objects = []
22
+ stack = []
23
+ start_index = None
24
+ for index , char in enumerate (compiler_output ):
25
+ if char == '[' :
26
+ if len (stack ) == 0 :
27
+ start_index = index # Start of a new JSON array
28
+ stack .append (char )
29
+ elif char == ']' :
30
+ if len (stack ) > 0 :
31
+ stack .pop ()
32
+ if len (stack ) == 0 and start_index is not None :
33
+ try :
34
+ json_data = json .loads (compiler_output [start_index :index + 1 ])
35
+ compiler_output_json_objects .extend (json_data )
36
+ start_index = None
37
+ except json .JSONDecodeError :
38
+ continue # Skip malformed JSON
39
+
40
+ compiler_warnings = [entry for entry in compiler_output_json_objects if entry .get ('kind' ) == 'warning' ]
41
+
42
+ return compiler_warnings
43
+
44
+
45
+
46
+ def get_unexpected_warnings (
47
+ warnings : list [dict ],
48
+ files_with_expected_warnings : set [str ],
49
+ ) -> int :
50
+ """
51
+ Fails if there are unexpected warnings
52
+ """
53
+ unexpected_warnings = []
54
+ for warning in warnings :
55
+ locations = warning ['locations' ]
56
+ for location in locations :
57
+ for key in ['caret' , 'start' , 'end' ]:
58
+ if key in location :
59
+ filename = location [key ]['file' ]
60
+ if filename not in files_with_expected_warnings :
61
+ unexpected_warnings .append (warning )
62
+
63
+ if unexpected_warnings :
64
+ print ("Unexpected warnings:" )
65
+ for warning in unexpected_warnings :
66
+ print (warning )
67
+ return 1
68
+
69
+
70
+ return 0
71
+
72
+
73
+ def get_unexpected_improvements (
74
+ warnings : list [dict ],
75
+ files_with_expected_warnings : set [str ],
76
+ ) -> int :
77
+ """
78
+ Fails if files that were expected to have warnings have no warnings
79
+ """
80
+
81
+ # Create set of files with warnings
82
+ files_with_ewarnings = set ()
83
+ for warning in warnings :
84
+ locations = warning ['locations' ]
85
+ for location in locations :
86
+ for key in ['caret' , 'start' , 'end' ]:
87
+ if key in location :
88
+ filename = location [key ]['file' ]
<
9E7A
tr class="diff-line-row">
89
+ files_with_ewarnings .add (filename )
90
+
91
+
92
+
93
+ unexpected_improvements = []
94
+ for filename in files_with_expected_warnings :
95
+ if filename not in files_with_ewarnings :
96
+ unexpected_improvements .append (filename )
97
+
98
+ if unexpected_improvements :
99
+ print ("Unexpected improvements:" )
100
+ for filename in unexpected_improvements :
101
+ print (filename )
102
+ return 1
103
+
104
+
105
+ return 0
106
+
107
+
108
+
109
+
110
+
111
+
112
+ def main (argv : list [str ] | None = None ) -> int :
113
+ parser = argparse .ArgumentParser ()
114
+ parser .add_argument (
115
+ "--compiler-output-file-path" ,
116
+ type = str ,
117
+ required = True ,
118
+ help = "Path to the file"
119
+ )
120
+ parser .add_argument (
121
+ "--warning-ignore-file-path" ,
122
+ type = str ,
123
+ required = True ,
124
+ help = "Path to the warning ignore file"
125
+ )
126
+
127
+
128
+ args = parser .parse_args (argv )
129
+
130
+ exit_code = 0
131
+
132
+ # Check that the compiler output file is a valid path
133
+ if not Path (args .compiler_output_file_path ).is_file ():
134
+ print (f"Compiler output file does not exist: { args .compiler_output_file_path } " )
135
+ return 1
136
+
137
+ # Check that the warning ignore file is a valid path
138
+ if not Path (args .warning_ignore_file_path ).is_file ():
139
+ print (f"Warning ignore file does not exist: { args .warning_ignore_file_path } " )
140
+ return 1
141
+
142
+ with Path (args .compiler_output_file_path ).open (encoding = "UTF-8" ) as f :
143
+ compiler_output_file_contents = f .read ()
144
+
145
+ with Path (args .warning_ignore_file_path ).open (encoding = "UTF-8" ) as clean_files :
146
+ files_with_expected_warnings = {
147
+ filename .strip ()
148
+ for filename in clean_files
149
+ if filename .strip () and not filename .startswith ("#" )
150
+ }
151
+
152
+
153
+ if len (compiler_output_file_contents ) > 0 :
154
+ print ("Successfully got compiler output" )
155
+ else :
156
+ exit_code = 1
157
+
158
+ if len (files_with_expected_warnings ) > 0 :
159
+ print ("we have some exceptions" )
160
+
161
+
162
+ warnings = extract_warnings_from_compiler_output (compiler_output_file_contents )
163
+
164
+ exit_code = get_unexpected_warnings (warnings , files_with_expected_warnings )
165
+
166
+ exit_code = get_unexpected_improvements (warnings , files_with_expected_warnings )
167
+
168
+
169
+
170
+
171
+ return exit_code
172
+
173
+
174
+
175
+
176
+
177
+
178
+
179
+
180
+ if __name__ == "__main__" :
181
+ sys .exit (main ())
0 commit comments