@@ -291,6 +291,21 @@ module API {
291291 not m .matches ( "%.%" )
292292 }
293293
294+ /**
295+ * Holds if an import of module `m` exists.
296+ *
297+ * This is determined without referring to `Node`,
298+ * allowing this predicate to be used in a negative
299+ * context when constructing new nodes.
300+ */
301+ predicate moduleImportExists ( string m ) {
302+ Impl:: isImported ( m ) and
303+ // restrict `moduleImport` so it will never give results for a dotted name. Note
304+ // that we cannot move this logic to the `MkModuleImport` construction, since we
305+ // need the intermediate API graph nodes for the prefixes in `import foo.bar.baz`.
306+ not m .matches ( "%.%" )
307+ }
308+
294309 /** Gets a node corresponding to the built-in with the given name, if any. */
295310 Node builtin ( string n ) { result = moduleImport ( "builtins" ) .getMember ( n ) }
296311
@@ -492,14 +507,38 @@ module API {
492507 *
493508 * Ignores relative imports, such as `from ..foo.bar import baz`.
494509 */
495- private predicate imports ( DataFlow:: Node imp , string name ) {
510+ private predicate imports ( DataFlow:: CfgNode imp , string name ) {
496511 exists ( PY:: ImportExprNode iexpr |
497- imp .asCfgNode ( ) = iexpr and
512+ imp .getNode ( ) = iexpr and
498513 not iexpr .getNode ( ) .isRelative ( ) and
499514 name = iexpr .getNode ( ) .getImportedModuleName ( )
500515 )
501516 }
502517
518+ /**
519+ * Holds is them module `name` is imported.
520+ *
521+ * This is determined syntactically.
522+ */
523+ cached
524+ predicate isImported ( string name ) {
525+ // Ignore the following module name for Python 2, as we alias `__builtin__` to `builtins` elsewhere
526+ ( name != "__builtin__" or PY:: major_version ( ) = 3 ) and
527+ (
528+ exists ( PY:: ImportExpr iexpr |
529+ not iexpr .isRelative ( ) and
530+ name = iexpr .getImportedModuleName ( )
531+ )
532+ or
533+ // When we `import foo.bar.baz` we want to create API graph nodes also for the prefixes
534+ // `foo` and `foo.bar`:
535+ name = any ( PY:: ImportExpr e | not e .isRelative ( ) ) .getAnImportedModuleName ( )
536+ )
537+ or
538+ // The `builtins` module should always be implicitly available
539+ name = "builtins"
540+ }
541+
503542 private import semmle.python.dataflow.new.internal.Builtins
504543 private import semmle.python.dataflow.new.internal.ImportStar
505544
@@ -518,7 +557,7 @@ module API {
518557 */
519558 private TApiNode potential_import_star_base ( PY:: Scope s ) {
520559 exists ( DataFlow:: Node n |
521- n .asCfgNode ( ) = ImportStar:: potentialImportStarBase ( s ) and
560+ n .( DataFlow :: CfgNode ) . getNode ( ) = ImportStar:: potentialImportStarBase ( s ) and
522561 use ( result , n )
523562 )
524563 }
@@ -540,17 +579,17 @@ module API {
540579 or
541580 // TODO: I had expected `DataFlow::AttrWrite` to contain the attribute writes from a dict, that's how JS works.
542581 exists ( PY:: Dict dict , PY:: KeyValuePair item |
543- dict = pred .asExpr ( ) and
582+ dict = pred .( DataFlow :: ExprNode ) . getNode ( ) . getNode ( ) and
544583 dict .getItem ( _) = item and
545584 lbl = Label:: member ( item .getKey ( ) .( PY:: StrConst ) .getS ( ) ) and
546- rhs .asExpr ( ) = item .getValue ( )
585+ rhs .( DataFlow :: ExprNode ) . getNode ( ) . getNode ( ) = item .getValue ( )
547586 )
548587 or
549- exists ( PY:: CallableExpr fn | fn = pred .asExpr ( ) |
588+ exists ( PY:: CallableExpr fn | fn = pred .( DataFlow :: ExprNode ) . getNode ( ) . getNode ( ) |
550589 not fn .getInnerScope ( ) .isAsync ( ) and
551590 lbl = Label:: return ( ) and
552591 exists ( PY:: Return ret |
553- rhs .asExpr ( ) = ret .getValue ( ) and
592+ rhs .( DataFlow :: ExprNode ) . getNode ( ) . getNode ( ) = ret .getValue ( ) and
554593 ret .getScope ( ) = fn .getInnerScope ( )
555594 )
556595 )
@@ -592,7 +631,8 @@ module API {
592631 // Subclassing a node
593632 lbl = Label:: subclass ( ) and
594633 exists ( DataFlow:: Node superclass | pred .flowsTo ( superclass ) |
595- ref .asExpr ( ) .( PY:: ClassExpr ) .getABase ( ) = superclass .asExpr ( )
634+ ref .( DataFlow:: ExprNode ) .getNode ( ) .getNode ( ) .( PY:: ClassExpr ) .getABase ( ) =
635+ superclass .( DataFlow:: ExprNode ) .getNode ( ) .getNode ( )
596636 )
597637 or
598638 // awaiting
@@ -604,26 +644,27 @@ module API {
604644 )
605645 or
606646 exists ( DataFlow:: Node def , PY:: CallableExpr fn |
607- rhs ( base , def ) and fn = trackDefNode ( def ) .asExpr ( )
647+ rhs ( base , def ) and fn = trackDefNode ( def ) .( DataFlow :: ExprNode ) . getNode ( ) . getNode ( )
608648 |
609649 exists ( int i , int offset |
610650 if exists ( PY:: Parameter p | p = fn .getInnerScope ( ) .getAnArg ( ) and p .isSelf ( ) )
611651 then offset = 1
612652 else offset = 0
613653 |
614654 lbl = Label:: parameter ( i - offset ) and
615- ref .asExpr ( ) = fn .getInnerScope ( ) .getArg ( i )
655+ ref .( DataFlow :: ExprNode ) . getNode ( ) . getNode ( ) = fn .getInnerScope ( ) .getArg ( i )
616656 )
617657 or
618658 exists ( string name , PY:: Parameter param |
619659 lbl = Label:: keywordParameter ( name ) and
620660 param = fn .getInnerScope ( ) .getArgByName ( name ) and
621661 not param .isSelf ( ) and
622- ref .asExpr ( ) = param
662+ ref .( DataFlow :: ExprNode ) . getNode ( ) . getNode ( ) = param
623663 )
624664 or
625665 lbl = Label:: selfParameter ( ) and
626- ref .asExpr ( ) = any ( PY:: Parameter p | p = fn .getInnerScope ( ) .getAnArg ( ) and p .isSelf ( ) )
666+ ref .( DataFlow:: ExprNode ) .getNode ( ) .getNode ( ) =
667+ any ( PY:: Parameter p | p = fn .getInnerScope ( ) .getAnArg ( ) and p .isSelf ( ) )
627668 )
628669 or
629670 // Built-ins, treated as members of the module `builtins`
@@ -635,7 +676,7 @@ module API {
635676 base = potential_import_star_base ( s ) and
636677 lbl =
637678 Label:: member ( any ( string name |
638- ImportStar:: namePossiblyDefinedInImportStar ( ref .asCfgNode ( ) , name , s )
679+ ImportStar:: namePossiblyDefinedInImportStar ( ref .( DataFlow :: CfgNode ) . getNode ( ) , name , s )
639680 ) )
640681 )
641682 }
@@ -901,7 +942,7 @@ module API {
901942 ApiLabel memberFromRef ( DataFlow:: AttrRef ref ) {
902943 result = member ( ref .getAttributeName ( ) )
903944 or
904- not exists ( ref .getAttributeName ( ) ) and
945+ ref .unknownAttribute ( ) and
905946 result = unknownMember ( )
906947 }
907948
0 commit comments