@@ -78,34 +78,63 @@ module ImportResolution {
7878 }
7979
8080 /**
81- * Holds if the module `m` defines a name `name` by assigning `defn` to it. This is an
82- * overapproximation, as `name` may not in fact be exported (e.g. by defining an `__all__` that does
83- * not include `name`).
81+ * Holds if the module `m` defines a name `name` with the value `val`. The value
82+ * represents the value `name` will have at the end of the module (the last place we
83+ * have def-use flow to).
84+ *
85+ * Note: The handling of re-exporting imports is a bit simplistic. We assume that if
86+ * an import is made, it will be re-exported (which will not be the case if a new
87+ * value is assigned to the name, or it is deleted).
8488 */
8589 pragma [ nomagic]
86- predicate module_export ( Module m , string name , DataFlow:: CfgNode defn ) {
87- exists ( EssaVariable v , EssaDefinition essaDef |
88- v .getName ( ) = name and
89- v .getAUse ( ) = m .getANormalExit ( ) and
90- allowedEssaImportStep * ( essaDef , v .getDefinition ( ) )
90+ predicate module_export ( Module m , string name , DataFlow:: Node val ) {
91+ // Definitions made inside `m` itself
92+ //
93+ // for code such as `foo = ...; foo.bar = ...` there will be TWO
94+ // EssaDefinition/EssaVariable. One for `foo = ...` (AssignmentDefinition) and one
95+ // for `foo.bar = ...`. The one for `foo.bar = ...` (EssaNodeRefinement). The
96+ // EssaNodeRefinement is the one that will reach the end of the module (normal
97+ // exit).
98+ //
99+ // However, we cannot just use the EssaNodeRefinement as the `val`, because the
100+ // normal data-flow depends on use-use flow, and use-use flow targets CFG nodes not
101+ // EssaNodes. So we need to go back from the EssaDefinition/EssaVariable that
102+ // reaches the end of the module, to the first definition of the variable, and then
103+ // track forwards using use-use flow to find a suitable CFG node that has flow into
104+ // it from use-use flow.
105+ exists ( EssaVariable lastUseVar , EssaVariable firstDef |
106+ lastUseVar .getName ( ) = name and
107+ // we ignore special variable $ introduced by our analysis (not used for anything)
108+ // we ignore special variable * introduced by `from <pkg> import *` -- TODO: understand why we even have this?
109+ not name in [ "$" , "*" ] and
110+ lastUseVar .getAUse ( ) = m .getANormalExit ( ) and
111+ allowedEssaImportStep * ( firstDef , lastUseVar ) and
112+ not allowedEssaImportStep ( _, firstDef )
91113 |
92- defn .getNode ( ) = essaDef .( AssignmentDefinition ) .getValue ( )
114+ not EssaFlow:: defToFirstUse ( firstDef , _) and
115+ val .asVar ( ) = firstDef
93116 or
94- defn .getNode ( ) = essaDef .( ArgumentRefinement ) .getArgument ( )
117+ exists ( ControlFlowNode mid , ControlFlowNode end |
118+ EssaFlow:: defToFirstUse ( firstDef , mid ) and
119+ EssaFlow:: useToNextUse * ( mid , end ) and
120+ not EssaFlow:: useToNextUse ( end , _) and
121+ lastUseVar .getAUse ( ) = end and
122+ val .asCfgNode ( ) = end
123+ )
95124 )
96125 or
97- // `from <pkg> import *`
126+ // re-exports from `from <pkg> import *`
98127 exists ( Module importedFrom |
99128 importedFrom = ImportStar:: getStarImported ( m ) and
100- module_export ( importedFrom , name , defn ) and
129+ module_export ( importedFrom , name , val ) and
101130 potential_module_export ( importedFrom , name )
102131 )
103132 or
104- // `import <pkg>` or `from <pkg> import <stuff>`
133+ // re-exports from `import <pkg>` or `from <pkg> import <stuff>`
105134 exists ( Alias a |
106- defn .asExpr ( ) = a .getValue ( ) and
135+ val .asExpr ( ) = a .getValue ( ) and
107136 a .getAsname ( ) .( Name ) .getId ( ) = name and
108- defn .getScope ( ) = m
137+ val .getScope ( ) = m
109138 )
110139 }
111140
0 commit comments