Emit a correct invalid-name message when using multi-naming style. (#4924)
* Emit a correct invalid-name message when using multi-naming style.
Previously, given `--function-rgx=(?:(?P<UP>[A-Z]+)|(?P<down>[a-z]+))$` and the code:
```
def FOO():
pass
def UPPER():
pass
def lower():
pass
```
It would emit a message: ``Function name `lower` doesn't conform to '(?:(?P<UP>[A-Z]+)|(?P<down>[a-z]+))$' pattern [invalid-name]``.
The message is misleading as `lower` *does* conform to `(?:(?P<UP>[A-Z]+)|(?P<down>[a-z]+))$`. It's just not the prevalent group "UP".
After this commit, the message becomes: ``Function name `lower` doesn't conform to UP group in the '(?:(?P<UP>[A-Z]+)|(?P<down>[a-z]+))$' pattern [invalid-name]``
Co-authored-by: Pierre Sassoulas <[email protected]>
diff --git a/ChangeLog b/ChangeLog
index 2664a84..f0f5c1a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -43,6 +43,7 @@
..
Put bug fixes that should not wait for a new minor version here
+* The ``invalid-name`` message is now more detailed when using multiple naming style regexes.
What's New in Pylint 2.10.2?
diff --git a/doc/whatsnew/2.11.rst b/doc/whatsnew/2.11.rst
index bd95563..ce66052 100644
--- a/doc/whatsnew/2.11.rst
+++ b/doc/whatsnew/2.11.rst
@@ -40,3 +40,4 @@
* Added ``py-version`` config key (if ``[MASTER]`` section). Used for version dependant checks.
Will default to whatever Python version pylint is executed with.
+* The ``invalid-name`` message is now more detailed when using multiple naming style regexes.
diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py
index 6aba62d..d2599d9 100644
--- a/pylint/checkers/base.py
+++ b/pylint/checkers/base.py
@@ -66,7 +66,7 @@
import itertools
import re
import sys
-from typing import Pattern
+from typing import Optional, Pattern
import astroid
from astroid import nodes
@@ -1903,6 +1903,7 @@
continue
groups = collections.defaultdict(list)
min_warnings = sys.maxsize
+ prevalent_group, _ = max(all_groups.items(), key=lambda item: len(item[1]))
for group in all_groups.values():
groups[len(group)].append(group)
min_warnings = min(len(group), min_warnings)
@@ -1915,7 +1916,7 @@
else:
warnings = groups[min_warnings][0]
for args in warnings:
- self._raise_name_warning(*args)
+ self._raise_name_warning(*args, prevalent_group=prevalent_group)
@utils.check_messages(
"disallowed-name", "invalid-name", "assign-to-new-keyword", "non-ascii-name"
@@ -2016,10 +2017,21 @@
return self._name_group.get(node_type, node_type)
def _raise_name_warning(
- self, node, node_type, name, confidence, warning="invalid-name"
- ):
+ self,
+ node: nodes.NodeNG,
+ node_type: str,
+ name: str,
+ confidence,
+ warning: str = "invalid-name",
+ prevalent_group: Optional[str] = None,
+ ) -> None:
type_label = HUMAN_READABLE_TYPES[node_type]
hint = self._name_hints[node_type]
+ if prevalent_group:
+ # This happens in the multi naming match case. The expected
+ # prevalent group needs to be spelled out to make the message
+ # correct.
+ hint = f"the `{prevalent_group}` group in the {hint}"
if self.config.include_naming_hint:
hint += f" ({self._name_regexps[node_type].pattern!r} pattern)"
args = (
diff --git a/tests/checkers/unittest_base.py b/tests/checkers/unittest_base.py
index 7121f25..6a20db9 100644
--- a/tests/checkers/unittest_base.py
+++ b/tests/checkers/unittest_base.py
@@ -303,7 +303,11 @@
message = Message(
"invalid-name",
node=classes[0],
- args=("Class", "classb", "'(?:(?P<UP>[A-Z]+)|(?P<down>[a-z]+))$' pattern"),
+ args=(
+ "Class",
+ "classb",
+ "the `UP` group in the '(?:(?P<UP>[A-Z]+)|(?P<down>[a-z]+))$' pattern",
+ ),
)
with self.assertAddsMessages(message):
cls = None
@@ -340,7 +344,7 @@
args=(
"Class",
"CLASSC",
- "'(?:(?P<UP>[A-Z]+)|(?P<down>[a-z]+))$' pattern",
+ "the `down` group in the '(?:(?P<UP>[A-Z]+)|(?P<down>[a-z]+))$' pattern",
),
),
]
@@ -371,7 +375,11 @@
message = Message(
"invalid-name",
node=function_defs[1],
- args=("Function", "FUNC", "'(?:(?P<UP>[A-Z]+)|(?P<down>[a-z]+))$' pattern"),
+ args=(
+ "Function",
+ "FUNC",
+ "the `down` group in the '(?:(?P<UP>[A-Z]+)|(?P<down>[a-z]+))$' pattern",
+ ),
)
with self.assertAddsMessages(message):
func = None
@@ -402,7 +410,7 @@
args=(
"Function",
"UPPER",
- "'(?:(?P<ignore>FOO)|(?P<UP>[A-Z]+)|(?P<down>[a-z]+))$' pattern",
+ "the `down` group in the '(?:(?P<ignore>FOO)|(?P<UP>[A-Z]+)|(?P<down>[a-z]+))$' pattern",
),
)
with self.assertAddsMessages(message):
diff --git a/tests/functional/i/invalid/invalid_name_multinaming_style.py b/tests/functional/i/invalid/invalid_name_multinaming_style.py
new file mode 100644
index 0000000..561a154
--- /dev/null
+++ b/tests/functional/i/invalid/invalid_name_multinaming_style.py
@@ -0,0 +1,13 @@
+"""Test for multi naming style."""
+
+
+def UPPER(): # [invalid-name]
+ """UPPER func that will generate invalid-name error."""
+
+
+def lower():
+ """lower func that satisfies the prevalent group `lower`."""
+
+
+def anotherlower():
+ """Another lower func that satisfies the prevalent group `lower`."""
diff --git a/tests/functional/i/invalid/invalid_name_multinaming_style.rc b/tests/functional/i/invalid/invalid_name_multinaming_style.rc
new file mode 100644
index 0000000..d513cfb
--- /dev/null
+++ b/tests/functional/i/invalid/invalid_name_multinaming_style.rc
@@ -0,0 +1,2 @@
+[BASIC]
+function-rgx=^(?:(?P<UP>[A-Z]+)|(?P<down>[a-z]+))$
diff --git a/tests/functional/i/invalid/invalid_name_multinaming_style.txt b/tests/functional/i/invalid/invalid_name_multinaming_style.txt
new file mode 100644
index 0000000..abcde05
--- /dev/null
+++ b/tests/functional/i/invalid/invalid_name_multinaming_style.txt
@@ -0,0 +1 @@
+invalid-name:4:0:UPPER:"Function name ""UPPER"" doesn't conform to the `down` group in the '^(?:(?P<UP>[A-Z]+)|(?P<down>[a-z]+))$' pattern":HIGH