@@ -389,6 +389,17 @@ private predicate typeEquality(AstNode n1, TypePath prefix1, AstNode n2, TypePat
389389 prefix2.isEmpty() and
390390 s = getRangeType(n1)
391391 )
392+ or
393+ exists(ClosureExpr ce, int index |
394+ n1 = ce and
395+ n2 = ce.getParam(index).getPat() and
396+ prefix1 = closureParameterPath(ce.getNumberOfParams(), index) and
397+ prefix2.isEmpty()
398+ )
399+ or
400+ n1.(ClosureExpr).getBody() = n2 and
401+ prefix1 = closureReturnPath() and
402+ prefix2.isEmpty()
392403}
393404
394405pragma[nomagic]
@@ -1441,6 +1452,120 @@ private Type inferForLoopExprType(AstNode n, TypePath path) {
14411452 )
14421453}
14431454
1455+ /**
1456+ * An invoked expression, the target of a call that is either a local variable
1457+ * or a non-path expression. This means that the expression denotes a
1458+ * first-class function.
1459+ */
1460+ final private class InvokedClosureExpr extends Expr {
1461+ private CallExpr call;
1462+
1463+ InvokedClosureExpr() {
1464+ call.getFunction() = this and
1465+ (not this instanceof PathExpr or this = any(Variable v).getAnAccess())
1466+ }
1467+
1468+ Type getTypeAt(TypePath path) { result = inferType(this, path) }
1469+
1470+ CallExpr getCall() { result = call }
1471+ }
1472+
1473+ private module InvokedClosureSatisfiesConstraintInput implements
1474+ SatisfiesConstraintInputSig<InvokedClosureExpr>
1475+ {
1476+ predicate relevantConstraint(InvokedClosureExpr term, Type constraint) {
1477+ exists(term) and
1478+ constraint.(TraitType).getTrait() instanceof FnOnceTrait
1479+ }
1480+ }
1481+
1482+ /** Gets the type of `ce` when viewed as an implementation of `FnOnce`. */
1483+ private Type invokedClosureFnTypeAt(InvokedClosureExpr ce, TypePath path) {
1484+ SatisfiesConstraint<InvokedClosureExpr, InvokedClosureSatisfiesConstraintInput>::satisfiesConstraintType(ce,
1485+ _, path, result)
1486+ }
1487+
1488+ /** Gets the path to a closure's return type. */
1489+ private TypePath closureReturnPath() {
1490+ result = TypePath::singleton(TDynTraitTypeParameter(any(FnOnceTrait t).getOutputType()))
1491+ }
1492+
1493+ /** Gets the path to a closure with arity `arity`s `index`th parameter type. */
1494+ private TypePath closureParameterPath(int arity, int index) {
1495+ result =
1496+ TypePath::cons(TDynTraitTypeParameter(any(FnOnceTrait t).getTypeParam()),
1497+ TypePath::singleton(TTupleTypeParameter(arity, index)))
1498+ }
1499+
1500+ /** Gets the path to the return type of the `FnOnce` trait. */
1501+ private TypePath fnReturnPath() {
1502+ result = TypePath::singleton(TAssociatedTypeTypeParameter(any(FnOnceTrait t).getOutputType()))
1503+ }
1504+
1505+ /**
1506+ * Gets the path to the parameter type of the `FnOnce` trait with arity `arity`
1507+ * and index `index`.
1508+ */
1509+ private TypePath fnParameterPath(int arity, int index) {
1510+ result =
1511+ TypePath::cons(TTypeParamTypeParameter(any(FnOnceTrait t).getTypeParam()),
1512+ TypePath::singleton(TTupleTypeParameter(arity, index)))
1513+ }
1514+
1515+ pragma[nomagic]
1516+ private Type inferDynamicCallExprType(Expr n, TypePath path) {
1517+ exists(InvokedClosureExpr ce |
1518+ // Propagate the function's return type to the call expression
1519+ exists(TypePath path0 | result = invokedClosureFnTypeAt(ce, path0) |
1520+ n = ce.getCall() and
1521+ path = path0.stripPrefix(fnReturnPath())
1522+ or
1523+ // Propagate the function's parameter type to the arguments
1524+ exists(int index |
1525+ n = ce.getCall().getArgList().getArg(index) and
1526+ path = path0.stripPrefix(fnParameterPath(ce.getCall().getNumberOfArgs(), index))
1527+ )
1528+ )
1529+ or
1530+ // _If_ the invoked expression has the type of a closure, then we propagate
1531+ // the surrounding types into the closure.
1532+ exists(int arity, TypePath path0 |
1533+ ce.getTypeAt(TypePath::nil()).(DynTraitType).getTrait() instanceof FnOnceTrait
1534+ |
1535+ // Propagate the type of arguments to the parameter types of closure
1536+ exists(int index |
1537+ n = ce and
1538+ arity = ce.getCall().getNumberOfArgs() and
1539+ result = inferType(ce.getCall().getArg(index), path0) and
1540+ path = closureParameterPath(arity, index).append(path0)
1541+ )
1542+ or
1543+ // Propagate the type of the call expression to the return type of the closure
1544+ n = ce and
1545+ arity = ce.getCall().getNumberOfArgs() and
1546+ result = inferType(ce.getCall(), path0) and
1547+ path = closureReturnPath().append(path0)
1548+ )
1549+ )
1550+ }
1551+
1552+ pragma[nomagic]
1553+ private Type inferClosureExprType(AstNode n, TypePath path) {
1554+ exists(ClosureExpr ce |
1555+ n = ce and
1556+ path.isEmpty() and
1557+ result = TDynTraitType(any(FnOnceTrait t))
1558+ or
1559+ n = ce and
1560+ path = TypePath::singleton(TDynTraitTypeParameter(any(FnOnceTrait t).getTypeParam())) and
1561+ result = TTuple(ce.getNumberOfParams())
1562+ or
1563+ // Propagate return type annotation to body
1564+ n = ce.getBody() and
1565+ result = ce.getRetType().getTypeRepr().(TypeMention).resolveTypeAt(path)
1566+ )
1567+ }
1568+
14441569pragma[nomagic]
14451570private Type inferCastExprType(CastExpr ce, TypePath path) {
14461571 result = ce.getTypeRepr().(TypeMention).resolveTypeAt(path)
@@ -2068,6 +2193,10 @@ private module Cached {
20682193 or
20692194 result = inferForLoopExprType(n, path)
20702195 or
2196+ result = inferDynamicCallExprType(n, path)
2197+ or
2198+ result = inferClosureExprType(n, path)
2199+ or
20712200 result = inferCastExprType(n, path)
20722201 or
20732202 result = inferStructPatType(n, path)
0 commit comments