lint: Fix lint-format-strings false positives when format specifiers have argument positions

Do not error on valid format specifications like strprintf("arg2=%2$s arg1=%1$s arg2=%2$s", arg1, arg2);

Needed to avoid lint error in upcoming commit: https://cirrus-ci.com/task/4755032734695424?logs=lint#L221

Additionally tested with python -m doctest test/lint/run-lint-format-strings.py
This commit is contained in:
Ryan Ofsky 2023-03-23 10:29:35 -04:00
parent c63c8a1590
commit 398c3719b0

View file

@ -241,20 +241,32 @@ def count_format_specifiers(format_string):
3 3
>>> count_format_specifiers("foo %d bar %i foo %% foo %*d foo") >>> count_format_specifiers("foo %d bar %i foo %% foo %*d foo")
4 4
>>> count_format_specifiers("foo %5$d")
5
>>> count_format_specifiers("foo %5$*7$d")
7
""" """
assert type(format_string) is str assert type(format_string) is str
format_string = format_string.replace('%%', 'X') format_string = format_string.replace('%%', 'X')
n = 0 n = max_pos = 0
in_specifier = False for m in re.finditer("%(.*?)[aAcdeEfFgGinopsuxX]", format_string, re.DOTALL):
for i, char in enumerate(format_string): # Increase the max position if the argument has a position number like
if char == "%": # "5$", otherwise increment the argument count.
in_specifier = True pos_num, = re.match(r"(?:(^\d+)\$)?", m.group(1)).groups()
if pos_num is not None:
max_pos = max(max_pos, int(pos_num))
else:
n += 1 n += 1
elif char in "aAcdeEfFgGinopsuxX":
in_specifier = False # Increase the max position if there is a "*" width argument with a
elif in_specifier and char == "*": # position like "*7$", and increment the argument count if there is a
# "*" width argument with no position.
star, star_pos_num = re.match(r"(?:.*?(\*(?:(\d+)\$)?)|)", m.group(1)).groups()
if star_pos_num is not None:
max_pos = max(max_pos, int(star_pos_num))
elif star is not None:
n += 1 n += 1
return n return max(n, max_pos)
def main(): def main():