Skip to content

Commit c7dcdbb

Browse files
committed
feat: mark generated classes as final if freezed class is sealed
1 parent ef36f88 commit c7dcdbb

File tree

4 files changed

+110
-1
lines changed

4 files changed

+110
-1
lines changed

packages/freezed/lib/src/models.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,7 @@ class Class {
504504
required this.superCall,
505505
required this.properties,
506506
required this.copyWithTarget,
507+
required this.isSealed,
507508
}) : assert(constructors.isNotEmpty);
508509

509510
final String name;
@@ -516,6 +517,7 @@ class Class {
516517
final CopyWithTarget? copyWithTarget;
517518
final PropertyList properties;
518519
final List<Class> parents = [];
520+
final bool isSealed;
519521

520522
static Class _from(
521523
ClassDeclaration declaration,
@@ -597,6 +599,7 @@ class Class {
597599
genericsParameterTemplate: GenericsParameterTemplate.fromGenericElement(
598600
declaration.declaredElement!.typeParameters,
599601
),
602+
isSealed: declaration.declaredElement!.isSealed,
600603
);
601604
}
602605

packages/freezed/lib/src/templates/concrete_template.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class Concrete {
4949
/// @nodoc
5050
$jsonSerializable
5151
${constructor.decorators.join('\n')}
52-
class ${constructor.redirectedName}${data.genericsDefinitionTemplate} $_concreteSuper {
52+
${data.isSealed ? 'final ' : ''}class ${constructor.redirectedName}${data.genericsDefinitionTemplate} $_concreteSuper {
5353
$_concreteConstructor
5454
$_concreteFromJsonConstructor
5555
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import 'package:analyzer/dart/analysis/results.dart';
2+
import 'package:analyzer/error/error.dart';
3+
import 'package:build_test/build_test.dart';
4+
import 'package:test/test.dart';
5+
6+
void main() {
7+
group('marks generated classes as final and sealed', () {
8+
group('single constructor', () {
9+
test(
10+
'causes pattern_never_matches_value_type warning when trying to match on pattern that can never match',
11+
() async {
12+
final main = await resolveSources(
13+
{
14+
'freezed|test/integration/main.dart': r'''
15+
library main;
16+
import 'finalized.dart';
17+
18+
void main() {
19+
switch (FinalizedFoo()) {
20+
case FinalizedBar():
21+
break;
22+
23+
case FinalizedFoo():
24+
break;
25+
}
26+
}
27+
''',
28+
},
29+
(r) => r.findLibraryByName('main'),
30+
);
31+
32+
final errorResult = await main!.session
33+
.getErrors('/freezed/test/integration/main.dart') as ErrorsResult;
34+
35+
expect(errorResult.errors, hasLength(1));
36+
37+
final [error] = errorResult.errors;
38+
39+
expect(error.errorCode.errorSeverity, ErrorSeverity.WARNING);
40+
expect(error.errorCode.name, 'PATTERN_NEVER_MATCHES_VALUE_TYPE');
41+
});
42+
});
43+
44+
group('multiple constructors', () {
45+
test(
46+
'causes pattern_never_matches_value_type warning when trying to match on pattern that can never match',
47+
() async {
48+
final main = await resolveSources(
49+
{
50+
'freezed|test/integration/main.dart': r'''
51+
library main;
52+
import 'finalized.dart';
53+
54+
void main() {
55+
switch (FinalizedMultiple.b()) {
56+
case FinalizedBar():
57+
break;
58+
59+
case FinalizedMultipleA():
60+
break;
61+
62+
case FinalizedMultipleB():
63+
break;
64+
65+
case FinalizedMultipleC():
66+
break;
67+
}
68+
}
69+
''',
70+
},
71+
(r) => r.findLibraryByName('main'),
72+
);
73+
74+
final errorResult = await main!.session
75+
.getErrors('/freezed/test/integration/main.dart') as ErrorsResult;
76+
77+
expect(errorResult.errors, hasLength(1));
78+
79+
final [error] = errorResult.errors;
80+
81+
expect(error.errorCode.errorSeverity, ErrorSeverity.WARNING);
82+
expect(error.errorCode.name, 'PATTERN_NEVER_MATCHES_VALUE_TYPE');
83+
});
84+
});
85+
});
86+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import 'package:freezed_annotation/freezed_annotation.dart';
2+
3+
part 'finalized.freezed.dart';
4+
5+
@freezed
6+
sealed class FinalizedFoo with _$FinalizedFoo {
7+
factory FinalizedFoo() = _FinalizedFoo;
8+
}
9+
10+
@freezed
11+
sealed class FinalizedBar with _$FinalizedBar {
12+
factory FinalizedBar() = _FinalizedBar;
13+
}
14+
15+
@freezed
16+
sealed class FinalizedMultiple with _$FinalizedMultiple {
17+
factory FinalizedMultiple.a() = FinalizedMultipleA;
18+
factory FinalizedMultiple.b() = FinalizedMultipleB;
19+
factory FinalizedMultiple.c() = FinalizedMultipleC;
20+
}

0 commit comments

Comments
 (0)