blob: a45b7392fa939862720484b4b4e7b51782799c56 [file] [log] [blame]
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE.md file.
import 'package:kernel/ast.dart' show TypeParameter;
import 'package:kernel/type_algebra.dart' show containsTypeVariable;
import 'package:kernel/util/graph.dart' show Graph, computeStrongComponents;
import 'kernel_builder.dart'
show
ClassBuilder,
FormalParameterBuilder,
FunctionTypeAliasBuilder,
FunctionTypeBuilder,
KernelClassBuilder,
KernelFormalParameterBuilder,
KernelFunctionTypeBuilder,
KernelNamedTypeBuilder,
KernelTypeBuilder,
KernelTypeVariableBuilder,
NamedTypeBuilder,
TypeBuilder,
TypeDeclarationBuilder,
TypeVariableBuilder;
import '../dill/dill_class_builder.dart' show DillClassBuilder;
import '../dill/dill_typedef_builder.dart' show DillFunctionTypeAliasBuilder;
import '../fasta_codes.dart'
show
LocatedMessage,
templateBoundIssueViaCycleNonSimplicity,
templateBoundIssueViaLoopNonSimplicity,
templateBoundIssueViaRawTypeWithNonSimpleBounds,
templateNonSimpleBoundViaReference,
templateNonSimpleBoundViaVariable;
KernelTypeBuilder substituteRange(
KernelTypeBuilder type,
Map<TypeVariableBuilder, KernelTypeBuilder> upperSubstitution,
Map<TypeVariableBuilder, KernelTypeBuilder> lowerSubstitution,
{bool isCovariant = true}) {
if (type is KernelNamedTypeBuilder) {
if (type.declaration is KernelTypeVariableBuilder) {
if (isCovariant) {
return upperSubstitution[type.declaration] ?? type;
}
return lowerSubstitution[type.declaration] ?? type;
}
if (type.arguments == null || type.arguments.length == 0) {
return type;
}
List<TypeBuilder> arguments;
for (int i = 0; i < type.arguments.length; i++) {
TypeBuilder substitutedArgument = substituteRange(
type.arguments[i], upperSubstitution, lowerSubstitution,
isCovariant: isCovariant);
if (substitutedArgument != type.arguments[i]) {
arguments ??= type.arguments.toList();
arguments[i] = substitutedArgument;
}
}
if (arguments != null) {
return new KernelNamedTypeBuilder(type.name, arguments)
..bind(type.declaration);
}
return type;
}
if (type is KernelFunctionTypeBuilder) {
List<KernelTypeVariableBuilder> variables;
if (type.typeVariables != null) {
variables =
new List<KernelTypeVariableBuilder>(type.typeVariables.length);
}
List<KernelFormalParameterBuilder> formals;
if (type.formals != null) {
formals = new List<KernelFormalParameterBuilder>(type.formals.length);
}
KernelTypeBuilder returnType;
bool changed = false;
if (type.typeVariables != null) {
for (int i = 0; i < variables.length; i++) {
KernelTypeVariableBuilder variable = type.typeVariables[i];
KernelTypeBuilder bound = substituteRange(
variable.bound, upperSubstitution, lowerSubstitution,
isCovariant: isCovariant);
if (bound != variable.bound) {
variables[i] = new KernelTypeVariableBuilder(
variable.name, variable.parent, variable.charOffset, bound);
changed = true;
} else {
variables[i] = variable;
}
}
}
if (type.formals != null) {
for (int i = 0; i < formals.length; i++) {
KernelFormalParameterBuilder formal = type.formals[i];
KernelTypeBuilder parameterType = substituteRange(
formal.type, upperSubstitution, lowerSubstitution,
isCovariant: !isCovariant);
if (parameterType != formal.type) {
formals[i] = new KernelFormalParameterBuilder(
formal.metadata,
formal.modifiers,
parameterType,
formal.name,
formal.hasThis,
formal.parent,
formal.charOffset);
changed = true;
} else {
formals[i] = formal;
}
}
}
returnType = substituteRange(
type.returnType, upperSubstitution, lowerSubstitution,
isCovariant: true);
if (returnType != type.returnType) {
changed = true;
}
if (changed) {
return new KernelFunctionTypeBuilder(returnType, variables, formals);
}
return type;
}
return type;
}
KernelTypeBuilder substitute(KernelTypeBuilder type,
Map<TypeVariableBuilder, KernelTypeBuilder> substitution) {
return substituteRange(type, substitution, substitution, isCovariant: true);
}
/// Calculates bounds to be provided as type arguments in place of missing type
/// arguments on raw types with the given type parameters.
///
/// See the [description]
/// (https://github.com/dart-lang/sdk/blob/master/docs/language/informal/instantiate-to-bound.md)
/// of the algorithm for details.
List<KernelTypeBuilder> calculateBounds(
List<TypeVariableBuilder> variables,
KernelTypeBuilder dynamicType,
KernelTypeBuilder bottomType,
KernelClassBuilder objectClass) {
List<KernelTypeBuilder> bounds =
new List<KernelTypeBuilder>(variables.length);
for (int i = 0; i < variables.length; i++) {
bounds[i] = variables[i].bound ?? dynamicType;
}
TypeVariablesGraph graph = new TypeVariablesGraph(variables, bounds);
List<List<int>> stronglyConnected = computeStrongComponents(graph);
for (List<int> component in stronglyConnected) {
Map<TypeVariableBuilder, KernelTypeBuilder> dynamicSubstitution =
<TypeVariableBuilder, KernelTypeBuilder>{};
Map<TypeVariableBuilder, KernelTypeBuilder> nullSubstitution =
<TypeVariableBuilder, KernelTypeBuilder>{};
for (int variableIndex in component) {
dynamicSubstitution[variables[variableIndex]] = dynamicType;
nullSubstitution[variables[variableIndex]] = bottomType;
}
for (int variableIndex in component) {
bounds[variableIndex] = substituteRange(
bounds[variableIndex], dynamicSubstitution, nullSubstitution,
isCovariant: true);
}
}
for (int i = 0; i < variables.length; i++) {
Map<TypeVariableBuilder, KernelTypeBuilder> substitution =
<TypeVariableBuilder, KernelTypeBuilder>{};
Map<TypeVariableBuilder, KernelTypeBuilder> nullSubstitution =
<TypeVariableBuilder, KernelTypeBuilder>{};
substitution[variables[i]] = bounds[i];
nullSubstitution[variables[i]] = bottomType;
for (int j = 0; j < variables.length; j++) {
bounds[j] = substituteRange(bounds[j], substitution, nullSubstitution,
isCovariant: true);
}
}
return bounds;
}
/// Graph of mutual dependencies of type variables from the same declaration.
/// Type variables are represented by their indices in the corresponding
/// declaration.
class TypeVariablesGraph implements Graph<int> {
List<int> vertices;
List<TypeVariableBuilder> variables;
List<TypeBuilder> bounds;
// `edges[i]` is the list of indices of type variables that reference the type
// variable with the index `i` in their bounds.
List<List<int>> edges;
TypeVariablesGraph(this.variables, this.bounds) {
assert(variables.length == bounds.length);
vertices = new List<int>(variables.length);
Map<TypeVariableBuilder, int> variableIndices =
<TypeVariableBuilder, int>{};
edges = new List<List<int>>(variables.length);
for (int i = 0; i < vertices.length; i++) {
vertices[i] = i;
variableIndices[variables[i]] = i;
edges[i] = <int>[];
}
void collectReferencesFrom(int index, TypeBuilder type) {
if (type is NamedTypeBuilder) {
if (type.declaration is TypeVariableBuilder &&
this.variables.contains(type.declaration)) {
edges[variableIndices[type.declaration]].add(index);
}
if (type.arguments != null) {
for (TypeBuilder argument in type.arguments) {
collectReferencesFrom(index, argument);
}
}
} else if (type is FunctionTypeBuilder) {
if (type.typeVariables != null) {
for (TypeVariableBuilder typeVariable in type.typeVariables) {
collectReferencesFrom(index, typeVariable.bound);
}
}
if (type.formals != null) {
for (FormalParameterBuilder parameter in type.formals) {
collectReferencesFrom(index, parameter.type);
}
}
collectReferencesFrom(index, type.returnType);
}
}
for (int i = 0; i < vertices.length; i++) {
collectReferencesFrom(i, bounds[i]);
}
}
/// Returns indices of type variables that depend on the type variable with
/// [index].
Iterable<int> neighborsOf(int index) {
return edges[index];
}
}
/// Finds all type builders for [variable] in [type].
///
/// Returns list of the found type builders.
List<NamedTypeBuilder<TypeBuilder, Object>> findVariableUsesInType(
TypeVariableBuilder<TypeBuilder, Object> variable,
TypeBuilder type,
) {
var uses = <NamedTypeBuilder<TypeBuilder, Object>>[];
if (type is NamedTypeBuilder) {
if (type.declaration == variable) {
uses.add(type);
} else {
if (type.arguments != null) {
for (TypeBuilder argument in type.arguments) {
uses.addAll(findVariableUsesInType(variable, argument));
}
}
}
} else if (type is FunctionTypeBuilder) {
uses.addAll(findVariableUsesInType(variable, type.returnType));
if (type.typeVariables != null) {
for (TypeVariableBuilder<TypeBuilder, Object> dependentVariable
in type.typeVariables) {
if (dependentVariable.bound != null) {
uses.addAll(
findVariableUsesInType(variable, dependentVariable.bound));
}
if (dependentVariable.defaultType != null) {
uses.addAll(
findVariableUsesInType(variable, dependentVariable.defaultType));
}
}
}
if (type.formals != null) {
for (FormalParameterBuilder<TypeBuilder> formal in type.formals) {
uses.addAll(findVariableUsesInType(variable, formal.type));
}
}
}
return uses;
}
/// Finds those of [variables] that reference other [variables] in their bounds.
///
/// Returns flattened list of pairs. The first element in the pair is the type
/// variable builder from [variables] that references other [variables] in its
/// bound. The second element in the pair is the list of found references
/// represented as type builders.
List<Object> findInboundReferences(
List<TypeVariableBuilder<TypeBuilder, Object>> variables) {
var variablesAndDependencies = <Object>[];
for (TypeVariableBuilder<TypeBuilder, Object> dependent in variables) {
var dependencies = <NamedTypeBuilder<TypeBuilder, Object>>[];
for (TypeVariableBuilder<TypeBuilder, Object> dependence in variables) {
List<NamedTypeBuilder<TypeBuilder, Object>> uses =
findVariableUsesInType(dependence, dependent.bound);
if (uses.length != 0) {
dependencies.addAll(uses);
}
}
if (dependencies.length != 0) {
variablesAndDependencies.add(dependent);
variablesAndDependencies.add(dependencies);
}
}
return variablesAndDependencies;
}
/// Finds raw generic types in [type] with inbound references in type variables.
///
/// Returns flattened list of pairs. The first element in the pair is the found
/// raw generic type. The second element in the pair is the list of type
/// variables of that type with inbound references in the format specified in
/// [findInboundReferences].
List<Object> findRawTypesWithInboundReferences(TypeBuilder type) {
var typesAndDependencies = <Object>[];
if (type is NamedTypeBuilder<TypeBuilder, Object>) {
if (type.arguments == null) {
TypeDeclarationBuilder<TypeBuilder, Object> declaration =
type.declaration;
if (declaration is DillClassBuilder) {
bool hasInbound = false;
List<TypeParameter> typeParameters = declaration.target.typeParameters;
for (int i = 0; i < typeParameters.length && !hasInbound; ++i) {
if (containsTypeVariable(
typeParameters[i].bound, typeParameters.toSet())) {
hasInbound = true;
}
}
if (hasInbound) {
typesAndDependencies.add(type);
typesAndDependencies.add(const <Object>[]);
}
} else if (declaration is DillFunctionTypeAliasBuilder) {
bool hasInbound = false;
List<TypeParameter> typeParameters = declaration.target.typeParameters;
for (int i = 0; i < typeParameters.length && !hasInbound; ++i) {
if (containsTypeVariable(
typeParameters[i].bound, typeParameters.toSet())) {
hasInbound = true;
}
}
if (hasInbound) {
typesAndDependencies.add(type);
typesAndDependencies.add(const <Object>[]);
}
} else if (declaration is ClassBuilder<TypeBuilder, Object> &&
declaration.typeVariables != null) {
List<Object> dependencies =
findInboundReferences(declaration.typeVariables);
if (dependencies.length != 0) {
typesAndDependencies.add(type);
typesAndDependencies.add(dependencies);
}
} else if (declaration is FunctionTypeAliasBuilder<TypeBuilder, Object> &&
declaration.typeVariables != null) {
List<Object> dependencies =
findInboundReferences(declaration.typeVariables);
if (dependencies.length != 0) {
typesAndDependencies.add(type);
typesAndDependencies.add(dependencies);
}
}
} else {
for (TypeBuilder argument in type.arguments) {
typesAndDependencies
.addAll(findRawTypesWithInboundReferences(argument));
}
}
} else if (type is FunctionTypeBuilder) {
typesAndDependencies
.addAll(findRawTypesWithInboundReferences(type.returnType));
if (type.typeVariables != null) {
for (TypeVariableBuilder<TypeBuilder, Object> variable
in type.typeVariables) {
if (variable.bound != null) {
typesAndDependencies
.addAll(findRawTypesWithInboundReferences(variable.bound));
}
if (variable.defaultType != null) {
typesAndDependencies
.addAll(findRawTypesWithInboundReferences(variable.defaultType));
}
}
}
if (type.formals != null) {
for (FormalParameterBuilder<TypeBuilder> formal in type.formals) {
typesAndDependencies
.addAll(findRawTypesWithInboundReferences(formal.type));
}
}
}
return typesAndDependencies;
}
/// Finds issues by raw generic types with inbound references in type variables.
///
/// Returns flattened list of triplets. The first element of the triplet is the
/// [TypeDeclarationBuilder] for the type variable from [variables] that has raw
/// generic types with inbound references in its bound. The second element of
/// the triplet is the error message. The third element is the context.
List<Object> getInboundReferenceIssues(
List<TypeVariableBuilder<TypeBuilder, Object>> variables) {
var issues = <Object>[];
for (TypeVariableBuilder<TypeBuilder, Object> variable in variables) {
if (variable.bound != null) {
List<Object> rawTypesAndMutualDependencies =
findRawTypesWithInboundReferences(variable.bound);
for (int i = 0; i < rawTypesAndMutualDependencies.length; i += 2) {
NamedTypeBuilder<TypeBuilder, Object> type =
rawTypesAndMutualDependencies[i];
List<Object> variablesAndDependencies =
rawTypesAndMutualDependencies[i + 1];
for (int j = 0; j < variablesAndDependencies.length; j += 2) {
TypeVariableBuilder<TypeBuilder, Object> dependent =
variablesAndDependencies[j];
List<NamedTypeBuilder<TypeBuilder, Object>> dependencies =
variablesAndDependencies[j + 1];
for (NamedTypeBuilder<TypeBuilder, Object> dependency
in dependencies) {
issues.add(variable);
issues.add(templateBoundIssueViaRawTypeWithNonSimpleBounds
.withArguments(type.declaration.name));
issues.add(<LocatedMessage>[
templateNonSimpleBoundViaVariable
.withArguments(dependency.declaration.name)
.withLocation(dependent.fileUri, dependent.charOffset,
dependent.name.length)
]);
}
}
if (variablesAndDependencies.length == 0) {
// The inbound references are in a compiled declaration in a .dill.
issues.add(variable);
issues.add(templateBoundIssueViaRawTypeWithNonSimpleBounds
.withArguments(type.declaration.name));
issues.add(const <LocatedMessage>[]);
}
}
}
}
return issues;
}
/// Finds raw type paths starting from those in [start] and ending with [end].
///
/// Returns list of found paths. Each path is represented as a list of
/// alternating builders of the raw generic types from the path and builders of
/// type variables of the immediately preceding types that contain the reference
/// to the next raw generic type in the path. The list ends with the type
/// builder for [end].
///
/// The reason for putting the type variables into the paths as well as for
/// using type for [start], and not the corresponding type declaration,
/// is better error reporting.
List<List<Object>> findRawTypePathsToDeclaration(
TypeBuilder start, TypeDeclarationBuilder<TypeBuilder, Object> end,
[Set<TypeDeclarationBuilder<TypeBuilder, Object>> visited]) {
visited ??= new Set<TypeDeclarationBuilder<TypeBuilder, Object>>.identity();
var paths = <List<Object>>[];
if (start is NamedTypeBuilder<TypeBuilder, Object>) {
TypeDeclarationBuilder<TypeBuilder, Object> declaration = start.declaration;
if (start.arguments == null) {
if (start.declaration == end) {
paths.add(<Object>[start]);
} else if (visited.add(start.declaration)) {
if (declaration is ClassBuilder<TypeBuilder, Object> &&
declaration.typeVariables != null) {
for (TypeVariableBuilder<TypeBuilder, Object> variable
in declaration.typeVariables) {
if (variable.bound != null) {
for (List<Object> path in findRawTypePathsToDeclaration(
variable.bound, end, visited)) {
paths.add(<Object>[start, variable]..addAll(path));
}
}
}
} else if (declaration
is FunctionTypeAliasBuilder<TypeBuilder, Object> &&
declaration.typeVariables != null) {
for (TypeVariableBuilder<TypeBuilder, Object> variable
in declaration.typeVariables) {
if (variable.bound != null) {
for (List<Object> dependencyPath in findRawTypePathsToDeclaration(
variable.bound, end, visited)) {
paths.add(<Object>[start, variable]..addAll(dependencyPath));
}
}
}
}
visited.remove(start.declaration);
}
} else {
for (TypeBuilder argument in start.arguments) {
paths.addAll(findRawTypePathsToDeclaration(argument, end, visited));
}
}
} else if (start is FunctionTypeBuilder) {
paths.addAll(findRawTypePathsToDeclaration(start.returnType, end, visited));
if (start.typeVariables != null) {
for (TypeVariableBuilder<TypeBuilder, Object> variable
in start.typeVariables) {
if (variable.bound != null) {
paths.addAll(
findRawTypePathsToDeclaration(variable.bound, end, visited));
}
if (variable.defaultType != null) {
paths.addAll(findRawTypePathsToDeclaration(
variable.defaultType, end, visited));
}
}
}
if (start.formals != null) {
for (FormalParameterBuilder<TypeBuilder> formal in start.formals) {
paths.addAll(findRawTypePathsToDeclaration(formal.type, end, visited));
}
}
}
return paths;
}
/// Finds raw generic type cycles ending and starting with [declaration].
///
/// Returns list of found cycles. Each cycle is represented as a list of
/// alternating raw generic types from the cycle and type variables of the
/// immediately preceding type that reference the next type in the cycle. The
/// cycle starts with a type variable from [declaration] and ends with a type
/// that has [declaration] as its declaration.
///
/// The reason for putting the type variables into the cycles is better error
/// reporting.
List<List<Object>> findRawTypeCycles(
TypeDeclarationBuilder<TypeBuilder, Object> declaration) {
var cycles = <List<Object>>[];
if (declaration is ClassBuilder<TypeBuilder, Object> &&
declaration.typeVariables != null) {
for (TypeVariableBuilder<TypeBuilder, Object> variable
in declaration.typeVariables) {
if (variable.bound != null) {
for (List<Object> path
in findRawTypePathsToDeclaration(variable.bound, declaration)) {
cycles.add(<Object>[variable]..addAll(path));
}
}
}
} else if (declaration is FunctionTypeAliasBuilder<TypeBuilder, Object> &&
declaration.typeVariables != null) {
for (TypeVariableBuilder<TypeBuilder, Object> variable
in declaration.typeVariables) {
if (variable.bound != null) {
for (List<Object> dependencyPath
in findRawTypePathsToDeclaration(variable.bound, declaration)) {
cycles.add(<Object>[variable]..addAll(dependencyPath));
}
}
}
}
return cycles;
}
/// Converts raw generic type [cycles] for [declaration] into reportable issues.
///
/// The [cycles] are expected to be in the format specified for the return value
/// of [findRawTypeCycles].
///
/// Returns flattened list of triplets. The first element of the triplet is the
/// [TypeDeclarationBuilder] for the type variable from [variables] that has raw
/// generic types with inbound references in its bound. The second element of
/// the triplet is the error message. The third element is the context.
List<Object> convertRawTypeCyclesIntoIssues(
TypeDeclarationBuilder<TypeBuilder, Object> declaration,
List<List<Object>> cycles) {
List<Object> issues = <Object>[];
for (List<Object> cycle in cycles) {
if (cycle.length == 2) {
// Loop.
TypeVariableBuilder<TypeBuilder, Object> variable = cycle[0];
NamedTypeBuilder<TypeBuilder, Object> type = cycle[1];
issues.add(variable);
issues.add(templateBoundIssueViaLoopNonSimplicity
.withArguments(type.declaration.name));
issues.add(null); // Context.
} else {
var context = <LocatedMessage>[];
for (int i = 0; i < cycle.length; i += 2) {
TypeVariableBuilder<TypeBuilder, Object> variable = cycle[i];
NamedTypeBuilder<TypeBuilder, Object> type = cycle[i + 1];
context.add(templateNonSimpleBoundViaReference
.withArguments(type.declaration.name)
.withLocation(
variable.fileUri, variable.charOffset, variable.name.length));
}
NamedTypeBuilder<TypeBuilder, Object> firstEncounteredType = cycle[1];
issues.add(declaration);
issues.add(templateBoundIssueViaCycleNonSimplicity.withArguments(
declaration.name, firstEncounteredType.declaration.name));
issues.add(context);
}
}
return issues;
}
/// Finds issues by cycles of raw generic types containing [declaration].
///
/// Returns flattened list of triplets according to the format specified by
/// [convertRawTypeCyclesIntoIssues].
List<Object> getRawTypeCycleIssues(
TypeDeclarationBuilder<TypeBuilder, Object> declaration) {
return convertRawTypeCyclesIntoIssues(
declaration, findRawTypeCycles(declaration));
}
/// Finds non-simplicity issues for the given set of [variables].
///
/// The issues are those caused by raw types with inbound references in the
/// bounds of their type variables.
///
/// Returns flattened list of triplets, each triplet representing an issue. The
/// first element in the triplet is the type declaration that has the issue.
/// The second element in the triplet is the error message. The third element
/// in the triplet is the context.
List<Object> getNonSimplicityIssuesForTypeVariables(
List<TypeVariableBuilder<TypeBuilder, Object>> variables) {
if (variables == null) return <Object>[];
return getInboundReferenceIssues(variables);
}
/// Finds non-simplicity issues for the given [declaration].
///
/// The issues are those caused by raw types with inbound references in the
/// bounds of type variables from [declaration] and by cycles of raw types
/// containing [declaration].
///
/// Returns flattened list of triplets, each triplet representing an issue. The
/// first element in the triplet is the type declaration that has the issue.
/// The second element in the triplet is the error message. The third element
/// in the triplet is the context.
List<Object> getNonSimplicityIssuesForDeclaration(
TypeDeclarationBuilder<TypeBuilder, Object> declaration) {
var issues = <Object>[];
if (declaration is ClassBuilder<TypeBuilder, Object> &&
declaration.typeVariables != null) {
issues.addAll(getInboundReferenceIssues(declaration.typeVariables));
} else if (declaration is FunctionTypeAliasBuilder<TypeBuilder, Object> &&
declaration.typeVariables != null) {
issues.addAll(getInboundReferenceIssues(declaration.typeVariables));
}
List<List<Object>> cyclesToReport = <List<Object>>[];
for (List<Object> cycle in findRawTypeCycles(declaration)) {
// To avoid reporting the same error for each element of the cycle, we only
// do so if it comes the first in the lexicographical order. Note that
// one-element cycles shouldn't be checked, as they are loops.
if (cycle.length == 2) {
cyclesToReport.add(cycle);
} else {
String declarationPathAndName =
"${declaration.fileUri}:${declaration.name}";
String lexMinPathAndName = null;
for (int i = 1; i < cycle.length; i += 2) {
NamedTypeBuilder<TypeBuilder, Object> type = cycle[i];
String pathAndName =
"${type.declaration.fileUri}:${type.declaration.name}";
if (lexMinPathAndName == null ||
lexMinPathAndName.compareTo(pathAndName) > 0) {
lexMinPathAndName = pathAndName;
}
}
if (declarationPathAndName == lexMinPathAndName) {
cyclesToReport.add(cycle);
}
}
}
issues.addAll(convertRawTypeCyclesIntoIssues(declaration, cyclesToReport));
return issues;
}
void findGenericFunctionTypes(TypeBuilder type, {List<TypeBuilder> result}) {
result ??= <TypeBuilder>[];
if (type is FunctionTypeBuilder) {
if (type.typeVariables != null && type.typeVariables.length > 0) {
result.add(type);
for (TypeVariableBuilder<TypeBuilder, Object> typeVariable
in type.typeVariables) {
findGenericFunctionTypes(typeVariable.bound, result: result);
findGenericFunctionTypes(typeVariable.defaultType, result: result);
}
}
findGenericFunctionTypes(type.returnType, result: result);
if (type.formals != null) {
for (FormalParameterBuilder<TypeBuilder> formal in type.formals) {
findGenericFunctionTypes(formal.type, result: result);
}
}
} else if (type is NamedTypeBuilder<TypeBuilder, Object> &&
type.arguments != null) {
for (TypeBuilder argument in type.arguments) {
findGenericFunctionTypes(argument, result: result);
}
}
}