1import sys
2
3from . import check, v
4
5assert v  # Silence pyflakes.
6
7
8def test_function_object1(v):
9    v.scan(
10        """\
11def func():
12    pass
13
14a = func
15"""
16    )
17    check(v.defined_funcs, ["func"])
18    check(v.unused_funcs, [])
19
20
21def test_function_object2(v):
22    v.scan(
23        """\
24def func():
25    pass
26
27func
28"""
29    )
30    check(v.defined_funcs, ["func"])
31    check(v.unused_funcs, [])
32
33
34def test_function1(v):
35    v.scan(
36        """\
37def func1(a):
38    pass
39
40def func2(b):
41    func1(b)
42"""
43    )
44    check(v.defined_funcs, ["func1", "func2"])
45    check(v.unused_funcs, ["func2"])
46
47
48def test_function2(v):
49    v.scan(
50        """\
51def func(a):
52    pass
53
54func(5)
55"""
56    )
57    check(v.unused_funcs, [])
58    check(v.defined_funcs, ["func"])
59
60
61def test_function3(v):
62    v.scan(
63        """\
64def foo(a):
65    pass
66
67b = foo(5)
68"""
69    )
70    check(v.unused_funcs, [])
71    check(v.defined_funcs, ["foo"])
72
73
74def test_async_function(v):
75    v.scan(
76        """\
77async def foo():
78    pass
79"""
80    )
81    check(v.defined_funcs, ["foo"])
82    check(v.unused_funcs, ["foo"])
83
84
85def test_async_method(v):
86    v.scan(
87        """\
88class Foo:
89    async def bar(self):
90        pass
91"""
92    )
93    check(v.defined_classes, ["Foo"])
94    check(v.defined_funcs, [])
95    check(v.defined_methods, ["bar"])
96    check(v.unused_classes, ["Foo"])
97    check(v.unused_methods, ["bar"])
98
99
100def test_function_and_method1(v):
101    v.scan(
102        """\
103class Bar(object):
104    def func(self):
105        pass
106
107def func():
108    pass
109
110func()
111"""
112    )
113    check(v.defined_classes, ["Bar"])
114    check(v.defined_funcs, ["func"])
115    check(v.defined_methods, ["func"])
116    check(v.unused_classes, ["Bar"])
117    check(v.unused_funcs, [])
118    # Bar.func is unused, but it's hard to detect this without producing a
119    # false positive in test_function_and_method2.
120    check(v.unused_methods, [])
121
122
123def test_function_and_method2(v):
124    v.scan(
125        """\
126class Bar(object):
127    def func(self):
128        pass
129
130    other_name_for_func = func
131
132Bar().other_name_for_func()
133"""
134    )
135    check(v.defined_classes, ["Bar"])
136    check(v.defined_funcs, [])
137    check(v.defined_methods, ["func"])
138    check(v.defined_vars, ["other_name_for_func"])
139    check(v.unused_classes, [])
140    check(v.unused_funcs, [])
141    check(v.unused_methods, [])
142    check(v.unused_vars, [])
143
144
145def test_attribute1(v):
146    v.scan(
147        """\
148foo.bar = 1
149foo.bar = 2
150"""
151    )
152    check(v.unused_funcs, [])
153    check(v.defined_funcs, [])
154    check(v.defined_attrs, ["bar", "bar"])
155    check(v.used_names, ["foo"])
156    check(v.unused_attrs, ["bar", "bar"])
157
158
159def test_ignored_attributes(v):
160    v.scan(
161        """\
162A._ = 0
163A._a = 1
164A.__b = 2
165A.__c__ = 3
166A._d_ = 4
167"""
168    )
169    check(v.defined_attrs, ["_", "_a", "__b", "__c__", "_d_"])
170    check(v.used_names, ["A"])
171    check(v.unused_attrs, ["_", "__b", "__c__", "_a", "_d_"])
172    check(v.unused_vars, [])
173
174
175def test_getattr(v):
176    v.scan(
177        """\
178class Thing:
179    used_attr1 = 1
180    used_attr2 = 2
181    used_attr3 = 3
182    unused_attr = 4
183
184getattr(Thing, "used_attr1")
185getattr(Thing, "used_attr2", None)
186hasattr(Thing, "used_attr3")
187
188# Weird calls ignored
189hasattr(Thing, "unused_attr", None)
190getattr(Thing)
191getattr("unused_attr")
192getattr(Thing, "unused_attr", 1, 2)
193"""
194    )
195    check(v.unused_vars, ["unused_attr"])
196    check(
197        v.used_names,
198        [
199            "Thing",
200            "getattr",
201            "hasattr",
202            "used_attr1",
203            "used_attr2",
204            "used_attr3",
205        ],
206    )
207
208
209def test_callback1(v):
210    v.scan(
211        """\
212class Bar(object):
213    def foo(self):
214        pass
215
216b = Bar()
217b.foo
218"""
219    )
220    check(v.used_names, ["Bar", "b", "foo"])
221    check(v.defined_classes, ["Bar"])
222    check(v.defined_funcs, [])
223    check(v.defined_methods, ["foo"])
224    check(v.unused_classes, [])
225    check(v.unused_funcs, [])
226
227
228def test_class1(v):
229    v.scan(
230        """\
231class Bar(object):
232    pass
233"""
234    )
235    check(v.used_names, [])
236    check(v.defined_classes, ["Bar"])
237    check(v.unused_classes, ["Bar"])
238
239
240def test_class2(v):
241    v.scan(
242        """\
243class Bar():
244    pass
245class Foo(Bar):
246    pass
247Foo()
248"""
249    )
250    check(v.used_names, ["Bar", "Foo"])
251    check(v.defined_classes, ["Bar", "Foo"])
252    check(v.unused_classes, [])
253
254
255def test_class3(v):
256    v.scan(
257        """\
258class Bar():
259    pass
260[Bar]
261"""
262    )
263    check(v.used_names, ["Bar"])
264    check(v.defined_classes, ["Bar"])
265    check(v.unused_classes, [])
266
267
268def test_class4(v):
269    v.scan(
270        """\
271class Bar():
272    pass
273Bar()
274"""
275    )
276    check(v.used_names, ["Bar"])
277    check(v.defined_classes, ["Bar"])
278    check(v.unused_classes, [])
279
280
281def test_class5(v):
282    v.scan(
283        """\
284class Bar():
285    pass
286b = Bar()
287"""
288    )
289    check(v.used_names, ["Bar"])
290    check(v.defined_classes, ["Bar"])
291    check(v.unused_classes, [])
292    check(v.unused_vars, ["b"])
293
294
295def test_class6(v):
296    v.scan(
297        """\
298class Bar():
299    pass
300a = []
301a.insert(0, Bar())
302"""
303    )
304    check(v.defined_classes, ["Bar"])
305    check(v.unused_classes, [])
306
307
308def test_class7(v):
309    v.scan(
310        """\
311class Bar(object):
312    pass
313
314class Foo(object):
315    def __init__(self):
316        self.b = xyz.Bar(self)
317"""
318    )
319    check(v.defined_classes, ["Bar", "Foo"])
320    check(v.unused_classes, ["Foo"])
321
322
323def test_method1(v):
324    v.scan(
325        """\
326def __init__(self):
327    self.a.foo()
328
329class Bar(object):
330    def foo(self):
331        pass
332
333    @classmethod
334    def bar(cls):
335        pass
336
337    @staticmethod
338    def foobar():
339        pass
340"""
341    )
342    check(v.defined_classes, ["Bar"])
343    check(v.defined_funcs, [])
344    check(v.defined_methods, ["foo", "bar", "foobar"])
345    check(v.unused_classes, ["Bar"])
346    check(v.unused_funcs, [])
347    check(v.unused_methods, ["bar", "foobar"])
348
349
350def test_token_types(v):
351    v.scan(
352        """\
353a
354b = 2
355c()
356x.d
357"""
358    )
359    check(v.defined_funcs, [])
360    check(v.defined_vars, ["b"])
361    check(v.used_names, ["a", "c", "d", "x"])
362    check(v.unused_attrs, [])
363    check(v.unused_funcs, [])
364    check(v.unused_props, [])
365    check(v.unused_vars, ["b"])
366
367
368def test_variable1(v):
369    v.scan("a = 1\nb = a")
370    check(v.defined_funcs, [])
371    check(v.used_names, ["a"])
372    check(v.defined_vars, ["a", "b"])
373    check(v.unused_vars, ["b"])
374
375
376def test_variable2(v):
377    v.scan("a = 1\nc = b.a")
378    check(v.defined_funcs, [])
379    check(v.defined_vars, ["a", "c"])
380    check(v.used_names, ["a", "b"])
381    check(v.unused_vars, ["c"])
382
383
384def test_variable3(v):
385    v.scan("(a, b), c = (d, e, f)")
386    check(v.defined_funcs, [])
387    check(v.defined_vars, ["a", "b", "c"])
388    check(v.used_names, ["d", "e", "f"])
389    check(v.unused_vars, ["a", "b", "c"])
390
391
392def test_variable4(v):
393    v.scan("for a, b in func(): a")
394    check(v.defined_funcs, [])
395    check(v.defined_vars, ["a", "b"])
396    check(v.used_names, ["a", "func"])
397    check(v.unused_vars, ["b"])
398
399
400def test_variable5(v):
401    v.scan("[a for a, b in func()]")
402    check(v.defined_vars, ["a", "b"])
403    check(v.used_names, ["a", "func"])
404    check(v.unused_vars, ["b"])
405
406
407def test_ignored_variables(v):
408    v.scan(
409        """\
410_ = 0
411_a = 1
412__b = 2
413__c__ = 3
414_d_ = 4
415"""
416    )
417    check(v.defined_vars, ["__b"])
418    check(sorted(v.used_names), [])
419    check(v.unused_vars, ["__b"])
420
421
422def test_prop1(v):
423    v.scan(
424        """\
425class Bar(object):
426    @property
427    def prop(self):
428        pass
429
430c = Bar()
431c.prop
432"""
433    )
434    check(v.defined_classes, ["Bar"])
435    check(v.defined_props, ["prop"])
436    check(v.unused_classes, [])
437    check(v.unused_props, [])
438
439
440def test_prop2(v):
441    v.scan(
442        """\
443class Bar(object):
444    @property
445    def prop(self):
446        pass
447
448prop = 1
449"""
450    )
451    check(v.defined_classes, ["Bar"])
452    check(v.defined_props, ["prop"])
453    check(v.defined_vars, ["prop"])
454    check(v.unused_classes, ["Bar"])
455    check(v.unused_props, ["prop"])
456
457
458def test_object_attribute(v):
459    v.scan(
460        """\
461class Bar(object):
462    def __init__(self):
463        self.a = []
464"""
465    )
466    check(v.defined_attrs, ["a"])
467    check(v.defined_classes, ["Bar"])
468    check(v.defined_vars, [])
469    check(v.used_names, [])
470    check(v.unused_attrs, ["a"])
471    check(v.unused_classes, ["Bar"])
472
473
474def test_function_names_in_test_file(v):
475    v.scan(
476        """\
477def test_func():
478    pass
479
480def other_func():
481    pass
482
483class TestClass:
484    pass
485
486class BasicTestCase:
487    pass
488
489class OtherClass:
490    pass
491""",
492        filename="dir/test_function_names.py",
493    )
494    check(v.defined_attrs, [])
495    check(v.defined_classes, ["OtherClass"])
496    check(v.defined_funcs, ["other_func"])
497    check(v.defined_vars, [])
498    check(v.used_names, [])
499    check(v.unused_attrs, [])
500    check(v.unused_classes, ["OtherClass"])
501    check(v.unused_funcs, ["other_func"])
502
503
504def test_async_function_name_in_test_file(v):
505    v.scan(
506        """\
507async def test_func():
508    pass
509
510async def other_func():
511    pass
512""",
513        filename="dir/test_function_names.py",
514    )
515    check(v.defined_funcs, ["other_func"])
516    check(v.unused_funcs, ["other_func"])
517
518
519def test_async_function_name_in_normal_file(v):
520    v.scan(
521        """\
522async def test_func():
523    pass
524
525async def other_func():
526    pass
527""",
528        filename="dir/function_names.py",
529    )
530    check(v.defined_funcs, ["test_func", "other_func"])
531    check(v.unused_funcs, ["other_func", "test_func"])
532
533
534def test_function_names_in_normal_file(v):
535    v.scan(
536        """\
537def test_func():
538    pass
539
540def other_func():
541    pass
542
543class TestClass:
544    pass
545
546class BasicTestCase:
547    pass
548
549class OtherClass:
550    pass
551"""
552    )
553    check(v.defined_attrs, [])
554    check(v.defined_classes, ["BasicTestCase", "OtherClass", "TestClass"])
555    check(v.defined_funcs, ["test_func", "other_func"])
556    check(v.defined_vars, [])
557    check(v.used_names, [])
558    check(v.unused_attrs, [])
559    check(v.unused_classes, ["BasicTestCase", "OtherClass", "TestClass"])
560    check(v.unused_funcs, ["other_func", "test_func"])
561
562
563def test_global_attribute(v):
564    v.scan(
565        """\
566# Module foo:
567a = 1
568if a == 1:
569    pass
570
571# Module bar:
572import foo
573foo.a = 2
574"""
575    )
576    check(v.defined_attrs, ["a"])
577    check(v.defined_vars, ["a"])
578    check(v.used_names, ["a", "foo"])
579    check(v.unused_attrs, [])
580
581
582def test_boolean(v):
583    v.scan(
584        """\
585a = True
586a
587"""
588    )
589    check(v.defined_vars, ["a"])
590    check(v.used_names, ["a"])
591    check(v.unused_vars, [])
592
593
594def test_builtin_types(v):
595    v.scan(
596        """\
597a = b
598a = 1
599a = "s"
600a = object
601a = False
602"""
603    )
604    check(v.defined_vars, ["a"] * 5)
605    check(v.used_names, ["b"])
606    check(v.unused_vars, ["a"] * 5)
607
608
609def test_unused_args(v):
610    v.scan(
611        """\
612def foo(x, y):
613    return x + 1
614"""
615    )
616    check(v.defined_vars, ["x", "y"])
617    check(v.used_names, ["x"])
618    check(v.unused_vars, ["y"])
619
620
621def test_unused_kwargs(v):
622    v.scan(
623        """\
624def foo(x, y=3, **kwargs):
625    return x + 1
626"""
627    )
628    check(v.defined_vars, ["kwargs", "x", "y"])
629    check(v.used_names, ["x"])
630    check(v.unused_vars, ["kwargs", "y"])
631
632
633def test_unused_kwargs_with_odd_name(v):
634    v.scan(
635        """\
636def foo(**bar):
637    pass
638"""
639    )
640    check(v.defined_vars, ["bar"])
641    check(v.used_names, [])
642    check(v.unused_vars, ["bar"])
643
644
645def test_unused_vararg(v):
646    v.scan(
647        """\
648def foo(*bar):
649    pass
650"""
651    )
652    check(v.defined_vars, ["bar"])
653    check(v.used_names, [])
654    check(v.unused_vars, ["bar"])
655
656
657def test_multiple_definition(v):
658    v.scan(
659        """\
660a = 1
661a = 2
662"""
663    )
664    check(v.defined_vars, ["a", "a"])
665    check(v.used_names, [])
666    check(v.unused_vars, ["a", "a"])
667
668
669def test_arg_type_annotation(v):
670    v.scan(
671        """\
672from typing import Iterable
673
674def f(n: int) -> Iterable[int]:
675    yield n
676"""
677    )
678
679    check(v.unused_vars, [])
680    check(v.unused_funcs, ["f"])
681    check(v.unused_imports, [])
682
683
684def test_var_type_annotation(v):
685    v.scan(
686        """\
687from typing import List
688
689x: List[int] = [1]
690"""
691    )
692
693    check(v.unused_vars, ["x"])
694    check(v.unused_funcs, [])
695    check(v.unused_imports, [])
696
697
698def test_type_hint_comments(v):
699    v.scan(
700        """\
701from typing import Any, Dict, List, Text, Tuple
702
703
704def plain_function(arg):
705    # type: (Text) -> None
706    pass
707
708async def async_function(arg):
709    # type: (List[int]) -> None
710    pass
711
712some_var = {}  # type: Dict[str, str]
713
714class Thing:
715    def __init__(self):
716        self.some_attr = (1, 2)  # type: Tuple[int, int]
717
718for x in []:  # type: Any
719    print(x)
720"""
721    )
722
723    if sys.version_info < (3, 8):
724        check(v.unused_imports, ["Any", "Dict", "List", "Text", "Tuple"])
725    else:
726        check(v.unused_imports, [])
727        assert not v.found_dead_code_or_error
728
729
730def test_invalid_type_comment(v):
731    v.scan(
732        """\
733def bad():
734    # type: bogus
735    pass
736bad()
737"""
738    )
739
740    if sys.version_info < (3, 8):
741        assert not v.found_dead_code_or_error
742    else:
743        assert v.found_dead_code_or_error
744