From 6ddd3f08820ec80a685416603c4a6cf8715f8a53 Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Tue, 15 Oct 2024 21:46:45 -0500 Subject: [PATCH] feat: use `covariant` in `operator==` override for improved type safety (#180) --- benchmarks/README.md | 32 ++++++------- lib/src/equatable.dart | 8 ++-- test/equatable_test.dart | 100 --------------------------------------- 3 files changed, 19 insertions(+), 121 deletions(-) diff --git a/benchmarks/README.md b/benchmarks/README.md index aed2ba06..edadc124 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -13,7 +13,7 @@ Benchmarks used to measure the performance of equality comparisons using `packag ``` EmptyEquatable - total runs: 7 741 659 + total runs: 7 984 597 total time: 2.0000 s average run: 0 μs runs/second: Infinity @@ -22,7 +22,7 @@ EmptyEquatable time per unit: 0.0000 μs PrimitiveEquatable - total runs: 1 347 013 + total runs: 1 349 110 total time: 2.0000 s average run: 1 μs runs/second: 1 000 000 @@ -31,7 +31,7 @@ PrimitiveEquatable time per unit: 0.0100 μs CollectionEquatable (static, small) - total runs: 54 740 + total runs: 54 582 total time: 2.0000 s average run: 36 μs runs/second: 27 778 @@ -40,25 +40,25 @@ CollectionEquatable (static, small) time per unit: 0.3600 μs CollectionEquatable (static, medium) - total runs: 45 852 + total runs: 46 839 total time: 2.0000 s - average run: 43 μs - runs/second: 23 256 + average run: 42 μs + runs/second: 23 810 units: 100 - units/second: 2 325 581 - time per unit: 0.4300 μs + units/second: 2 380 952 + time per unit: 0.4200 μs CollectionEquatable (static, large) - total runs: 20 328 + total runs: 20 867 total time: 2.0001 s - average run: 98 μs - runs/second: 10 204 + average run: 95 μs + runs/second: 10 526 units: 100 - units/second: 1 020 408 - time per unit: 0.9800 μs + units/second: 1 052 632 + time per unit: 0.9500 μs CollectionEquatable (dynamic, small) - total runs: 623 140 + total runs: 629 974 total time: 2.0000 s average run: 3 μs runs/second: 333 333 @@ -67,7 +67,7 @@ CollectionEquatable (dynamic, small) time per unit: 0.0300 μs CollectionEquatable (dynamic, medium) - total runs: 618 821 + total runs: 628 191 total time: 2.0000 s average run: 3 μs runs/second: 333 333 @@ -76,7 +76,7 @@ CollectionEquatable (dynamic, medium) time per unit: 0.0300 μs CollectionEquatable (dynamic, large) - total runs: 627 611 + total runs: 632 540 total time: 2.0000 s average run: 3 μs runs/second: 333 333 diff --git a/lib/src/equatable.dart b/lib/src/equatable.dart index 41a10499..10235b25 100644 --- a/lib/src/equatable.dart +++ b/lib/src/equatable.dart @@ -48,13 +48,11 @@ macro class Equatable implements ClassDeclarationsMacro, ClassDefinitionMacro { ClassDeclaration clazz, MemberDeclarationBuilder builder, ) async { - final (object, boolean) = await ( - builder.codeFrom(_dartCore, 'Object'), - builder.codeFrom(_dartCore, 'bool'), - ).wait; + final boolean = await builder.codeFrom(_dartCore, 'bool'); + return builder.declareInType( DeclarationCode.fromParts( - ['external ', boolean, ' operator==(', object, ' other);'], + ['external ', boolean, ' operator==(', ' covariant ${clazz.identifier.name}', ' other);'], ), ); } diff --git a/test/equatable_test.dart b/test/equatable_test.dart index 42785453..9055f680 100644 --- a/test/equatable_test.dart +++ b/test/equatable_test.dart @@ -8,8 +8,6 @@ import 'package:test/test.dart'; import 'custom_list.dart'; -class NonEquatable {} - @Equatable() class EmptyEquatable { const EmptyEquatable(); @@ -29,13 +27,6 @@ class MultipartEquatable { final T d2; } -@Equatable() -class OtherEquatable { - const OtherEquatable(this.data); - - final String data; -} - enum Color { blonde, black, brown } @Equatable() @@ -97,12 +88,6 @@ void main() { expect(instanceA == instanceB, true); expect(instanceA.hashCode == instanceB.hashCode, true); }); - - test('should return false when compared to non-equatable', () { - final instanceA = EmptyEquatable(); - final instanceB = NonEquatable(); - expect(instanceA == instanceB, false); - }); }); group('Simple Equatable (string)', () { @@ -124,18 +109,6 @@ void main() { expect(instanceA.hashCode == instanceB.hashCode, true); }); - test('should return false when compared to non-equatable', () { - final instanceA = SimpleEquatable('simple'); - final instanceB = NonEquatable(); - expect(instanceA == instanceB, false); - }); - - test('should return false when compared to different equatable', () { - final instanceA = SimpleEquatable('simple'); - final instanceB = OtherEquatable('simple'); - expect(instanceA == instanceB, false); - }); - test('should return false when values are different', () { final instanceA = SimpleEquatable('simple'); final instanceB = SimpleEquatable('Simple'); @@ -162,12 +135,6 @@ void main() { expect(instanceA.hashCode == instanceB.hashCode, true); }); - test('should return false when compared to non-equatable', () { - final instanceA = SimpleEquatable(0); - final instanceB = NonEquatable(); - expect(instanceA == instanceB, false); - }); - test('should return false when values are different', () { final instanceA = SimpleEquatable(0); final instanceB = SimpleEquatable(1); @@ -194,12 +161,6 @@ void main() { expect(instanceA.hashCode == instanceB.hashCode, true); }); - test('should return false when compared to non-equatable', () { - final instanceA = SimpleEquatable(true); - final instanceB = NonEquatable(); - expect(instanceA == instanceB, false); - }); - test('should return false when values are different', () { final instanceA = SimpleEquatable(true); final instanceB = SimpleEquatable(false); @@ -227,12 +188,6 @@ void main() { expect(instanceA.hashCode == instanceB.hashCode, true); }); - test('should return false when compared to non-equatable', () { - final instanceA = SimpleEquatable({'a': 1, 'b': 2, 'c': 3}); - final instanceB = NonEquatable(); - expect(instanceA == instanceB, false); - }); - test('should return false when values are different', () { final instanceA = SimpleEquatable({'a': 1, 'b': 2, 'c': 3}); final instanceB = SimpleEquatable({'a': 1, 'b': 2, 'c': 4}); @@ -308,18 +263,6 @@ void main() { expect(instanceA.hashCode == instanceB.hashCode, true); }); - test('should return false when compared to non-equatable', () { - final instanceA = SimpleEquatable( - { - SimpleEquatable('a'): 1, - SimpleEquatable('b'): 2, - SimpleEquatable('c'): 3, - }, - ); - final instanceB = NonEquatable(); - expect(instanceA == instanceB, false); - }); - test('should return false when values are different', () { final instanceA = SimpleEquatable( { @@ -383,17 +326,6 @@ void main() { expect(instanceA.hashCode == instanceB.hashCode, true); }); - test('should return false when compared to non-equatable', () { - final instanceA = SimpleEquatable( - EquatableData( - key: 'foo', - value: 'bar', - ), - ); - final instanceB = NonEquatable(); - expect(instanceA == instanceB, false); - }); - test('should return false when values are different', () { final instanceA = SimpleEquatable( EquatableData( @@ -438,12 +370,6 @@ void main() { expect(instanceA.hashCode == instanceB.hashCode, true); }); - test('should return false when compared to non-equatable', () { - final instanceA = MultipartEquatable('s1', 's2'); - final instanceB = NonEquatable(); - expect(instanceA == instanceB, false); - }); - test('should return false when values are different', () { final instanceA = MultipartEquatable('s1', 's2'); final instanceB = MultipartEquatable('s2', 's1'); @@ -499,17 +425,6 @@ void main() { expect(instanceA.hashCode == instanceB.hashCode, true); }); - test('should return false when compared to non-equatable', () { - final instanceA = ComplexEquatable( - name: 'Joe', - age: 40, - hairColor: Color.black, - children: ['Bob'], - ); - final instanceB = NonEquatable(); - expect(instanceA == instanceB, false); - }); - test('should return false when values are different', () { final instanceA = ComplexEquatable( name: 'Joe', @@ -623,21 +538,6 @@ void main() { expect(instanceA.hashCode == instanceB.hashCode, true); }); - test('should return false when compared to non-equatable', () { - final instanceA = Credentials.fromJson( - json.decode( - ''' - { - "username":"Admin", - "password":"admin" - } - ''', - ) as Map, - ); - final instanceB = NonEquatable(); - expect(instanceA == instanceB, false); - }); - test('should return false when values are different', () { final instanceA = Credentials.fromJson( json.decode(