From 6df0b3e2a4381c64d4ca68c99d20b101b2b61fa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Wed, 16 Oct 2019 22:11:35 +0300 Subject: [PATCH] Flag various collections as dangerous defaults (#3183) (#3194) --- ChangeLog | 4 +++ doc/whatsnew/2.5.rst | 2 ++ pylint/checkers/base.py | 16 ++++++++-- .../d/dangerous_default_value_py30.py | 30 ++++++++++++++++++- .../d/dangerous_default_value_py30.txt | 9 +++++- 5 files changed, 57 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7ec37d5c43..b64a725cd4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -22,6 +22,10 @@ Release date: TBA Close #3190 +* Flag mutable ``collections.*`` utilities as dangerous defaults + + Close #3183 + What's New in Pylint 2.4.3? =========================== diff --git a/doc/whatsnew/2.5.rst b/doc/whatsnew/2.5.rst index 6a075f635e..afd2dda873 100644 --- a/doc/whatsnew/2.5.rst +++ b/doc/whatsnew/2.5.rst @@ -30,3 +30,5 @@ Other Changes A pyproject.toml file must prepend section names with ``tool.pylint.``, for example ``[tool.pylint.'MESSAGES CONTROL']``. These files can also be passed in on the command line. + +* Mutable ``collections.*`` are now flagged as dangerous defaults. diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py index bea58016d7..0906bf33f5 100644 --- a/pylint/checkers/base.py +++ b/pylint/checkers/base.py @@ -159,13 +159,25 @@ def get_regex(cls, name_type): # Name categories that are always consistent with all naming conventions. EXEMPT_NAME_CATEGORIES = {"exempt", "ignore"} -# A mapping from builtin-qname -> symbol, to be used when generating messages +# A mapping from qname -> symbol, to be used when generating messages # about dangerous default values as arguments DEFAULT_ARGUMENT_SYMBOLS = dict( zip( [".".join([BUILTINS, x]) for x in ("set", "dict", "list")], ["set()", "{}", "[]"], - ) + ), + **{ + x: "%s()" % x + for x in ( + "collections.deque", + "collections.ChainMap", + "collections.Counter", + "collections.OrderedDict", + "collections.defaultdict", + "collections.UserDict", + "collections.UserList", + ) + }, ) REVERSED_COMPS = {"<": ">", "<=": ">=", ">": "<", ">=": "<="} COMPARISON_OPERATORS = frozenset(("==", "!=", "<", ">", "<=", ">=")) diff --git a/tests/functional/d/dangerous_default_value_py30.py b/tests/functional/d/dangerous_default_value_py30.py index 58a22f4434..9aab816350 100644 --- a/tests/functional/d/dangerous_default_value_py30.py +++ b/tests/functional/d/dangerous_default_value_py30.py @@ -1,5 +1,5 @@ # pylint: disable=missing-docstring - +import collections HEHE = {} @@ -77,3 +77,31 @@ def function15(value=INVALID_DICT): # [dangerous-default-value] def function16(value={1}): # [dangerous-default-value] """set literal as default value""" return value + +def function17(value=collections.deque()): # [dangerous-default-value] + """mutable, dangerous""" + return value + +def function18(value=collections.ChainMap()): # [dangerous-default-value] + """mutable, dangerous""" + return value + +def function19(value=collections.Counter()): # [dangerous-default-value] + """mutable, dangerous""" + return value + +def function20(value=collections.OrderedDict()): # [dangerous-default-value] + """mutable, dangerous""" + return value + +def function21(value=collections.defaultdict()): # [dangerous-default-value] + """mutable, dangerous""" + return value + +def function22(value=collections.UserDict()): # [dangerous-default-value] + """mutable, dangerous""" + return value + +def function23(value=collections.UserList()): # [dangerous-default-value] + """mutable, dangerous""" + return value diff --git a/tests/functional/d/dangerous_default_value_py30.txt b/tests/functional/d/dangerous_default_value_py30.txt index 7571c00f7c..40caaf5a33 100644 --- a/tests/functional/d/dangerous_default_value_py30.txt +++ b/tests/functional/d/dangerous_default_value_py30.txt @@ -11,4 +11,11 @@ dangerous-default-value:52:function12:Dangerous default value dict() (builtins.d dangerous-default-value:61:function13:Dangerous default value OINK (builtins.dict) as argument dangerous-default-value:65:function14:Dangerous default value dict() (builtins.dict) as argument dangerous-default-value:73:function15:Dangerous default value INVALID_DICT (builtins.dict) as argument -dangerous-default-value:77:function16:Dangerous default value set() as argument \ No newline at end of file +dangerous-default-value:77:function16:Dangerous default value set() as argument +dangerous-default-value:81:function17:Dangerous default value deque() (collections.deque) as argument +dangerous-default-value:85:function18:Dangerous default value ChainMap() (collections.ChainMap) as argument +dangerous-default-value:89:function19:Dangerous default value Counter() (collections.Counter) as argument +dangerous-default-value:93:function20:Dangerous default value OrderedDict() (collections.OrderedDict) as argument +dangerous-default-value:97:function21:Dangerous default value defaultdict() (collections.defaultdict) as argument +dangerous-default-value:101:function22:Dangerous default value UserDict() (collections.UserDict) as argument +dangerous-default-value:105:function23:Dangerous default value UserList() (collections.UserList) as argument