1515import python
1616private import semmle.python.dataflow.new.DataFlow
1717private import semmle.python.dataflow.new.internal.DataFlowDispatch
18+ private import semmle.python.dataflow.new.internal.ReExposedInstance
1819
1920predicate calls_close ( Call c ) { exists ( Attribute a | c .getFunc ( ) = a and a .getName ( ) = "close" ) }
2021
@@ -24,26 +25,10 @@ predicate only_stmt_in_finally(Try t, Call c) {
2425 )
2526}
2627
27- /**
28- * Holds if `read` is an attribute read that re-exposes an instance of `cls` held in an
29- * instance attribute, for example `BufferedRWPair.reader`.
30- *
31- * Instance-attribute type tracking can launder such an instance out of a field. The object
32- * is owned by the enclosing instance, so its lifetime spans that instance and cannot be
33- * expressed with a `with` statement; closing it in a `finally` block is therefore not a
34- * candidate for refactoring.
35- */
36- private predicate launderedAttrRead ( Class cls , DataFlow:: AttrRead read ) {
37- read = classInstanceTracker ( cls )
38- }
28+ /** Holds if `node` is tracked to be an instance of some class. */
29+ private predicate classInstanceNode ( DataFlow:: Node node ) { node = classInstanceTracker ( _) }
3930
40- /** Type tracking forward from an attribute read that re-exposes an instance held in a field. */
41- private DataFlow:: TypeTrackingNode launderedInstance ( Class cls , DataFlow:: TypeTracker t ) {
42- t .start ( ) and
43- launderedAttrRead ( cls , result )
44- or
45- exists ( DataFlow:: TypeTracker t2 | result = launderedInstance ( cls , t2 ) .track ( t2 , t ) )
46- }
31+ private module ClassReExposed = ReExposedInstance< classInstanceNode / 1 > ;
4732
4833from Call close , Try t , Class cls , DataFlow:: Node closeTarget
4934where
5439 // Don't report closing a resource that is held in an instance attribute (e.g. `self.reader`).
5540 // Such flow is introduced by instance-attribute type tracking; the object's lifetime is tied
5641 // to the enclosing instance and cannot be expressed with a `with` statement.
57- not launderedInstance ( cls , DataFlow :: TypeTracker :: end ( ) ) . flowsTo ( closeTarget ) and
42+ not ClassReExposed :: isReExposed ( closeTarget ) and
5843 DuckTyping:: isContextManager ( cls )
5944select close ,
6045 "Instance of context-manager class $@ is closed in a finally block. Consider using 'with' statement." ,
0 commit comments