Add check to enforce literal syntax for Python builtin types

This check requires authors to initialize empty or zero builtin types
using the literal syntax (e.g., `{}` instead of `dict()`).

Authors may ignore this requirement for certain builtins using the
`--ignore` option.

Authors may also forbid calling `dict()` with keyword arguments
(`dict(a=1, b=2)`) using the `--no-allow-dict-kwargs` flag.
diff --git a/tests/check_builtin_literals_test.py b/tests/check_builtin_literals_test.py
new file mode 100644
index 0000000..a38e522
--- /dev/null
+++ b/tests/check_builtin_literals_test.py
@@ -0,0 +1,107 @@
+import ast
+
+import pytest
+
+from pre_commit_hooks.check_builtin_literals import BuiltinTypeCall
+from pre_commit_hooks.check_builtin_literals import BuiltinTypeVisitor
+from pre_commit_hooks.check_builtin_literals import main
+from testing.util import get_resource_path
+
+
+@pytest.fixture
+def visitor():
+    return BuiltinTypeVisitor()
+
+
+@pytest.mark.parametrize(
+    ('expression', 'calls'),
+    [
+        # complex
+        ("0j", []),
+        ("complex()", [BuiltinTypeCall('complex', 1, 0)]),
+        ("complex(0, 0)", []),
+        ("complex('0+0j')", []),
+        # float
+        ("0.0", []),
+        ("float()", [BuiltinTypeCall('float', 1, 0)]),
+        ("float('0.0')", []),
+        # int
+        ("0", []),
+        ("int()", [BuiltinTypeCall('int', 1, 0)]),
+        ("int('0')", []),
+        # list
+        ("[]", []),
+        ("list()", [BuiltinTypeCall('list', 1, 0)]),
+        ("list('abc')", []),
+        ("list([c for c in 'abc'])", []),
+        ("list(c for c in 'abc')", []),
+        # str
+        ("''", []),
+        ("str()", [BuiltinTypeCall('str', 1, 0)]),
+        ("str('0')", []),
+        ("[]", []),
+        # tuple
+        ("()", []),
+        ("tuple()", [BuiltinTypeCall('tuple', 1, 0)]),
+        ("tuple('abc')", []),
+        ("tuple([c for c in 'abc'])", []),
+        ("tuple(c for c in 'abc')", []),
+    ],
+)
+def test_non_dict_exprs(visitor, expression, calls):
+    visitor.visit(ast.parse(expression))
+    assert visitor.builtin_type_calls == calls
+
+
+@pytest.mark.parametrize(
+    ('expression', 'calls'),
+    [
+        ("{}", []),
+        ("dict()", [BuiltinTypeCall('dict', 1, 0)]),
+        ("dict(a=1, b=2, c=3)", []),
+        ("dict(**{'a': 1, 'b': 2, 'c': 3})", []),
+        ("dict([(k, v) for k, v in [('a', 1), ('b', 2), ('c', 3)]])", []),
+        ("dict((k, v) for k, v in [('a', 1), ('b', 2), ('c', 3)])", []),
+    ],
+)
+def test_dict_allow_kwargs_exprs(visitor, expression, calls):
+    visitor.visit(ast.parse(expression))
+    assert visitor.builtin_type_calls == calls
+
+
+@pytest.mark.parametrize(
+    ('expression', 'calls'),
+    [
+        ("dict()", [BuiltinTypeCall('dict', 1, 0)]),
+        ("dict(a=1, b=2, c=3)", [BuiltinTypeCall('dict', 1, 0)]),
+        ("dict(**{'a': 1, 'b': 2, 'c': 3})", [BuiltinTypeCall('dict', 1, 0)]),
+    ],
+)
+def test_dict_no_allow_kwargs_exprs(expression, calls):
+    visitor = BuiltinTypeVisitor(allow_dict_kwargs=False)
+    visitor.visit(ast.parse(expression))
+    assert visitor.builtin_type_calls == calls
+
+
+def test_ignore_constructors():
+    visitor = BuiltinTypeVisitor(ignore=('complex', 'dict', 'float', 'int', 'list', 'str', 'tuple'))
+    visitor.visit(ast.parse(open(get_resource_path('builtin_constructors.py'), 'rb').read(), 'builtin_constructors.py'))
+    assert visitor.builtin_type_calls == []
+
+
+def test_failing_file():
+    rc = main([get_resource_path('builtin_constructors.py')])
+    assert rc == 1
+
+
+def test_passing_file():
+    rc = main([get_resource_path('builtin_literals.py')])
+    assert rc == 0
+
+
+def test_failing_file_ignore_all():
+    rc = main([
+        '--ignore=complex,dict,float,int,list,str,tuple',
+        get_resource_path('builtin_constructors.py'),
+    ])
+    assert rc == 0