9
9
import re
10
10
import sys
11
11
from pathlib import Path
12
+ from typing import NamedTuple
13
+
14
+ class FileWarnings (NamedTuple ):
15
+ name : str
16
+ count : int
12
17
13
18
14
19
def extract_warnings_from_compiler_output_clang (
@@ -19,7 +24,8 @@ def extract_warnings_from_compiler_output_clang(
19
24
"""
20
25
# Regex to find warnings in the compiler output
21
26
clang_warning_regex = re .compile (
22
- r"(?P<file>.*):(?P<line>\d+):(?P<column>\d+): warning: (?P<message>.*)"
27
+ r"(?P<file>.*):(?P<line>\d+):(?P<column>\d+): warning: "
28
+ r"(?P<message>.*) (?P<option>\[-[^\]]+\])$"
23
29
)
24
30
compiler_warnings = []
25
31
for line in compiler_output .splitlines ():
@@ -30,6 +36,7 @@ def extract_warnings_from_compiler_output_clang(
30
36
"line" : match .group ("line" ),
31
37
"column" : match .group ("column" ),
32
38
"message" : match .group ("message" ),
39
+ "option" : match .group ("option" ).lstrip ("[" ).rstrip ("]" ),
33
40
}
34
41
)
35
42
@@ -49,9 +56,7 @@ def extract_warnings_from_compiler_output_json(
49
56
"""
50
57
# Regex to find json arrays at the top level of the file
51
58
# in the compiler output
52
- json_arrays = re .findall (
53
- r"\[(?:[^[\]]|\[[^\]]*\])*\]" , compiler_output
54
- )
59
+ json_arrays = re .findall (r"\[(?:[^[\]]|\[[^]]*])*]" , compiler_output )
55
60
compiler_warnings = []
56
61
for array in json_arrays :
57
62
try :
@@ -74,6 +79,7 @@ def extract_warnings_from_compiler_output_json(
74
79
"line" : location [key ]["line" ],
75
80
"column" : location [key ]["column" ],
76
81
"message" : warning ["message" ],
82
+ "option" : warning ["option" ],
77
83
}
78
84
)
79
85
# Found a caret, start, or end in location so
@@ -92,18 +98,26 @@ def extract_warnings_from_compiler_output_json(
92
98
def get_warnings_by_file (warnings : list [dict ]) -> dict [str , list [dict ]]:
93
99
"""
94
100
Returns a dictionary where the key is the file and the data is the warnings
95
- in that file
101
+ in that file. Does not include duplicate warnings for a file from list of
102
+ provided warnings.
96
103
"""
97
104
warnings_by_file = defaultdict (list )
105
+ warnings_added = set ()
98
106
for warning in warnings :
99
- warnings_by_file [warning ["file" ]].append (warning )
107
+ warning_key = (
108
+ f"{ warning ['file' ]} -{ warning ['line' ]} -"
109
+ f"{ warning ['column' ]} -{ warning ['option' ]} "
110
+ )
111
+ if warning_key not in warnings_added :
112
+ warnings_added .add (warning_key )
113
+ warnings_by_file [warning ["file" ]].append (warning )
100
114
101
115
return warnings_by_file
102
116
103
117
104
118
def get_unexpected_warnings (
105
- files_with_expected_warnings : set [str ],
106
- files_with_warnings : dict [ str , list [ dict ] ],
119
+ files_with_expected_warnings : set [FileWarnings ],
120
+ files_with_warnings : set [ FileWarnings ],
107
121
) -> int :
108
122
"""
109
123
Returns failure status if warnings discovered in list of warnings
@@ -112,7 +126,14 @@ def get_unexpected_warnings(
112
126
"""
113
127
unexpected_warnings = []
114
128
for file in files_with_warnings .keys ():
115
- if file not in files_with_expected_warnings :
129
+ found_file_in_ignore_list = False
130
+ for ignore_file in files_with_expected_warnings :
131
+ if file == ignore_file .name :
132
+ if len (files_with_warnings [file ]) > ignore_file .count :
133
+ unexpected_warnings .extend (files_with_warnings [file ])
134
+ found_file_in_ignore_list = True
135
+ break
136
+ if not found_file_in_ignore_list :
116
137
unexpected_warnings .extend (files_with_warnings [file ])
117
138
118
139
if unexpected_warnings :
@@ -125,22 +146,24 @@ def get_unexpected_warnings(
125
146
126
147
127
148
def get_unexpected_improvements (
128
- files_with_expected_warnings : set [str ],
129
- files_with_warnings : dict [ str , list [ dict ] ],
149
+ files_with_expected_warnings : set [FileWarnings ],
150
+ files_with_warnings : set [ FileWarnings ],
130
151
) -> int :
131
152
"""
132
153
Returns failure status if there are no warnings in the list of warnings
133
154
for a file that is in the list of files with expected warnings
134
155
"""
135
156
unexpected_improvements = []
136
157
for file in files_with_expected_warnings :
137
- if file not in files_with_warnings .keys ():
158
+ if file . name not in files_with_warnings .keys ():
138
159
unexpected_improvements .append (file )
160
+ elif len (files_with_warnings [file .name ]) < file .count :
161
+ unexpected_improvements .append (file )
139
162
140
163
if unexpected_improvements :
141
164
print ("Unexpected improvements:" )
142
165
for file in unexpected_improvements :
143
- print (file )
166
+ print (file . name )
144
167
return 1
145
168
146
169
return 0
@@ -214,8 +237,11 @@ def main(argv: list[str] | None = None) -> int:
214
237
with Path (args .warning_ignore_file_path ).open (
215
238
encoding = "UTF-8"
216
239
) as clean_files :
240
+ # Files with expected warnings are stored as a set of tuples
241
+ # where the first element is the file name and the second element
242
+ # is the number of warnings expected in that file
217
243
files_with_expected_warnings = {
218
- file .strip ()
244
+ FileWarnings ( file .strip (). split ()[ 0 ], int ( file . strip (). split ()[ 1 ]) )
219
245
for file in clean_files
220
246
if file .strip () and not file .startswith ("#" )
221
247
}
0 commit comments