diff --git a/ChangeLog b/ChangeLog index 29e172ba78..b8dce9a625 100644 --- a/ChangeLog +++ b/ChangeLog @@ -36,6 +36,14 @@ Release date: TBA Closes #3878 +* Added ``use-list-literal``: Emitted when ``list()`` is called with no arguments instead of using ``[]`` + + Closes #4365 + +* Added ``use-dict-literal``: Emitted when ``dict()`` is called with no arguments instead of using ``{}`` + + Closes #4365 + * ``CodeStyleChecker`` * Extended ``consider-using-tuple`` check to cover ``in`` comparisons. diff --git a/doc/whatsnew/2.10.rst b/doc/whatsnew/2.10.rst index 534b791303..6bfea5b5f6 100644 --- a/doc/whatsnew/2.10.rst +++ b/doc/whatsnew/2.10.rst @@ -16,6 +16,14 @@ New checkers Closes #3826 +* Added ``use-list-literal``: Emitted when ``list()`` is called with no arguments instead of using ``[]`` + + Closes #4365 + +* Added ``use-dict-literal``: Emitted when ``dict()`` is called with no arguments instead of using ``{}`` + + Closes #4365 + Extensions ========== diff --git a/pylint/checkers/refactoring/refactoring_checker.py b/pylint/checkers/refactoring/refactoring_checker.py index 9a4309a460..c46d71234d 100644 --- a/pylint/checkers/refactoring/refactoring_checker.py +++ b/pylint/checkers/refactoring/refactoring_checker.py @@ -413,6 +413,18 @@ class RefactoringChecker(checkers.BaseTokenChecker): "value by index lookup. " "The value can be accessed directly instead.", ), + "R1734": ( + "Consider using [] instead of list()", + "use-list-literal", + "Emitted when using list() to create an empty list instead of the literal []. " + "The literal is faster as it avoids an additional function call.", + ), + "R1735": ( + "Consider using {} instead of dict()", + "use-dict-literal", + "Emitted when using dict() to create an empty dictionary instead of the literal {}. " + "The literal is faster as it avoids an additional function call.", + ), } options = ( ( @@ -964,6 +976,8 @@ def _check_consider_using_generator(self, node): "super-with-arguments", "consider-using-generator", "consider-using-with", + "use-list-literal", + "use-dict-literal", ) def visit_call(self, node): self._check_raising_stopiteration_in_generator_next_call(node) @@ -972,6 +986,7 @@ def visit_call(self, node): self._check_super_with_arguments(node) self._check_consider_using_generator(node) self._check_consider_using_with(node) + self._check_use_list_or_dict_literal(node) @staticmethod def _has_exit_in_scope(scope): @@ -1454,6 +1469,16 @@ def _check_consider_using_with(self, node: astroid.Call): if could_be_used_in_with and not _will_be_released_automatically(node): self.add_message("consider-using-with", node=node) + def _check_use_list_or_dict_literal(self, node: astroid.Call) -> None: + """Check if empty list or dict is created by using the literal [] or {}""" + if node.as_string() in ("list()", "dict()"): + inferred = utils.safe_infer(node.func) + if isinstance(inferred, astroid.ClassDef) and not node.args: + if inferred.qname() == "builtins.list": + self.add_message("use-list-literal", node=node) + elif inferred.qname() == "builtins.dict" and not node.keywords: + self.add_message("use-dict-literal", node=node) + def _check_consider_using_join(self, aug_assign): """ We start with the augmented assignment and work our way upwards. diff --git a/pylint/checkers/stdlib.py b/pylint/checkers/stdlib.py index 4e6813c6a0..afc5faa80f 100644 --- a/pylint/checkers/stdlib.py +++ b/pylint/checkers/stdlib.py @@ -443,11 +443,11 @@ def __init__(self, linter=None): for since_vers, func_list in DEPRECATED_METHODS[sys.version_info[0]].items(): if since_vers <= sys.version_info: self._deprecated_methods.update(func_list) - self._deprecated_attributes = dict() + self._deprecated_attributes = {} for since_vers, func_list in DEPRECATED_ARGUMENTS.items(): if since_vers <= sys.version_info: self._deprecated_attributes.update(func_list) - self._deprecated_classes = dict() + self._deprecated_classes = {} for since_vers, class_list in DEPRECATED_CLASSES.items(): if since_vers <= sys.version_info: self._deprecated_classes.update(class_list) diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index 73d6fe0cc1..ffec05d6e3 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -502,7 +502,7 @@ def parse_format_string( IncompleteFormatString or UnsupportedFormatCharacter if a parse error occurs.""" keys = set() - key_types = dict() + key_types = {} pos_types = [] num_args = 0 diff --git a/pylint/utils/pragma_parser.py b/pylint/utils/pragma_parser.py index 363cc26652..488b4626ca 100644 --- a/pylint/utils/pragma_parser.py +++ b/pylint/utils/pragma_parser.py @@ -116,7 +116,7 @@ def parse_pragma(pylint_pragma: str) -> Generator[PragmaRepresenter, None, None] if action: yield emit_pragma_representer(action, messages) action = value - messages = list() + messages = [] assignment_required = action in MESSAGE_KEYWORDS elif kind in ("MESSAGE_STRING", "MESSAGE_NUMBER"): messages.append(value) diff --git a/tests/functional/c/consider/consider_using_dict_comprehension.py b/tests/functional/c/consider/consider_using_dict_comprehension.py index da0be6d5a7..c9d740e18c 100644 --- a/tests/functional/c/consider/consider_using_dict_comprehension.py +++ b/tests/functional/c/consider/consider_using_dict_comprehension.py @@ -1,4 +1,4 @@ -# pylint: disable=missing-docstring, invalid-name +# pylint: disable=missing-docstring, invalid-name, use-dict-literal numbers = [1, 2, 3, 4, 5, 6] diff --git a/tests/functional/c/consider/consider_using_dict_items.py b/tests/functional/c/consider/consider_using_dict_items.py index b8d230ca5d..32e92e8034 100644 --- a/tests/functional/c/consider/consider_using_dict_items.py +++ b/tests/functional/c/consider/consider_using_dict_items.py @@ -1,5 +1,5 @@ """Emit a message for iteration through dict keys and subscripting dict with key.""" -# pylint: disable=line-too-long,missing-docstring,unsubscriptable-object,too-few-public-methods,redefined-outer-name +# pylint: disable=line-too-long,missing-docstring,unsubscriptable-object,too-few-public-methods,redefined-outer-name,use-dict-literal def bad(): a_dict = {1: 1, 2: 2, 3: 3} diff --git a/tests/functional/c/consider/consider_using_get.py b/tests/functional/c/consider/consider_using_get.py index 8c5c689015..0b71431d0f 100644 --- a/tests/functional/c/consider/consider_using_get.py +++ b/tests/functional/c/consider/consider_using_get.py @@ -1,5 +1,5 @@ # pylint: disable=missing-docstring,invalid-name,using-constant-test,invalid-sequence-index,undefined-variable -dictionary = dict() +dictionary = {} key = 'key' if 'key' in dictionary: # [consider-using-get] diff --git a/tests/functional/d/dangerous_default_value_py30.py b/tests/functional/d/dangerous_default_value_py30.py index 4f74dbe266..161eaceed3 100644 --- a/tests/functional/d/dangerous_default_value_py30.py +++ b/tests/functional/d/dangerous_default_value_py30.py @@ -1,4 +1,4 @@ -# pylint: disable=missing-docstring +# pylint: disable=missing-docstring, use-list-literal, use-dict-literal import collections HEHE = {} diff --git a/tests/functional/g/generic_alias/generic_alias_side_effects.py b/tests/functional/g/generic_alias/generic_alias_side_effects.py index 64b3d9810c..ea7d7a9ce9 100644 --- a/tests/functional/g/generic_alias/generic_alias_side_effects.py +++ b/tests/functional/g/generic_alias/generic_alias_side_effects.py @@ -1,4 +1,4 @@ -# pylint: disable=missing-docstring,invalid-name,line-too-long,too-few-public-methods +# pylint: disable=missing-docstring,invalid-name,line-too-long,too-few-public-methods,use-list-literal,use-dict-literal import typing import collections from typing import Generic, TypeVar diff --git a/tests/functional/i/invalid/e/invalid_exceptions_caught.py b/tests/functional/i/invalid/e/invalid_exceptions_caught.py index 252643446f..5cb6c84b8b 100644 --- a/tests/functional/i/invalid/e/invalid_exceptions_caught.py +++ b/tests/functional/i/invalid/e/invalid_exceptions_caught.py @@ -1,4 +1,4 @@ -# pylint: disable=missing-docstring, too-few-public-methods, useless-object-inheritance +# pylint: disable=missing-docstring, too-few-public-methods, useless-object-inheritance, use-list-literal # pylint: disable=too-many-ancestors, no-absolute-import, import-error, multiple-imports,wrong-import-position from __future__ import print_function diff --git a/tests/functional/i/invalid/g/invalid_getnewargs_ex_returned.py b/tests/functional/i/invalid/g/invalid_getnewargs_ex_returned.py index e378950962..ad94b8805a 100644 --- a/tests/functional/i/invalid/g/invalid_getnewargs_ex_returned.py +++ b/tests/functional/i/invalid/g/invalid_getnewargs_ex_returned.py @@ -1,6 +1,6 @@ """Check invalid value returned by __getnewargs_ex__ """ -# pylint: disable=too-few-public-methods,missing-docstring,no-self-use,import-error, useless-object-inheritance +# pylint: disable=too-few-public-methods,missing-docstring,no-self-use,import-error, useless-object-inheritance, use-dict-literal import six from missing import Missing diff --git a/tests/functional/i/invalid/invalid_unary_operand_type.py b/tests/functional/i/invalid/invalid_unary_operand_type.py index f2a0124f6d..e7231c34da 100644 --- a/tests/functional/i/invalid/invalid_unary_operand_type.py +++ b/tests/functional/i/invalid/invalid_unary_operand_type.py @@ -1,6 +1,6 @@ """Detect problems with invalid operands used on invalid objects.""" # pylint: disable=missing-docstring,too-few-public-methods,invalid-name -# pylint: disable=unused-variable, useless-object-inheritance +# pylint: disable=unused-variable, useless-object-inheritance, use-dict-literal import collections diff --git a/tests/functional/n/not_callable.py b/tests/functional/n/not_callable.py index 8d5b000c5b..01a9009bf1 100644 --- a/tests/functional/n/not_callable.py +++ b/tests/functional/n/not_callable.py @@ -1,4 +1,4 @@ -# pylint: disable=missing-docstring,no-self-use,too-few-public-methods,wrong-import-position, useless-object-inheritance +# pylint: disable=missing-docstring,no-self-use,too-few-public-methods,wrong-import-position,useless-object-inheritance,use-dict-literal REVISION = None diff --git a/tests/functional/s/statement_without_effect.py b/tests/functional/s/statement_without_effect.py index bac4f67954..53da873d88 100644 --- a/tests/functional/s/statement_without_effect.py +++ b/tests/functional/s/statement_without_effect.py @@ -1,5 +1,5 @@ """Test for statements without effects.""" -# pylint: disable=too-few-public-methods, useless-object-inheritance, unnecessary-comprehension +# pylint: disable=too-few-public-methods, useless-object-inheritance, unnecessary-comprehension, use-list-literal # +1:[pointless-string-statement] """inline doc string should use a separated message""" diff --git a/tests/functional/u/unnecessary/unnecessary_comprehension.py b/tests/functional/u/unnecessary/unnecessary_comprehension.py index d6a3d1e3bc..3d76f6e66c 100644 --- a/tests/functional/u/unnecessary/unnecessary_comprehension.py +++ b/tests/functional/u/unnecessary/unnecessary_comprehension.py @@ -13,7 +13,7 @@ [2 * x for x in iterable] # exclude useful comprehensions [(x, y, 1) for x, y in iterable] # exclude useful comprehensions # Test case for issue #4499 -a_dict = dict() +a_dict = {} [(k, v) for k, v in a_dict.items()] # [unnecessary-comprehension] # Set comprehensions @@ -39,8 +39,8 @@ {2 * x: 3 + x for x in iterable} # exclude useful comprehensions # Some additional tests on helptext -- when object is already a list/set/dict -my_list = list() -my_dict = dict() +my_list = [] +my_dict = {} my_set = set() [elem for elem in my_list] # [unnecessary-comprehension] diff --git a/tests/functional/u/unnecessary/unnecessary_dict_index_lookup.py b/tests/functional/u/unnecessary/unnecessary_dict_index_lookup.py index f166e6c72a..55dd5688c0 100644 --- a/tests/functional/u/unnecessary/unnecessary_dict_index_lookup.py +++ b/tests/functional/u/unnecessary/unnecessary_dict_index_lookup.py @@ -1,7 +1,7 @@ # pylint: disable=missing-docstring, too-few-public-methods, expression-not-assigned, line-too-long -a_dict = dict() -b_dict = dict() +a_dict = {} +b_dict = {} for k, v in a_dict.items(): print(a_dict[k]) # [unnecessary-dict-index-lookup] diff --git a/tests/functional/u/unnecessary/unnecessary_lambda.py b/tests/functional/u/unnecessary/unnecessary_lambda.py index f0bd775f36..91f6bf86d3 100644 --- a/tests/functional/u/unnecessary/unnecessary_lambda.py +++ b/tests/functional/u/unnecessary/unnecessary_lambda.py @@ -1,4 +1,4 @@ -# pylint: disable=undefined-variable +# pylint: disable=undefined-variable, use-list-literal """test suspicious lambda expressions """ diff --git a/tests/functional/u/use/use_literal_dict.py b/tests/functional/u/use/use_literal_dict.py new file mode 100644 index 0000000000..3377b4e630 --- /dev/null +++ b/tests/functional/u/use/use_literal_dict.py @@ -0,0 +1,7 @@ +# pylint: disable=missing-docstring, invalid-name + +x = dict() # [use-dict-literal] +x = dict(a="1", b=None, c=3) +x = dict(zip(["a", "b", "c"], [1, 2, 3])) +x = {} +x = {"a": 1, "b": 2, "c": 3} diff --git a/tests/functional/u/use/use_literal_dict.txt b/tests/functional/u/use/use_literal_dict.txt new file mode 100644 index 0000000000..eede5530dc --- /dev/null +++ b/tests/functional/u/use/use_literal_dict.txt @@ -0,0 +1 @@ +use-dict-literal:3:4::"Consider using {} instead of dict()" diff --git a/tests/functional/u/use/use_literal_list.py b/tests/functional/u/use/use_literal_list.py new file mode 100644 index 0000000000..78614a49dd --- /dev/null +++ b/tests/functional/u/use/use_literal_list.py @@ -0,0 +1,9 @@ +# pylint: disable=missing-docstring, invalid-name + +x = list() # [use-list-literal] +x = list("string") +x = list(range(3)) +x = [] +x = ["string"] +x = [1, 2, 3] +x = [range(3)] diff --git a/tests/functional/u/use/use_literal_list.txt b/tests/functional/u/use/use_literal_list.txt new file mode 100644 index 0000000000..07987027d0 --- /dev/null +++ b/tests/functional/u/use/use_literal_list.txt @@ -0,0 +1 @@ +use-list-literal:3:4::"Consider using [] instead of list()" diff --git a/tests/functional/u/use/using_constant_test.py b/tests/functional/u/use/using_constant_test.py index b3d06230a5..74b8a9c1a1 100644 --- a/tests/functional/u/use/using_constant_test.py +++ b/tests/functional/u/use/using_constant_test.py @@ -2,7 +2,7 @@ # pylint: disable=invalid-name, missing-docstring,too-few-public-methods # pylint: disable=no-init,expression-not-assigned, useless-object-inheritance # pylint: disable=missing-parentheses-for-call-in-test, unnecessary-comprehension, condition-evals-to-constant - +# pylint: disable=use-list-literal, use-dict-literal import collections