diff --git a/ChangeLog b/ChangeLog index 7654860519..159b575aed 100644 --- a/ChangeLog +++ b/ChangeLog @@ -57,6 +57,10 @@ Release date: TBA Closes #4945 +* Fix false positive ``superfluous-parens`` for tuples created with inner tuples + + Closes #4907 + What's New in Pylint 2.10.3? ============================ diff --git a/doc/whatsnew/2.11.rst b/doc/whatsnew/2.11.rst index 6a22d5ba7a..cdbba865d4 100644 --- a/doc/whatsnew/2.11.rst +++ b/doc/whatsnew/2.11.rst @@ -65,3 +65,7 @@ Other Changes and ``pathlib.Path().write_text()`` Closes #4945 + +* Fix false positive ``superfluous-parens`` for tuples created with inner tuples + + Closes #4907 diff --git a/pylint/checkers/format.py b/pylint/checkers/format.py index 901259c3ab..1306070ba7 100644 --- a/pylint/checkers/format.py +++ b/pylint/checkers/format.py @@ -401,11 +401,11 @@ def _check_keyword_parentheses( depth -= 1 if depth: if contains_double_parens and tokens[i + 1].string == ")": - self.add_message( - "superfluous-parens", line=line_num, args=keyword_token - ) + # For walrus operators in `if (not)` conditions and comprehensions + if keyword_token in {"in", "if", "not"}: + continue return - contains_double_parens = 0 + contains_double_parens -= 1 continue # ')' can't happen after if (foo), since it would be a syntax error. if tokens[i + 1].string in (":", ")", "]", "}", "in") or tokens[ diff --git a/tests/checkers/unittest_format.py b/tests/checkers/unittest_format.py index b84ce44386..658a7e3f41 100644 --- a/tests/checkers/unittest_format.py +++ b/tests/checkers/unittest_format.py @@ -165,11 +165,6 @@ def testCheckKeywordParensHandlesUnnecessaryParens(self): (Message("superfluous-parens", line=1, args="if"), "if (foo):", 0), (Message("superfluous-parens", line=1, args="if"), "if ((foo, bar)):", 0), (Message("superfluous-parens", line=1, args="if"), "if (foo(bar)):", 0), - ( - Message("superfluous-parens", line=1, args="return"), - "return ((x for x in x))", - 0, - ), (Message("superfluous-parens", line=1, args="not"), "not (foo)", 0), (Message("superfluous-parens", line=1, args="not"), "if not (foo):", 1), (Message("superfluous-parens", line=1, args="if"), "if (not (foo)):", 0), diff --git a/tests/functional/s/super/superfluous_parens.py b/tests/functional/s/super/superfluous_parens.py index aea4b16c43..ad36583f9f 100644 --- a/tests/functional/s/super/superfluous_parens.py +++ b/tests/functional/s/super/superfluous_parens.py @@ -1,6 +1,7 @@ """Test the superfluous-parens warning.""" -from __future__ import print_function # pylint: disable=unneeded-not, unnecessary-comprehension, missing-function-docstring, invalid-name, fixme +# pylint: disable=import-error, missing-class-docstring, too-few-public-methods +import numpy as np A = 3 if (A == 5): # [superfluous-parens] pass @@ -18,10 +19,10 @@ del(DICT['b']) # [superfluous-parens] del DICT['a'] -B = [x for x in ((3, 4))] # [superfluous-parens] +B = [x for x in ((3, 4))] C = [x for x in ((3, 4) if 1 > 0 else (5, 6))] -D = [x for x in ((3, 4) if 1 > 0 else ((5, 6)))] # [superfluous-parens] -E = [x for x in ((3, 4) if 1 > 0 else ((((5, 6)))))] # [superfluous-parens] +D = [x for x in ((3, 4) if 1 > 0 else ((5, 6)))] +E = [x for x in ((3, 4) if 1 > 0 else ((((5, 6)))))] # Test assertions F = "Version 1.0" @@ -34,14 +35,30 @@ NUMS_LIST = ['1', '2', '3'] NUMS_SET = {'1', '2', '3'} NUMS_DICT = {'1': 1, '2': 2, '3': 3} +I = tuple(x for x in ((a, str(a)) for a in ())) # Test functions def function_A(): - return (x for x in ((3, 4))) # [superfluous-parens] + return (x for x in ((3, 4))) # TODO: Test string combinations, see https://github.com/PyCQA/pylint/issues/4792 # Lines 45, 46 & 47 should raise the superfluous-parens message -I = "TestString" -J = ("Test " + "String") -K = ("Test " + "String") in I +J = "TestString" +K = ("Test " + "String") +L = ("Test " + "String") in I assert "" + ("Version " + "String") in I + +# Test numpy +def function_B(var_1: int, var_2: int) -> np.ndarray: + result = (((var_1 & var_2)) > 0) + return result.astype(np.float32) + +def function_C(var_1: int, var_2: int) -> np.ndarray: + return (((var_1 & var_2)) > 0).astype(np.float32) + +# Test Class +class ClassA: + keys = [] + + def __iter__(self): + return ((k, getattr(self, k)) for k in self.keys) diff --git a/tests/functional/s/super/superfluous_parens.txt b/tests/functional/s/super/superfluous_parens.txt index 0afe7635db..d07817e3db 100644 --- a/tests/functional/s/super/superfluous_parens.txt +++ b/tests/functional/s/super/superfluous_parens.txt @@ -1,10 +1,6 @@ -superfluous-parens:5:0::Unnecessary parens after 'if' keyword:HIGH -superfluous-parens:7:0::Unnecessary parens after 'not' keyword:HIGH -superfluous-parens:11:0::Unnecessary parens after 'for' keyword:HIGH -superfluous-parens:13:0::Unnecessary parens after 'if' keyword:HIGH -superfluous-parens:18:0::Unnecessary parens after 'del' keyword:HIGH -superfluous-parens:21:0::Unnecessary parens after 'in' keyword:HIGH -superfluous-parens:23:0::Unnecessary parens after 'else' keyword:HIGH -superfluous-parens:24:0::Unnecessary parens after 'else' keyword:HIGH -superfluous-parens:30:0::Unnecessary parens after 'assert' keyword:HIGH -superfluous-parens:40:0::Unnecessary parens after 'in' keyword:HIGH +superfluous-parens:6:0::Unnecessary parens after 'if' keyword:HIGH +superfluous-parens:8:0::Unnecessary parens after 'not' keyword:HIGH +superfluous-parens:12:0::Unnecessary parens after 'for' keyword:HIGH +superfluous-parens:14:0::Unnecessary parens after 'if' keyword:HIGH +superfluous-parens:19:0::Unnecessary parens after 'del' keyword:HIGH +superfluous-parens:31:0::Unnecessary parens after 'assert' keyword:HIGH