Skip to content

Commit e1a766b

Browse files
Ilya Yanokcopybara-github
Ilya Yanok
authored andcommitted
First attempt was #671
and I had to roll it back, since it caused breakages in two ways: 1. Sometimes `ParameterElement` from `type.parameters` had `enclosingElement` set to `null` and we use that in one helper function. That was easy to fix, we could just pass `methodElement` to that function directly. It's probably correct that `ParameterElement` of a `FunctionType` doesn't link back to a `MethodElement`, but it's weird that sometimes it does, so it wasn't caught in the tests. I had to get rid of using `type.parameters` anyway because of the second problem. 2. `type.parameters` don't contain parameters' default values (totally correct, since default values belong to methods, not to types), but that means we can't use them, since we do need default values. So I ended up with a more hacky solution, that uses `type.typeFormals` just to get correct references for method's type parameters, and then uses `typeParameters`, `returnType` and `parameters` for the rest as before. Original commit description: Turns out `FunctionTypedElement.typeParameters` could be inconsistent for `MethodMember`s returned by `InheritanceManager3.getMember2`. `FunctionTypedElement.type.typeFormals` seem to be always good, but we have to also use `type.parameters` and `type.returnType` instead of just `parameters` and `returnType` in this case. Fixes #658 PiperOrigin-RevId: 545934516
1 parent c13496c commit e1a766b

File tree

2 files changed

+46
-6
lines changed

2 files changed

+46
-6
lines changed

lib/src/builder.dart

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1436,7 +1436,7 @@ class _MockClassInfo {
14361436
}
14371437

14381438
builder.body = superNoSuchMethod.code;
1439-
});
1439+
}, typeFormalsHack: method.type.typeFormals);
14401440
}
14411441

14421442
Expression _fallbackGeneratorCode(
@@ -2058,16 +2058,33 @@ class _MockClassInfo {
20582058
}
20592059

20602060
T _withTypeParameters<T>(Iterable<TypeParameterElement> typeParameters,
2061-
T Function(Iterable<TypeReference>, Iterable<TypeReference>) body) {
2062-
final typeVars = {for (final t in typeParameters) t: _newTypeVar(t)};
2063-
_typeVariableScopes.add(typeVars);
2064-
final typeRefsWithBounds = typeParameters.map(_typeParameterReference);
2061+
T Function(Iterable<TypeReference>, Iterable<TypeReference>) body,
2062+
{Iterable<TypeParameterElement>? typeFormalsHack}) {
2063+
final typeVars = [for (final t in typeParameters) _newTypeVar(t)];
2064+
final scope = Map.fromIterables(typeParameters, typeVars);
2065+
if (typeFormalsHack != null) {
2066+
// add a scope based on [type.typeFormals] just to make
2067+
// type parameters references, as [typeParameters] could contain
2068+
// an inconsistency if a parameter refers itself in its own bound,
2069+
// see https://github.com/dart-lang/mockito/issues/658
2070+
_typeVariableScopes.add(Map.fromIterables(typeFormalsHack, typeVars));
2071+
// use typeFormals instead of typeParameters to create refs.
2072+
typeParameters = typeFormalsHack;
2073+
} else {
2074+
_typeVariableScopes.add(scope);
2075+
}
2076+
final typeRefsWithBounds =
2077+
typeParameters.map(_typeParameterReference).toList();
20652078
final typeRefs =
20662079
typeParameters.map((t) => _typeParameterReference(t, withBound: false));
20672080

2081+
if (typeFormalsHack != null) {
2082+
_typeVariableScopes.removeLast();
2083+
_typeVariableScopes.add(scope);
2084+
}
20682085
final result = body(typeRefsWithBounds, typeRefs);
20692086
_typeVariableScopes.removeLast();
2070-
_usedTypeVariables.removeAll(typeVars.values);
2087+
_usedTypeVariables.removeAll(typeVars);
20712088
return result;
20722089
}
20732090

test/builder/custom_mocks_test.dart

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1843,6 +1843,29 @@ void main() {
18431843
expect(mocksContent, contains('Iterable<X1> m1<X1>(X1 Function(X)? f)'));
18441844
expect(mocksContent, contains('Iterable<X1?> m2<X1>(X1 Function(X)? f)'));
18451845
});
1846+
test('We preserve nested generic bounded type arguments', () async {
1847+
final mocksContent = await buildWithNonNullable({
1848+
...annotationsAsset,
1849+
'foo|lib/foo.dart': dedent(r'''
1850+
class Foo<A, B> {}
1851+
abstract class Bar<T> {
1852+
X m1<X extends Foo<Foo<X, T>, X>>(X Function(T)? f);
1853+
}
1854+
abstract class FooBar<X> extends Bar<X> {}
1855+
'''),
1856+
'foo|test/foo_test.dart': '''
1857+
import 'package:foo/foo.dart';
1858+
import 'package:mockito/annotations.dart';
1859+
1860+
@GenerateMocks([FooBar])
1861+
void main() {}
1862+
'''
1863+
});
1864+
expect(
1865+
mocksContent,
1866+
contains(
1867+
'X1 m1<X1 extends _i2.Foo<_i2.Foo<X1, X>, X1>>(X1 Function(X)? f)'));
1868+
});
18461869
}
18471870

18481871
TypeMatcher<List<int>> _containsAllOf(a, [b]) => decodedMatches(

0 commit comments

Comments
 (0)