@@ -380,6 +380,21 @@ module API {
380380 not m .matches ( "%.%" )
381381 }
382382
383+ /**
384+ * Holds if an import of module `m` exists.
385+ *
386+ * This is determined without referring to `Node`,
387+ * allowing this predicate to be used in a negative
388+ * context when constructing new nodes.
389+ */
390+ predicate moduleImportExists ( string m ) {
391+ Impl:: isImported ( m ) and
392+ // restrict `moduleImport` so it will never give results for a dotted name. Note
393+ // that we cannot move this logic to the `MkModuleImport` construction, since we
394+ // need the intermediate API graph nodes for the prefixes in `import foo.bar.baz`.
395+ not m .matches ( "%.%" )
396+ }
397+
383398 /** Gets a node corresponding to the built-in with the given name, if any. */
384399 Node builtin ( string n ) { result = moduleImport ( "builtins" ) .getMember ( n ) }
385400
@@ -605,14 +620,38 @@ module API {
605620 *
606621 * Ignores relative imports, such as `from ..foo.bar import baz`.
607622 */
608- private predicate imports ( DataFlow:: Node imp , string name ) {
623+ private predicate imports ( DataFlow:: CfgNode imp , string name ) {
609624 exists ( PY:: ImportExprNode iexpr |
610- imp .asCfgNode ( ) = iexpr and
625+ imp .getNode ( ) = iexpr and
611626 not iexpr .getNode ( ) .isRelative ( ) and
612627 name = iexpr .getNode ( ) .getImportedModuleName ( )
613628 )
614629 }
615630
631+ /**
632+ * Holds if the module `name` is imported.
633+ *
634+ * This is determined syntactically.
635+ */
636+ cached
637+ predicate isImported ( string name ) {
638+ // Ignore the following module name for Python 2, as we alias `__builtin__` to `builtins` elsewhere
639+ ( name != "__builtin__" or PY:: major_version ( ) = 3 ) and
640+ (
641+ exists ( PY:: ImportExpr iexpr |
642+ not iexpr .isRelative ( ) and
643+ name = iexpr .getImportedModuleName ( )
644+ )
645+ or
646+ // When we `import foo.bar.baz` we want to create API graph nodes also for the prefixes
647+ // `foo` and `foo.bar`:
648+ name = any ( PY:: ImportExpr e | not e .isRelative ( ) ) .getAnImportedModuleName ( )
649+ )
650+ or
651+ // The `builtins` module should always be implicitly available
652+ name = "builtins"
653+ }
654+
616655 private import semmle.python.dataflow.new.internal.Builtins
617656 private import semmle.python.dataflow.new.internal.ImportStar
618657
@@ -631,7 +670,7 @@ module API {
631670 */
632671 private TApiNode potential_import_star_base ( PY:: Scope s ) {
633672 exists ( DataFlow:: Node n |
634- n .asCfgNode ( ) = ImportStar:: potentialImportStarBase ( s ) and
673+ n .( DataFlow :: CfgNode ) . getNode ( ) = ImportStar:: potentialImportStarBase ( s ) and
635674 use ( result , n )
636675 )
637676 }
@@ -653,17 +692,17 @@ module API {
653692 or
654693 // TODO: I had expected `DataFlow::AttrWrite` to contain the attribute writes from a dict, that's how JS works.
655694 exists ( PY:: Dict dict , PY:: KeyValuePair item |
656- dict = pred .asExpr ( ) and
695+ dict = pred .( DataFlow :: ExprNode ) . getNode ( ) . getNode ( ) and
657696 dict .getItem ( _) = item and
658697 lbl = Label:: member ( item .getKey ( ) .( PY:: StrConst ) .getS ( ) ) and
659- rhs .asExpr ( ) = item .getValue ( )
698+ rhs .( DataFlow :: ExprNode ) . getNode ( ) . getNode ( ) = item .getValue ( )
660699 )
661700 or
662- exists ( PY:: CallableExpr fn | fn = pred .asExpr ( ) |
701+ exists ( PY:: CallableExpr fn | fn = pred .( DataFlow :: ExprNode ) . getNode ( ) . getNode ( ) |
663702 not fn .getInnerScope ( ) .isAsync ( ) and
664703 lbl = Label:: return ( ) and
665704 exists ( PY:: Return ret |
666- rhs .asExpr ( ) = ret .getValue ( ) and
705+ rhs .( DataFlow :: ExprNode ) . getNode ( ) . getNode ( ) = ret .getValue ( ) and
667706 ret .getScope ( ) = fn .getInnerScope ( )
668707 )
669708 )
@@ -716,9 +755,9 @@ module API {
716755 // "benign" and let subclasses edges flow through anyway.
717756 // see example in https://github.com/django/django/blob/c2250cfb80e27cdf8d098428824da2800a18cadf/tests/auth_tests/test_views.py#L40-L46
718757 (
719- ref .asExpr ( ) = clsExpr
758+ ref .( DataFlow :: ExprNode ) . getNode ( ) . getNode ( ) = clsExpr
720759 or
721- ref .asExpr ( ) = clsExpr .getADecoratorCall ( )
760+ ref .( DataFlow :: ExprNode ) . getNode ( ) . getNode ( ) = clsExpr .getADecoratorCall ( )
722761 )
723762 )
724763 or
@@ -731,26 +770,27 @@ module API {
731770 )
732771 or
733772 exists ( DataFlow:: Node def , PY:: CallableExpr fn |
734- rhs ( base , def ) and fn = trackDefNode ( def ) .asExpr ( )
773+ rhs ( base , def ) and fn = trackDefNode ( def ) .( DataFlow :: ExprNode ) . getNode ( ) . getNode ( )
735774 |
736775 exists ( int i , int offset |
737776 if exists ( PY:: Parameter p | p = fn .getInnerScope ( ) .getAnArg ( ) and p .isSelf ( ) )
738777 then offset = 1
739778 else offset = 0
740779 |
741780 lbl = Label:: parameter ( i - offset ) and
742- ref .asExpr ( ) = fn .getInnerScope ( ) .getArg ( i )
781+ ref .( DataFlow :: ExprNode ) . getNode ( ) . getNode ( ) = fn .getInnerScope ( ) .getArg ( i )
743782 )
744783 or
745784 exists ( string name , PY:: Parameter param |
746785 lbl = Label:: keywordParameter ( name ) and
747786 param = fn .getInnerScope ( ) .getArgByName ( name ) and
748787 not param .isSelf ( ) and
749- ref .asExpr ( ) = param
788+ ref .( DataFlow :: ExprNode ) . getNode ( ) . getNode ( ) = param
750789 )
751790 or
752791 lbl = Label:: selfParameter ( ) and
753- ref .asExpr ( ) = any ( PY:: Parameter p | p = fn .getInnerScope ( ) .getAnArg ( ) and p .isSelf ( ) )
792+ ref .( DataFlow:: ExprNode ) .getNode ( ) .getNode ( ) =
793+ any ( PY:: Parameter p | p = fn .getInnerScope ( ) .getAnArg ( ) and p .isSelf ( ) )
754794 )
755795 or
756796 // Built-ins, treated as members of the module `builtins`
@@ -762,7 +802,7 @@ module API {
762802 base = potential_import_star_base ( s ) and
763803 lbl =
764804 Label:: member ( any ( string name |
765- ImportStar:: namePossiblyDefinedInImportStar ( ref .asCfgNode ( ) , name , s )
805+ ImportStar:: namePossiblyDefinedInImportStar ( ref .( DataFlow :: CfgNode ) . getNode ( ) , name , s )
766806 ) )
767807 )
768808 or
@@ -854,7 +894,7 @@ module API {
854894 DataFlow:: LocalSourceNode trackUseNode ( DataFlow:: LocalSourceNode src ) {
855895 Stages:: TypeTracking:: ref ( ) and
856896 result = trackUseNode ( src , DataFlow:: TypeTracker:: end ( ) ) and
857- not result instanceof DataFlow:: ModuleVariableNode
897+ result instanceof DataFlow:: ExprNode
858898 }
859899
860900 /**
@@ -1044,7 +1084,7 @@ module API {
10441084 ApiLabel memberFromRef ( DataFlow:: AttrRef ref ) {
10451085 result = member ( ref .getAttributeName ( ) )
10461086 or
1047- not exists ( ref .getAttributeName ( ) ) and
1087+ ref .unknownAttribute ( ) and
10481088 result = unknownMember ( )
10491089 }
10501090
0 commit comments