@@ -383,6 +383,17 @@ private predicate typeEquality(AstNode n1, TypePath prefix1, AstNode n2, TypePat
383383 prefix2 .isEmpty ( ) and
384384 s = getRangeType ( n1 )
385385 )
386+ or
387+ exists ( ClosureExpr ce , int index |
388+ n1 = ce and
389+ n2 = ce .getParam ( index ) .getPat ( ) and
390+ prefix1 = closureParameterPath ( ce .getNumberOfParams ( ) , index ) and
391+ prefix2 .isEmpty ( )
392+ )
393+ or
394+ n1 .( ClosureExpr ) .getBody ( ) = n2 and
395+ prefix1 = closureReturnPath ( ) and
396+ prefix2 .isEmpty ( )
386397}
387398
388399pragma [ nomagic]
@@ -1435,6 +1446,120 @@ private Type inferForLoopExprType(AstNode n, TypePath path) {
14351446 )
14361447}
14371448
1449+ /**
1450+ * An invoked expression, the target of a call that is either a local variable
1451+ * or a non-path expression. This means that the expression denotes a
1452+ * first-class function.
1453+ */
1454+ final private class InvokedClosureExpr extends Expr {
1455+ private CallExpr call ;
1456+
1457+ InvokedClosureExpr ( ) {
1458+ call .getFunction ( ) = this and
1459+ ( not this instanceof PathExpr or this = any ( Variable v ) .getAnAccess ( ) )
1460+ }
1461+
1462+ Type getTypeAt ( TypePath path ) { result = inferType ( this , path ) }
1463+
1464+ CallExpr getCall ( ) { result = call }
1465+ }
1466+
1467+ private module InvokedClosureSatisfiesConstraintInput implements
1468+ SatisfiesConstraintInputSig< InvokedClosureExpr >
1469+ {
1470+ predicate relevantConstraint ( InvokedClosureExpr term , Type constraint ) {
1471+ exists ( term ) and
1472+ constraint .( TraitType ) .getTrait ( ) instanceof FnOnceTrait
1473+ }
1474+ }
1475+
1476+ /** Gets the type of `ce` when viewed as an implementation of `FnOnce`. */
1477+ private Type invokedClosureFnTypeAt ( InvokedClosureExpr ce , TypePath path ) {
1478+ SatisfiesConstraint< InvokedClosureExpr , InvokedClosureSatisfiesConstraintInput > :: satisfiesConstraintType ( ce ,
1479+ _, path , result )
1480+ }
1481+
1482+ /** Gets the path to a closure's return type. */
1483+ private TypePath closureReturnPath ( ) {
1484+ result = TypePath:: singleton ( TDynTraitTypeParameter ( any ( FnOnceTrait t ) .getOutputType ( ) ) )
1485+ }
1486+
1487+ /** Gets the path to a closure with arity `arity`s `index`th parameter type. */
1488+ private TypePath closureParameterPath ( int arity , int index ) {
1489+ result =
1490+ TypePath:: cons ( TDynTraitTypeParameter ( any ( FnOnceTrait t ) .getTypeParam ( ) ) ,
1491+ TypePath:: singleton ( TTupleTypeParameter ( arity , index ) ) )
1492+ }
1493+
1494+ /** Gets the path to the return type of the `FnOnce` trait. */
1495+ private TypePath fnReturnPath ( ) {
1496+ result = TypePath:: singleton ( TAssociatedTypeTypeParameter ( any ( FnOnceTrait t ) .getOutputType ( ) ) )
1497+ }
1498+
1499+ /**
1500+ * Gets the path to the parameter type of the `FnOnce` trait with arity `arity`
1501+ * and index `index`.
1502+ */
1503+ private TypePath fnParameterPath ( int arity , int index ) {
1504+ result =
1505+ TypePath:: cons ( TTypeParamTypeParameter ( any ( FnOnceTrait t ) .getTypeParam ( ) ) ,
1506+ TypePath:: singleton ( TTupleTypeParameter ( arity , index ) ) )
1507+ }
1508+
1509+ pragma [ nomagic]
1510+ private Type inferDynamicCallExprType ( Expr n , TypePath path ) {
1511+ exists ( InvokedClosureExpr ce |
1512+ // Propagate the function's return type to the call expression
1513+ exists ( TypePath path0 | result = invokedClosureFnTypeAt ( ce , path0 ) |
1514+ n = ce .getCall ( ) and
1515+ path = path0 .stripPrefix ( fnReturnPath ( ) )
1516+ or
1517+ // Propagate the function's parameter type to the arguments
1518+ exists ( int index |
1519+ n = ce .getCall ( ) .getArgList ( ) .getArg ( index ) and
1520+ path = path0 .stripPrefix ( fnParameterPath ( ce .getCall ( ) .getNumberOfArgs ( ) , index ) )
1521+ )
1522+ )
1523+ or
1524+ // _If_ the invoked expression has the type of a closure, then we propagate
1525+ // the surrounding types into the closure.
1526+ exists ( int arity , TypePath path0 |
1527+ ce .getTypeAt ( TypePath:: nil ( ) ) .( DynTraitType ) .getTrait ( ) instanceof FnOnceTrait
1528+ |
1529+ // Propagate the type of arguments to the parameter types of closure
1530+ exists ( int index |
1531+ n = ce and
1532+ arity = ce .getCall ( ) .getNumberOfArgs ( ) and
1533+ result = inferType ( ce .getCall ( ) .getArg ( index ) , path0 ) and
1534+ path = closureParameterPath ( arity , index ) .append ( path0 )
1535+ )
1536+ or
1537+ // Propagate the type of the call expression to the return type of the closure
1538+ n = ce and
1539+ arity = ce .getCall ( ) .getNumberOfArgs ( ) and
1540+ result = inferType ( ce .getCall ( ) , path0 ) and
1541+ path = closureReturnPath ( ) .append ( path0 )
1542+ )
1543+ )
1544+ }
1545+
1546+ pragma [ nomagic]
1547+ private Type inferClosureExprType ( AstNode n , TypePath path ) {
1548+ exists ( ClosureExpr ce |
1549+ n = ce and
1550+ path .isEmpty ( ) and
1551+ result = TDynTraitType ( any ( FnOnceTrait t ) )
1552+ or
1553+ n = ce and
1554+ path = TypePath:: singleton ( TDynTraitTypeParameter ( any ( FnOnceTrait t ) .getTypeParam ( ) ) ) and
1555+ result = TTuple ( ce .getNumberOfParams ( ) )
1556+ or
1557+ // Propagate return type annotation to body
1558+ n = ce .getBody ( ) and
1559+ result = ce .getRetType ( ) .getTypeRepr ( ) .( TypeMention ) .resolveTypeAt ( path )
1560+ )
1561+ }
1562+
14381563pragma [ nomagic]
14391564private Type inferCastExprType ( CastExpr ce , TypePath path ) {
14401565 result = ce .getTypeRepr ( ) .( TypeMention ) .resolveTypeAt ( path )
@@ -2062,6 +2187,10 @@ private module Cached {
20622187 or
20632188 result = inferForLoopExprType ( n , path )
20642189 or
2190+ result = inferDynamicCallExprType ( n , path )
2191+ or
2192+ result = inferClosureExprType ( n , path )
2193+ or
20652194 result = inferCastExprType ( n , path )
20662195 or
20672196 result = inferStructPatType ( n , path )
0 commit comments