Skip to content

Commit 2e5f470

Browse files
Update qhelp + alert messages
1 parent 6ca4f32 commit 2e5f470

16 files changed

+186
-212
lines changed

python/ql/src/Classes/CallsToInitDel/MissingCallToDel.qhelp

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,47 +4,49 @@
44
<qhelp>
55

66
<overview>
7-
<p>Python, unlike statically typed languages such as Java, allows complete freedom when calling methods during object destruction.
8-
However, standard object-oriented principles apply to Python classes using deep inheritance hierarchies.
9-
Therefore the developer has responsibility for ensuring that objects are properly cleaned up when
10-
there are multiple <code>__del__</code> methods that need to be called.
7+
<p>
8+
Python, unlike some other object-oriented languages such as Java, allows the developer complete freedom in
9+
when and how superclass finalizers are called during object finalization.
10+
However, the developer has responsibility for ensuring that objects are properly cleaned up, and that all superclass <code>__del__</code>
11+
methods are called.
1112
</p>
1213
<p>
13-
If the <code>__del__</code> method of a superclass is not called during object destruction it is likely that
14+
Classes with a <code>__del__</code> method (a finalizer) typically hold some resource such as a file handle that needs to be cleaned up.
15+
If the <code>__del__</code> method of a superclass is not called during object finalization, it is likely that
1416
that resources may be leaked.
1517
</p>
1618

17-
<p>A call to the <code>__del__</code> method of a superclass during object destruction may be omitted:
19+
<p>A call to the <code>__init__</code> method of a superclass during object initialization may be unintentionally skipped:
1820
</p>
1921
<ul>
20-
<li>When a subclass calls the <code>__del__</code> method of the wrong class.</li>
21-
<li>When a call to the <code>__del__</code> method of one its base classes is omitted.</li>
22+
<li>If a subclass calls the <code>__del__</code> method of the wrong class.</li>
23+
<li>If a call to the <code>__del__</code> method of one its base classes is omitted.</li>
24+
<li>If a call to <code>super().__del__</code> is used, but not all <code>__del__</code> methods in the Method Resolution Order (MRO)
25+
chain themselves call <code>super()</code>. This in particular arises more often in cases of multiple inheritance. </li>
2226
</ul>
2327

2428

2529
</overview>
2630
<recommendation>
27-
<p>Either be careful to explicitly call the <code>__del__</code> of the correct base class, or
28-
use <code>super()</code> throughout the inheritance hierarchy.</p>
29-
30-
<p>Alternatively refactor one or more of the classes to use composition rather than inheritance.</p>
31+
<p>Ensure that all superclass <code>__del__</code> methods are properly called.
32+
Either each base class's finalize method should be explicitly called, or <code>super()</code> calls
33+
should be consistently used throughout the inheritance hierarchy.</p>
3134

3235

3336
</recommendation>
3437
<example>
35-
<p>In this example, explicit calls to <code>__del__</code> are used, but <code>SportsCar</code> erroneously calls
38+
<p>In the following example, explicit calls to <code>__del__</code> are used, but <code>SportsCar</code> erroneously calls
3639
<code>Vehicle.__del__</code>. This is fixed in <code>FixedSportsCar</code> by calling <code>Car.__del__</code>.
3740
</p>
3841

39-
<sample src="MissingCallToDel.py" />
42+
<sample src="examples/MissingCallToDel.py" />
4043

4144
</example>
4245
<references>
4346

44-
<li>Python Tutorial: <a href="https://docs.python.org/2/tutorial/classes.html">Classes</a>.</li>
45-
<li>Python Standard Library: <a href="https://docs.python.org/2/library/functions.html#super">super</a>.</li>
46-
<li>Artima Developer: <a href="http://www.artima.com/weblogs/viewpost.jsp?thread=236275">Things to Know About Python Super</a>.</li>
47-
<li>Wikipedia: <a href="http://en.wikipedia.org/wiki/Composition_over_inheritance">Composition over inheritance</a>.</li>
47+
<li>Python Reference: <a href="https://docs.python.org/3/reference/datamodel.html#object.__del__">__del__</a>.</li>
48+
<li>Python Standard Library: <a href="https://docs.python.org/3/library/functions.html#super">super</a>.</li>
49+
<li>Python Glossary: <a href="https://docs.python.org/3/glossary.html#term-method-resolution-order">Method resolution order</a>.</li>
4850

4951
</references>
5052
</qhelp>

python/ql/src/Classes/CallsToInitDel/MissingCallToDel.ql

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,18 @@ where
3030
not possiblyMissingSuper.isNone() and
3131
possibleIssue = possiblyMissingSuper and
3232
msg =
33-
"This class does not call $@ during destruction. ($@ may be missing a call to super().__del__)"
33+
"This class does not call $@ during finalization. ($@ may be missing a call to super().__del__)"
3434
or
3535
possiblyMissingSuper.isNone() and
3636
(
3737
possibleIssue.asSome() = getDelMethod(base) and
3838
msg =
39-
"This class does not call $@ during destruction. ($@ may be missing a call to a base class __del__)"
39+
"This class does not call $@ during finalization. ($@ may be missing a call to a base class __del__)"
4040
or
4141
not exists(getDelMethod(base)) and
4242
possibleIssue.isNone() and
4343
msg =
44-
"This class does not call $@ during destruction. (The class lacks an __del__ method to ensure every base class __del__ is called.)"
44+
"This class does not call $@ during finalization. (The class lacks an __del__ method to ensure every base class __del__ is called.)"
4545
)
4646
)
4747
)

python/ql/src/Classes/CallsToInitDel/MissingCallToInit.qhelp

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,49 +4,47 @@
44
<qhelp>
55

66
<overview>
7-
<p>Python, unlike statically typed languages such as Java, allows complete freedom when calling methods during object initialization.
8-
However, standard object-oriented principles apply to Python classes using deep inheritance hierarchies.
9-
Therefore the developer has responsibility for ensuring that objects are properly initialized when
10-
there are multiple <code>__init__</code> methods that need to be called.
7+
<p>Python, unlike some other object-oriented languages such as Java, allows the developer complete freedom in
8+
when and how superclass initializers are called during object initialization.
9+
However, the developer has responsibility for ensuring that objects are properly initialized, and that all superclass <code>__init__</code>
10+
methods are called.
1111
</p>
1212
<p>
13-
If the <code>__init__</code> method of a superclass is not called during object initialization it is likely that
14-
that object will end up in an incorrect state.
13+
If the <code>__init__</code> method of a superclass is not called during object initialization, this can lead to errors due to
14+
the object not being fully initialized, such as having missing attributes.
1515
</p>
1616

17-
<p>A call to the <code>__init__</code> method of a superclass during object initialization may be omitted:
17+
<p>A call to the <code>__init__</code> method of a superclass during object initialization may be unintentionally skipped:
1818
</p>
1919
<ul>
20-
<li>When a subclass calls the <code>__init__</code> method of the wrong class.</li>
21-
<li>When a call to the <code>__init__</code> method of one its base classes is omitted.</li>
22-
<li>When multiple inheritance is used and a class inherits from several base classes,
23-
and at least one of those does not use <code>super()</code> in its own <code>__init__</code> method.</li>
20+
<li>If a subclass calls the <code>__init__</code> method of the wrong class.</li>
21+
<li>If a call to the <code>__init__</code> method of one its base classes is omitted.</li>
22+
<li>If a call to <code>super().__init__</code> is used, but not all <code>__init__</code> methods in the Method Resolution Order (MRO)
23+
chain themselves call <code>super()</code>. This in particular arises more often in cases of multiple inheritance. </li>
2424
</ul>
2525

2626

2727
</overview>
2828
<recommendation>
29-
<p>Either be careful to explicitly call the <code>__init__</code> of the correct base class, or
30-
use <code>super()</code> throughout the inheritance hierarchy.</p>
31-
32-
<p>Alternatively refactor one or more of the classes to use composition rather than inheritance.</p>
29+
<p>Ensure that all superclass <code>__init__</code> methods are properly called.
30+
Either each base class's initialize method should be explicitly called, or <code>super()</code> calls
31+
should be consistently used throughout the inheritance hierarchy.</p>
3332

3433

3534
</recommendation>
3635
<example>
37-
<p>In this example, explicit calls to <code>__init__</code> are used, but <code>SportsCar</code> erroneously calls
36+
<p>In the following example, explicit calls to <code>__init__</code> are used, but <code>SportsCar</code> erroneously calls
3837
<code>Vehicle.__init__</code>. This is fixed in <code>FixedSportsCar</code> by calling <code>Car.__init__</code>.
3938
</p>
4039

41-
<sample src="MissingCallToInit.py" />
40+
<sample src="examples/MissingCallToInit.py" />
4241

4342
</example>
4443
<references>
4544

46-
<li>Python Tutorial: <a href="https://docs.python.org/2/tutorial/classes.html">Classes</a>.</li>
47-
<li>Python Standard Library: <a href="https://docs.python.org/2/library/functions.html#super">super</a>.</li>
48-
<li>Artima Developer: <a href="http://www.artima.com/weblogs/viewpost.jsp?thread=236275">Things to Know About Python Super</a>.</li>
49-
<li>Wikipedia: <a href="http://en.wikipedia.org/wiki/Composition_over_inheritance">Composition over inheritance</a>.</li>
45+
<li>Python Reference: <a href="https://docs.python.org/3/reference/datamodel.html#object.__init__">__init__</a>.</li>
46+
<li>Python Standard Library: <a href="https://docs.python.org/3/library/functions.html#super">super</a>.</li>
47+
<li>Python Glossary: <a href="https://docs.python.org/3/glossary.html#term-method-resolution-order">Method resolution order</a>.</li>
5048

5149
</references>
5250
</qhelp>

python/ql/src/Classes/CallsToInitDel/SuperclassDelCalledMultipleTimes.qhelp

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,55 +4,52 @@
44
<qhelp>
55

66
<overview>
7-
<p>Python, unlike statically typed languages such as Java, allows complete freedom when calling methods during object destruction.
8-
However, standard object-oriented principles apply to Python classes using deep inheritance hierarchies.
9-
Therefore the developer has responsibility for ensuring that objects are properly cleaned up when
10-
there are multiple <code>__del__</code> methods that need to be called.
7+
<p>
8+
Python, unlike some other object-oriented languages such as Java, allows the developer complete freedom in
9+
when and how superclass finalizers are called during object finalization.
10+
However, the developer has responsibility for ensuring that objects are properly cleaned up.
1111
</p>
1212

1313
<p>
14-
Calling a <code>__del__</code> method more than once during object destruction risks resources being released multiple
15-
times. The relevant <code>__del__</code> method may not be designed to be called more than once.
14+
Objects with a <code>__del__</code> method (a finalizer) often hold resources such as file handles that need to be cleaned up.
15+
If a superclass finalizer is called multiple times, this may lead to errors such as closing an already closed file, and lead to objects not being
16+
cleaned up properly as expected.
1617
</p>
1718

1819
<p>There are a number of ways that a <code>__del__</code> method may be be called more than once.</p>
1920
<ul>
2021
<li>There may be more than one explicit call to the method in the hierarchy of <code>__del__</code> methods.</li>
21-
<li>A class using multiple inheritance directly calls the <code>__del__</code> methods of its base types.
22-
One or more of those base types uses <code>super()</code> to pass down the inheritance chain.</li>
22+
<li>In situations involving multiple inheritance, an finalization method may call the finalizers of each of its base types,
23+
which themselves both call the finalizer of a shared base type. (This is an example of the Diamond Inheritance problem)</li>
24+
<li>Another situation involving multiple inheritance arises when a subclass calls the <code>__del__</code> methods of each of its base classes,
25+
one of which calls <code>super().__del__</code>. This super call resolves to the next class in the Method Resolution Order (MRO) of the subclass,
26+
which may be another base class that already has its initializer explicitly called.</li>
2327
</ul>
2428

2529

2630
</overview>
2731
<recommendation>
28-
<p>Either be careful not to explicitly call a <code>__del__</code> method more than once, or
29-
use <code>super()</code> throughout the inheritance hierarchy.</p>
30-
31-
<p>Alternatively refactor one or more of the classes to use composition rather than inheritance.</p>
32+
<p>Ensure that each finalizer method is called exactly once during finalization.
33+
This can be ensured by calling <code>super().__del__</code> for each finalizer methid in the inheritance chain.
34+
</p>
3235

3336
</recommendation>
3437
<example>
35-
<p>In the first example, explicit calls to <code>__del__</code> are used, but <code>SportsCar</code> erroneously calls
36-
both <code>Vehicle.__del__</code> and <code>Car.__del__</code>.
37-
This can be fixed by removing the call to <code>Vehicle.__del__</code>, as shown in <code>FixedSportsCar</code>.
38-
</p>
3938

40-
<sample src="SuperclassDelCalledMultipleTimes.py" />
41-
42-
<p>In the second example, there is a mixture of explicit calls to <code>__del__</code> and calls using <code>super()</code>.
43-
To fix this example, <code>super()</code> should be used throughout.
39+
<p>In the following example, there is a mixture of explicit calls to <code>__del__</code> and calls using <code>super()</code>, resulting in <code>Vehicle.__del__</code>
40+
being called twice.
41+
<code>FixedSportsCar.__del__</code> fixes this by using <code>super()</code> consistently with the other delete methods.
4442
</p>
4543

4644
<sample src="SuperclassInitCalledMultipleTimes2.py" />
4745

4846
</example>
4947
<references>
50-
51-
<li>Python Tutorial: <a href="https://docs.python.org/2/tutorial/classes.html">Classes</a>.</li>
52-
<li>Python Standard Library: <a href="https://docs.python.org/2/library/functions.html#super">super</a>.</li>
53-
<li>Artima Developer: <a href="http://www.artima.com/weblogs/viewpost.jsp?thread=236275">Things to Know About Python Super</a>.</li>
54-
<li>Wikipedia: <a href="http://en.wikipedia.org/wiki/Composition_over_inheritance">Composition over inheritance</a>.</li>
55-
48+
49+
<li>Python Reference: <a href="https://docs.python.org/3/reference/datamodel.html#object.__del__">__del__</a>.</li>
50+
<li>Python Standard Library: <a href="https://docs.python.org/3/library/functions.html#super">super</a>.</li>
51+
<li>Python Glossary: <a href="https://docs.python.org/3/glossary.html#term-method-resolution-order">Method resolution order</a>.</li>
52+
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem">The Diamond Problem</a>.</li>
5653

5754
</references>
5855
</qhelp>

python/ql/src/Classes/CallsToInitDel/SuperclassDelCalledMultipleTimes.ql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ where
3636
(
3737
target1 != target2 and
3838
msg =
39-
"This deletion method calls $@ multiple times, via $@ and $@, resolving to $@ and $@ respectively."
39+
"This finalization method calls $@ multiple times, via $@ and $@, resolving to $@ and $@ respectively."
4040
or
4141
target1 = target2 and
4242
// The targets themselves are called multiple times (either is calledMulti, or something earlier in the MRO)
4343
// Mentioning them again would be redundant.
44-
msg = "This deletion method calls $@ multiple times, via $@ and $@."
44+
msg = "This finalization method calls $@ multiple times, via $@ and $@."
4545
)
4646
select meth, msg, calledMulti, calledMulti.getQualifiedName(), call1, "this call", call2,
4747
"this call", target1, target1.getQualifiedName(), target2, target2.getQualifiedName()

0 commit comments

Comments
 (0)