From 44dd7c2d95b556eb53681801fc8275b7100274de Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Fri, 19 Apr 2019 18:05:14 -0700 Subject: [PATCH] lint: Strengthen lint checks for string % non-tuple. This is really a job for an AST parser rather than a pile of regexes; among other issues, these will still miss violations that span multiple lines. But, you know, I tried. Signed-off-by: Anders Kaseorg --- tools/linter_lib/custom_check.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/tools/linter_lib/custom_check.py b/tools/linter_lib/custom_check.py index 35b9694f6b..0d3fa09c01 100644 --- a/tools/linter_lib/custom_check.py +++ b/tools/linter_lib/custom_check.py @@ -216,6 +216,17 @@ def check_file_for_long_lines(fn: str, ok = False return ok +PYDELIMS = r'''"'()\[\]{}#\\''' +PYREG = r"[^{}]".format(PYDELIMS) +PYSQ = r'"(?:[^"\\]|\\.)*"' +PYDQ = r"'(?:[^'\\]|\\.)*'" +PYLEFT = r"[(\[{]" +PYRIGHT = r"[)\]}]" +PYCODE = PYREG +for depth in range(5): + PYGROUP = r"""(?:{}|{}|{}{}*{})""".format(PYSQ, PYDQ, PYLEFT, PYCODE, PYRIGHT) + PYCODE = r"""(?:{}|{})""".format(PYREG, PYGROUP) + def build_custom_checkers(by_lang): # type: (Dict[str, List[str]]) -> Tuple[Callable[[], bool], Callable[[], bool]] @@ -465,15 +476,14 @@ def build_custom_checkers(by_lang): # This next check could have false positives, but it seems pretty # rare; if we find any, they can be added to the exclude list for # this rule. - {'pattern': r''' % [a-zA-Z0-9_."']*\)?$''', - 'exclude_line': set([ - ('tools/tests/test_template_parser.py', '{% foo'), - ]), - 'description': 'Used % comprehension without a tuple', + {'pattern': r"""^(?:[^'"#\\]|{}|{})*(?:{}|{})\s*%\s*(?![\s({{\\]|dict\(|tuple\()(?:[^,{}]|{})+(?:$|[,#\\]|{})""".format( + PYSQ, PYDQ, PYSQ, PYDQ, PYDELIMS, PYGROUP, PYRIGHT), + 'description': 'Used % formatting without a tuple', 'good_lines': ['"foo %s bar" % ("baz",)'], 'bad_lines': ['"foo %s bar" % "baz"']}, - {'pattern': r'''.*%s.* % \([a-zA-Z0-9_."']*\)$''', - 'description': 'Used % comprehension without a tuple', + {'pattern': r"""^(?:[^'"#\\]|{}|{})*(?:{}|{})\s*%\s*\((?:[^,{}]|{})*\)""".format( + PYSQ, PYDQ, PYSQ, PYDQ, PYDELIMS, PYGROUP), + 'description': 'Used % formatting with parentheses that do not form a tuple', 'good_lines': ['"foo %s bar" % ("baz",)"'], 'bad_lines': ['"foo %s bar" % ("baz")']}, {'pattern': 'sudo',