Skip to content

Commit

Permalink
parse records and typedef in type arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
dickermoshe committed Oct 29, 2024
1 parent 57d484f commit e8e95bc
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 12 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
* Require dart_style >= 2.3.7, so that the current Dart language version can be
passed to `DartFormatter`.
* Add topics to `pubspec.yaml`.
* Fix a bug where typedef-aliases in type arguments were not correctly
resolved.
* Fix a bug where record types were not correctly resolved.

## 5.4.4

Expand Down
27 changes: 15 additions & 12 deletions lib/src/builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -167,25 +167,28 @@ $rawOutput
// `Future`, which is needed when overriding some methods which return
// `FutureOr`.
final typeVisitor = _TypeVisitor(entryLib.typeProvider.futureDynamicType);
final seenTypes = <analyzer.InterfaceType>{};
final seenTypes = <analyzer.DartType>{};
final librariesWithTypes = <LibraryElement>{};

void addTypesFrom(analyzer.InterfaceType type) {
void addTypesFrom(analyzer.DartType type) {
// Prevent infinite recursion.
if (seenTypes.contains(type)) {
return;
}
seenTypes.add(type);
librariesWithTypes.add(type.element.library);
type.element.accept(typeVisitor);
if (type.alias != null) type.alias!.element.accept(typeVisitor);
// For a type like `Foo<Bar>`, add the `Bar`.
type.typeArguments
.whereType<analyzer.InterfaceType>()
.forEach(addTypesFrom);
// For a type like `Foo extends Bar<Baz>`, add the `Baz`.
for (final supertype in type.allSupertypes) {
addTypesFrom(supertype);
librariesWithTypes.addAll([
if (type.element?.library != null) type.element!.library!,
if (type.alias?.element.library != null) type.alias!.element.library,
]);
type.element?.accept(typeVisitor);
type.alias?.element.accept(typeVisitor);
switch (type) {
case analyzer.InterfaceType interface:
interface.typeArguments.forEach(addTypesFrom);
interface.allSupertypes.forEach(addTypesFrom);
case analyzer.RecordType record:
record.positionalTypes.forEach(addTypesFrom);
record.namedTypes.map((e) => e.type).forEach(addTypesFrom);
}
}

Expand Down
76 changes: 76 additions & 0 deletions test/builder/auto_mocks_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3553,6 +3553,34 @@ void main() {
expect(mocksContent, contains('class MockBaz extends _i1.Mock'));
expect(mocksContent, contains('implements _i2.Baz'));
});

test('when a type parameter is a typedef a function', () async {
final mocksContent = await buildWithNonNullable({
...annotationsAsset,
'foo|lib/foo.dart': dedent(r'''
typedef CreateInt = int Function();
class BaseFoo<T> {
BaseFoo(this.t);
final T t;
}
class Foo extends BaseFoo<CreateInt> {
Foo() : super(() => 1);
}
'''),
'foo|test/foo_test.dart': '''
import 'package:foo/foo.dart';
import 'package:mockito/annotations.dart';
@GenerateMocks([Foo])
void main() {}
'''
});

expect(mocksContent, contains('class MockFoo extends _i1.Mock'));
expect(mocksContent, contains('implements _i2.Foo'));
});
});

test('generation throws when the aliased type is nullable', () {
Expand Down Expand Up @@ -3620,6 +3648,54 @@ void main() {
contains('returnValue: _i3.Future<(int, {_i2.Bar bar})>.value('),
contains('bar: _FakeBar_0('))));
});
test('are supported as typedefs', () async {
final mocksContent = await buildWithNonNullable({
...annotationsAsset,
'foo|lib/foo.dart': dedent(r'''
class Bar {}
class BaseFoo<T> {
BaseFoo(this.t);
final T t;
}
class Foo extends BaseFoo<(Bar, Bar)> {
Foo() : super((Bar(), Bar()));
}
'''),
'foo|test/foo_test.dart': '''
import 'package:foo/foo.dart';
import 'package:mockito/annotations.dart';
@GenerateMocks([Foo])
void main() {}
'''
});

expect(mocksContent, contains('class MockFoo extends _i1.Mock'));
expect(mocksContent, contains('implements _i2.Foo'));
});
test('are supported as nested typedefs', () async {
final mocksContent = await buildWithNonNullable({
...annotationsAsset,
'foo|lib/foo.dart': dedent(r'''
class Bar {}
class BaseFoo<T> {
BaseFoo(this.t);
final T t;
}
class Foo extends BaseFoo<(int, (Bar, Bar))> {
Foo() : super(((1, (Bar(), Bar()))));
}
'''),
'foo|test/foo_test.dart': '''
import 'package:foo/foo.dart';
import 'package:mockito/annotations.dart';
@GenerateMocks([Foo])
void main() {}
'''
});

expect(mocksContent, contains('class MockFoo extends _i1.Mock'));
expect(mocksContent, contains('implements _i2.Foo'));
});
});

group('Extension types', () {
Expand Down

0 comments on commit e8e95bc

Please sign in to comment.