Extract getFreeParameters method from `TypeSystemImpl.instantiateTypeFormalsToBounds`.

I'm going to need to use this logic for
https://github.com/dart-lang/language/issues/731 (improved inference
for fold etc.)

Change-Id: I9232e42ccfd29b028fb393af06541a9a51f57579
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/239161
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
diff --git a/pkg/analyzer/lib/src/dart/element/type_system.dart b/pkg/analyzer/lib/src/dart/element/type_system.dart
index e782c15..d420c963 100644
--- a/pkg/analyzer/lib/src/dart/element/type_system.dart
+++ b/pkg/analyzer/lib/src/dart/element/type_system.dart
@@ -347,6 +347,52 @@
     return null;
   }
 
+  /// Computes the set of free type parameters appearing in [rootType].
+  ///
+  /// If a non-null [candidates] set is given, then only type parameters
+  /// appearing in it are considered; otherwise all type parameters are
+  /// considered.
+  List<TypeParameterElement>? getFreeParameters(DartType rootType,
+      {Set<TypeParameterElement>? candidates}) {
+    List<TypeParameterElement>? parameters;
+    Set<DartType> visitedTypes = HashSet<DartType>();
+    Set<TypeParameterElement> boundTypeParameters =
+        HashSet<TypeParameterElement>();
+
+    void appendParameters(DartType? type) {
+      if (type == null) {
+        return;
+      }
+      if (visitedTypes.contains(type)) {
+        return;
+      }
+      visitedTypes.add(type);
+      if (type is TypeParameterType) {
+        var element = type.element;
+        if ((candidates == null || candidates.contains(element)) &&
+            !boundTypeParameters.contains(element)) {
+          parameters ??= <TypeParameterElement>[];
+          parameters!.add(element);
+        }
+      } else {
+        if (type is FunctionType) {
+          assert(!type.typeFormals.any((t) => boundTypeParameters.contains(t)));
+          boundTypeParameters.addAll(type.typeFormals);
+          appendParameters(type.returnType);
+          type.parameters.map((p) => p.type).forEach(appendParameters);
+          // TODO(scheglov) https://github.com/dart-lang/sdk/issues/44218
+          type.alias?.typeArguments.forEach(appendParameters);
+          boundTypeParameters.removeAll(type.typeFormals);
+        } else if (type is InterfaceType) {
+          type.typeArguments.forEach(appendParameters);
+        }
+      }
+    }
+
+    appendParameters(rootType);
+    return parameters;
+  }
+
   /// Computes the greatest lower bound of [T1] and [T2].
   DartType getGreatestLowerBound(DartType T1, DartType T2) {
     return _greatestLowerBoundHelper.getGreatestLowerBound(T1, T2);
@@ -541,46 +587,12 @@
       }
     }
 
-    List<TypeParameterElement>? getFreeParameters(DartType rootType) {
-      List<TypeParameterElement>? parameters;
-      Set<DartType> visitedTypes = HashSet<DartType>();
-
-      void appendParameters(DartType? type) {
-        if (type == null) {
-          return;
-        }
-        if (visitedTypes.contains(type)) {
-          return;
-        }
-        visitedTypes.add(type);
-        if (type is TypeParameterType) {
-          var element = type.element;
-          if (all.contains(element)) {
-            parameters ??= <TypeParameterElement>[];
-            parameters!.add(element);
-          }
-        } else {
-          if (type is FunctionType) {
-            appendParameters(type.returnType);
-            type.parameters.map((p) => p.type).forEach(appendParameters);
-            // TODO(scheglov) https://github.com/dart-lang/sdk/issues/44218
-            type.alias?.typeArguments.forEach(appendParameters);
-          } else if (type is InterfaceType) {
-            type.typeArguments.forEach(appendParameters);
-          }
-        }
-      }
-
-      appendParameters(rootType);
-      return parameters;
-    }
-
     bool hasProgress = true;
     while (hasProgress) {
       hasProgress = false;
       for (TypeParameterElement parameter in partials.keys) {
         DartType value = partials[parameter]!;
-        var freeParameters = getFreeParameters(value);
+        var freeParameters = getFreeParameters(value, candidates: all);
         if (freeParameters == null) {
           defaults[parameter] = value;
           partials.remove(parameter);