summaryrefslogtreecommitdiff
path: root/pylint/extensions
diff options
context:
space:
mode:
authororSolocate <38433858+orSolocate@users.noreply.github.com>2022-10-22 19:58:46 +0300
committerGitHub <noreply@github.com>2022-10-22 18:58:46 +0200
commit78770cdab486b19a2e7ab0cc03b41c518c819f9a (patch)
treec391f293e3cc4dfc9d465c51620483ac66038492 /pylint/extensions
parent36756fa7ad74049cc46db90fc0bbaafc616869fa (diff)
downloadpylint-git-78770cdab486b19a2e7ab0cc03b41c518c819f9a.tar.gz
Add `magic-number` checker for comparison with literals (#7526)
Co-authored-by: Or Bahari <or.bahari@samsung.com>
Diffstat (limited to 'pylint/extensions')
-rw-r--r--pylint/extensions/magic_value.py88
1 files changed, 88 insertions, 0 deletions
diff --git a/pylint/extensions/magic_value.py b/pylint/extensions/magic_value.py
new file mode 100644
index 000000000..161bb2c95
--- /dev/null
+++ b/pylint/extensions/magic_value.py
@@ -0,0 +1,88 @@
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+
+"""Checks for magic values instead of literals."""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from astroid import nodes
+
+from pylint.checkers import BaseChecker, utils
+from pylint.interfaces import HIGH
+
+if TYPE_CHECKING:
+ from pylint.lint import PyLinter
+
+
+class MagicValueChecker(BaseChecker):
+ """Checks for constants in comparisons."""
+
+ name = "magic-value"
+ msgs = {
+ "R2004": (
+ "Consider using a named constant or an enum instead of '%s'.",
+ "magic-value-comparison",
+ "Using named constants instead of magic values helps improve readability and maintainability of your"
+ " code, try to avoid them in comparisons.",
+ )
+ }
+
+ options = (
+ (
+ "valid-magic-values",
+ {
+ "default": (0, -1, 1, "", "__main__"),
+ "type": "csv",
+ "metavar": "<argument names>",
+ "help": " List of valid magic values that `magic-value-compare` will not detect.",
+ },
+ ),
+ )
+
+ def _check_constants_comparison(self, node: nodes.Compare) -> None:
+ """
+ Magic values in any side of the comparison should be avoided,
+ Detects comparisons that `comparison-of-constants` core checker cannot detect.
+ """
+ const_operands = []
+ LEFT_OPERAND = 0
+ RIGHT_OPERAND = 1
+
+ left_operand = node.left
+ const_operands.append(isinstance(left_operand, nodes.Const))
+
+ right_operand = node.ops[0][1]
+ const_operands.append(isinstance(right_operand, nodes.Const))
+
+ if all(const_operands):
+ # `comparison-of-constants` avoided
+ return
+
+ operand_value = None
+ if const_operands[LEFT_OPERAND] and self._is_magic_value(left_operand):
+ operand_value = left_operand.value
+ elif const_operands[RIGHT_OPERAND] and self._is_magic_value(right_operand):
+ operand_value = right_operand.value
+ if operand_value is not None:
+ self.add_message(
+ "magic-value-comparison",
+ node=node,
+ args=(operand_value),
+ confidence=HIGH,
+ )
+
+ def _is_magic_value(self, node: nodes.NodeNG) -> bool:
+ return (not utils.is_singleton_const(node)) and (
+ node.value not in self.linter.config.valid_magic_values
+ )
+
+ @utils.only_required_for_messages("magic-comparison")
+ def visit_compare(self, node: nodes.Compare) -> None:
+ self._check_constants_comparison(node)
+
+
+def register(linter: PyLinter) -> None:
+ linter.register_checker(MagicValueChecker(linter))