Skip to content

Commit

Permalink
Second attempt to fix "not found" error for type vars in bounds
Browse files Browse the repository at this point in the history
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:

Use `FunctionTypedElement.type` while generating method overrides

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
  • Loading branch information
Ilya Yanok authored and copybara-github committed Jul 7, 2023
1 parent c13496c commit 72e4f3c
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 6 deletions.
29 changes: 23 additions & 6 deletions lib/src/builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1436,7 +1436,7 @@ class _MockClassInfo {
}

builder.body = superNoSuchMethod.code;
});
}, typeFormalsHack: method.type.typeFormals);
}

Expression _fallbackGeneratorCode(
Expand Down Expand Up @@ -2058,16 +2058,33 @@ class _MockClassInfo {
}

T _withTypeParameters<T>(Iterable<TypeParameterElement> typeParameters,
T Function(Iterable<TypeReference>, Iterable<TypeReference>) body) {
final typeVars = {for (final t in typeParameters) t: _newTypeVar(t)};
_typeVariableScopes.add(typeVars);
final typeRefsWithBounds = typeParameters.map(_typeParameterReference);
T Function(Iterable<TypeReference>, Iterable<TypeReference>) body,
{Iterable<TypeParameterElement>? typeFormalsHack}) {
final typeVars = [for (final t in typeParameters) _newTypeVar(t)];
final scope = Map.fromIterables(typeParameters, typeVars);
if (typeFormalsHack != null) {
// add a scope based on [type.typeFormals] just to make
// type parameters references, as [typeParameters] could contain
// an inconsistency if a parameter refers itself in its own bound,
// see https://github.com/dart-lang/mockito/issues/658
_typeVariableScopes.add(Map.fromIterables(typeFormalsHack, typeVars));
// use typeFormals instead of typeParameters to create refs.
typeParameters = typeFormalsHack;
} else {
_typeVariableScopes.add(scope);
}
final typeRefsWithBounds =
typeParameters.map(_typeParameterReference).toList();
final typeRefs =
typeParameters.map((t) => _typeParameterReference(t, withBound: false));

if (typeFormalsHack != null) {
_typeVariableScopes.removeLast();
_typeVariableScopes.add(scope);
}
final result = body(typeRefsWithBounds, typeRefs);
_typeVariableScopes.removeLast();
_usedTypeVariables.removeAll(typeVars.values);
_usedTypeVariables.removeAll(typeVars);
return result;
}

Expand Down
15 changes: 15 additions & 0 deletions test/builder/auto_mocks_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1540,6 +1540,21 @@ void main() {
);
});

test('widens the type of covariant generic parameters to be nullable',
() async {
await expectSingleNonNullableOutput(
dedent('''
abstract class FooBase<T extends Object> {
void m(Object a);
}
abstract class Foo<T extends Object> extends FooBase<T> {
void m(covariant T a);
}
'''),
_containsAllOf('void m(Object? a) => super.noSuchMethod('),
);
});

test('matches nullability of type arguments of a parameter', () async {
await expectSingleNonNullableOutput(
dedent(r'''
Expand Down
23 changes: 23 additions & 0 deletions test/builder/custom_mocks_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1843,6 +1843,29 @@ void main() {
expect(mocksContent, contains('Iterable<X1> m1<X1>(X1 Function(X)? f)'));
expect(mocksContent, contains('Iterable<X1?> m2<X1>(X1 Function(X)? f)'));
});
test('We preserve nested generic bounded type arguments', () async {
final mocksContent = await buildWithNonNullable({
...annotationsAsset,
'foo|lib/foo.dart': dedent(r'''
class Foo<A, B> {}
abstract class Bar<T> {
X m1<X extends Foo<Foo<X, T>, X>>(X Function(T)? f);
}
abstract class FooBar<X> extends Bar<X> {}
'''),
'foo|test/foo_test.dart': '''
import 'package:foo/foo.dart';
import 'package:mockito/annotations.dart';
@GenerateMocks([FooBar])
void main() {}
'''
});
expect(
mocksContent,
contains(
'X1 m1<X1 extends _i2.Foo<_i2.Foo<X1, X>, X1>>(X1 Function(X)? f)'));
});
}

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

0 comments on commit 72e4f3c

Please sign in to comment.