docs: protected List<Type> actualTypes(Inliner inliner) {
      ArrayList<Type> result = new ArrayList<>();
      ImmutableList<String> argNames = expressionArgumentTypes().keySet().asList();
      for (int i = 0; i < expressionArgumentTypes().size(); i++) {
        String argName = argNames.get(i);
        Optional<JCExpression> singleBinding =
            inliner.getOptionalBinding(new UFreeIdent.Key(argName));
        if (singleBinding.isPresent()) {
          result.add(singleBinding.get().type);
        } else {
          Optional<java.util.List<JCExpression>> exprs =
              inliner.getOptionalBinding(new URepeated.Key(argName));
          if (exprs.isPresent() && !exprs.get().isEmpty()) {
            Type[] exprTys = new Type[exprs.get().size()];
            for (int j = 0; j < exprs.get().size(); j++) {
              exprTys[j] = exprs.get().get(j).type;
            }
            // Get the least upper bound of the types of all expressions that the argument matches.
            // In the special case where exprs is empty, returns the "bottom" type, which is a
            // subtype of everything.
            result.add(inliner.types().lub(List.from(exprTys)));
          }
        }
      }
      for (PlaceholderExpressionKey key :
          Ordering.natural()
              .immutableSortedCopy(
                  Iterables.filter(inliner.bindings.keySet(), PlaceholderExpressionKey.class))) {
        result.add(inliner.getBinding(key).type);
      }
      return List.from(result);
    }