diff --git a/doc/whatsnew/fragments/10664.false_positive b/doc/whatsnew/fragments/10664.false_positive new file mode 100644 index 0000000000..3e0716ac50 --- /dev/null +++ b/doc/whatsnew/fragments/10664.false_positive @@ -0,0 +1,5 @@ +Fix a false positive for ``invalid-name`` on exclusive module-level assignments +composed of three or more branches. We won't raise ``disallowed-name`` on module-level names that can't be inferred +until a further refactor to remove this false negative is done. + +Closes #10664 diff --git a/pylint/checkers/base/name_checker/checker.py b/pylint/checkers/base/name_checker/checker.py index 29fb52d051..c2f2655282 100644 --- a/pylint/checkers/base/name_checker/checker.py +++ b/pylint/checkers/base/name_checker/checker.py @@ -523,16 +523,22 @@ def visit_assignname( # pylint: disable=too-many-branches,too-many-statements if ( (iattrs := tuple(node.frame().igetattr(node.name))) and util.Uninferable not in iattrs - and len(iattrs) == 2 - and astroid.are_exclusive(*iattrs) + and len(iattrs) > 1 + and all( + astroid.are_exclusive(*combo) + for combo in itertools.combinations(iattrs, 2) + ) ): node_type = "const" - self._check_name( - node_type, - node.name, - node, - disallowed_check_only=redefines_import, - ) + if not self._meets_exception_for_non_consts( + inferred_assign_type, node.name + ): + self._check_name( + node_type, + node.name, + node, + disallowed_check_only=redefines_import, + ) # Check names defined in function scopes elif isinstance(frame, nodes.FunctionDef): diff --git a/tests/functional/d/disallowed_name.py b/tests/functional/d/disallowed_name.py index a6155dd346..21ffffcc5c 100644 --- a/tests/functional/d/disallowed_name.py +++ b/tests/functional/d/disallowed_name.py @@ -6,6 +6,6 @@ def baz(): # [disallowed-name] class foo(): # [disallowed-name] pass -foo = {}.keys() # [disallowed-name] +foo = {}.keys() # Should raise disallowed-name once _check_name() is refactored. foo = 42 # [disallowed-name] aaa = 42 # [invalid-name] diff --git a/tests/functional/d/disallowed_name.txt b/tests/functional/d/disallowed_name.txt index 535510a532..728d58aa6d 100644 --- a/tests/functional/d/disallowed_name.txt +++ b/tests/functional/d/disallowed_name.txt @@ -1,5 +1,4 @@ disallowed-name:3:0:3:7:baz:"Disallowed name ""baz""":HIGH disallowed-name:6:0:6:9:foo:"Disallowed name ""foo""":HIGH -disallowed-name:9:0:9:3::"Disallowed name ""foo""":HIGH disallowed-name:10:0:10:3::"Disallowed name ""foo""":HIGH invalid-name:11:0:11:3::"Constant name ""aaa"" doesn't conform to UPPER_CASE naming style":HIGH diff --git a/tests/functional/i/invalid/invalid_name/invalid_name_module_level.py b/tests/functional/i/invalid/invalid_name/invalid_name_module_level.py index 8c2c2f2482..9012cea417 100644 --- a/tests/functional/i/invalid/invalid_name/invalid_name_module_level.py +++ b/tests/functional/i/invalid/invalid_name/invalid_name_module_level.py @@ -26,7 +26,19 @@ def A(): # [invalid-name] ASSIGNMENT_THAT_CRASHED_PYLINT = type(float.__new__.__code__) +# Exclusive assignment: uses const regex if CONST: OTHER_CONST = 1 -else: +elif CONSTA: OTHER_CONST = 2 +else: + OTHER_CONST = 3 + + +# Lists, sets, and objects can pass against the variable OR const regexes. +if CONST: + other_const = [1] +elif CONSTA: + other_const = [2] +else: + other_const = [3]