-
-
Notifications
You must be signed in to change notification settings - Fork 286
feat: make generated classes sealed/final #1121
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
|
|
This is valuable. Although I'm wondering whether we need a flag at all. Why not always make the generated class |
|
Happy to hear that 😊 If I would implement that change in the scope of my project I would certainly make |
|
From my side the PR is now "ready", meaning I have no further actionable task on my list. Would be happy to remove a whole bunch of code and just hardcode Tried to make the CI work, but I guess the reason it failed has nothing to do with my changes, so I just added a dependency override for Also I was thinking if And finally, I tried to add a changeset as the bot requested, but I was unable to make it work by following the guide it linked to, so I assume using this package is somewhat outdated? |
You can go ahead and do this change. Only Removing the flag and making the generated classes |
|
You can ignore the changeset bot btw |
af0373d to
2ef0d8b
Compare
|
Removed the configuration part, so basically it now just adds the I think it should be ready to merge. If there is something else that should be changed, just let me know, but throughout the week it might take a while until I can get to it. And since I get the chance to talk to you directly, just wanted to let you know how valuable Freezed and especially Riverpod are to the project I am working on (https://choosy.de if you want to know what people are building with your libs 😉). Since migrating from Bloc to Riverpod the ease of using providers from inside other providers made such a huge difference. Cant wait to get a first glimpse of the wizardry you (hopefully) are going to do with macros once they are stable 😊. |
|
Cool! I'll leave this open for a few more days, but it should be good to go. |
WalkthroughThe pull request introduces a new property for class modifiers in the Freezed package, allowing dynamic inclusion of modifiers like Changes
Sequence Diagram(s)No sequence diagram is provided, as the changes in this pull request do not introduce a new feature or significantly modify the control flow. The changes are focused on adding a new property to control class modifiers and updating the corresponding tests. Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes The changes in this pull request have a moderate level of complexity. The addition of a new property to control class modifiers in the Freezed package requires updates to the class structure, models, and annotations. The comprehensive suite of tests added to validate the behavior of final and sealed classes adds further complexity to the review. While the changes are focused on a specific aspect of the Freezed package, the spread across multiple files and the introduction of new tests make the overall effort moderately complex. Suggested reviewers
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Outside diff range and nitpick comments (4)
packages/freezed/test/integration/finalized.dart (2)
10-13: Consider consolidating similar test casesThis class is structurally identical to
FinalizedFoo. Consider whether having two identical test cases adds value, or if the test coverage could be achieved with a single class.
15-20: LGTM: Comprehensive test case for pattern matchingThis class provides excellent coverage for testing pattern matching with multiple constructors, which is crucial for validating the compile-time warnings mentioned in the PR objectives.
Consider adding test cases for:
- Nested sealed classes
- Generic sealed classes
- Sealed classes with complex property types
These additional cases would help ensure the feature works correctly in more complex scenarios.
packages/freezed/test/finalized_test.dart (2)
8-42: Consider enhancing test coverage for single constructor case.The test effectively verifies the warning for impossible pattern matches. Consider these enhancements:
- Add a positive test case to verify that valid pattern matches don't trigger warnings
- Verify the error message content for better diagnostic coverage
- Assert the error location (line/column) to ensure precise diagnostic reporting
Example enhancement:
test('causes pattern_never_matches_value_type warning when trying to match on pattern that can never match', () async { final main = await resolveSources( { 'freezed|test/integration/main.dart': r''' library main; import 'finalized.dart'; void main() { switch (FinalizedFoo()) { case FinalizedBar(): break; case FinalizedFoo(): break; } } ''', }, (r) => r.findLibraryByName('main'), ); final errorResult = await main!.session .getErrors('/freezed/test/integration/main.dart') as ErrorsResult; expect(errorResult.errors, hasLength(1)); final [error] = errorResult.errors; expect(error.errorCode.errorSeverity, ErrorSeverity.WARNING); expect(error.errorCode.name, 'PATTERN_NEVER_MATCHES_VALUE_TYPE'); + expect(error.message, contains('pattern can never match')); + expect(error.location.lineNumber, 10); + expect(error.location.columnNumber, 10); }); + test('does not warn for valid pattern matches', () async { + final main = await resolveSources( + { + 'freezed|test/integration/main.dart': r''' + library main; + import 'finalized.dart'; + + void main() { + switch (FinalizedFoo()) { + case FinalizedFoo(): + break; + } + } + ''', + }, + (r) => r.findLibraryByName('main'), + ); + + final errorResult = await main!.session + .getErrors('/freezed/test/integration/main.dart') as ErrorsResult; + + expect(errorResult.errors, isEmpty); + });
44-85: Consider refactoring multiple constructors test for better maintainability.While the test effectively verifies the core functionality, consider these improvements:
- Split into separate test cases for each constructor variant
- Extract common error verification logic into a test utility
- Add positive test cases for valid pattern matches
Example refactoring:
+Future<void> expectPatternMatchWarning(String source) async { + final main = await resolveSources( + {'freezed|test/integration/main.dart': source}, + (r) => r.findLibraryByName('main'), + ); + + final errorResult = await main!.session + .getErrors('/freezed/test/integration/main.dart') as ErrorsResult; + + expect(errorResult.errors, hasLength(1)); + final [error] = errorResult.errors; + expect(error.errorCode.errorSeverity, ErrorSeverity.WARNING); + expect(error.errorCode.name, 'PATTERN_NEVER_MATCHES_VALUE_TYPE'); +} group('multiple constructors', () { - test('causes pattern_never_matches_value_type warning when trying to match on pattern that can never match', + test('warns when matching FinalizedMultiple.b against FinalizedBar', () async { - final main = await resolveSources( - { - 'freezed|test/integration/main.dart': r''' + await expectPatternMatchWarning(r''' library main; import 'finalized.dart'; void main() { switch (FinalizedMultiple.b()) { case FinalizedBar(): break; } } - ''', - }, - (r) => r.findLibraryByName('main'), - ); + '''); + }); + test('warns when matching FinalizedMultiple.b against FinalizedMultipleA', + () async { + await expectPatternMatchWarning(r''' + library main; + import 'finalized.dart'; + + void main() { + switch (FinalizedMultiple.b()) { + case FinalizedMultipleA(): + break; + } + } + '''); + }); + test('allows matching FinalizedMultiple.b against FinalizedMultipleB', + () async { + final main = await resolveSources( + { + 'freezed|test/integration/main.dart': r''' + library main; + import 'finalized.dart'; + + void main() { + switch (FinalizedMultiple.b()) { + case FinalizedMultipleB(): + break; + } + } + ''', + }, + (r) => r.findLibraryByName('main'), + ); + + final errorResult = await main!.session + .getErrors('/freezed/test/integration/main.dart') as ErrorsResult; + + expect(errorResult.errors, isEmpty); + });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (4)
packages/freezed/lib/src/templates/concrete_template.dart(2 hunks)packages/freezed/pubspec_overrides.yaml(1 hunks)packages/freezed/test/finalized_test.dart(1 hunks)packages/freezed/test/integration/finalized.dart(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- packages/freezed/pubspec_overrides.yaml
🔇 Additional comments (4)
packages/freezed/test/integration/finalized.dart (2)
1-3: LGTM: Proper Freezed setup
The import and part directive follow the standard Freezed setup pattern.
5-8: LGTM: Simple sealed class test case
This provides a good test case for the simplest possible sealed class scenario with a single constructor.
Let's verify the test coverage for this class:
packages/freezed/test/finalized_test.dart (1)
1-7: LGTM! Well-structured test setup.
The imports and main test group structure are appropriate for testing the sealed/final class behavior.
packages/freezed/lib/src/templates/concrete_template.dart (1)
67-67:
Update minimum Dart SDK constraint due to use of final class and sealed class
The use of final class (line 67) and sealed class (line 89) requires Dart SDK version 2.17.0 or higher. Please update the minimum SDK version in your pubspec.yaml file to >=2.17.0 to ensure compatibility and prevent build errors for users on earlier SDK versions.
Run the following script to verify the current SDK constraints:
Also applies to: 89-89
|
My appologies, I kind of forgot about this PR. Let me see what we can do here. |
|
Friendly Reminder to take a look at this PR 😊 If there is a way I can help testing it, just let me know! |
Is this PR stil relevant as now freezed support "extends" ? |
47f79b8 to
c7dcdbb
Compare
At least the original way how it was implemented will no longer work. Just did a quick rebase from upstream, but have to write quite a few more tests to make sure it works in combination with the new features from release Will take a closer look on the weekend! |
|
Sorry as you can see, I got sidetracked. I'm not sure whether changing the generated code makes sense, when analyzer likely should know already that the After-all, |
|
Actually ignore me, I can see why the analyzer works this way. if you don't mind fixing the conflicts, we can merge this. For now, let's not enable it by default though. I'd rather not make a 4.0 right after a 3.0 |
c7dcdbb to
df45cb2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/freezed_annotation/lib/freezed_annotation.dart (1)
344-379: Fix minor spelling in documentation.In the doc comment, “analzyer” should be spelled “analyzer” for clarity.
- /// so when using them in a switch statement, the analzyer will warn you + /// so when using them in a switch statement, the analyzer will warn you
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
.vscode/settings.json(1 hunks)packages/freezed/lib/src/models.dart(2 hunks)packages/freezed/lib/src/templates/concrete_template.dart(1 hunks)packages/freezed/test/finalized_test.dart(1 hunks)packages/freezed/test/integration/finalized.dart(1 hunks)packages/freezed_annotation/lib/freezed_annotation.dart(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- packages/freezed/lib/src/models.dart
- packages/freezed/test/finalized_test.dart
- packages/freezed/lib/src/templates/concrete_template.dart
🧰 Additional context used
🪛 Biome (1.9.4)
.vscode/settings.json
[error] 9-9: Expected a property but instead found '}'.
Expected a property here.
(parse)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: freezed (packages/freezed, master, get)
- GitHub Check: freezed (packages/freezed_annotation, master, get)
- GitHub Check: freezed (packages/freezed_lint, master, get)
🔇 Additional comments (2)
packages/freezed_annotation/lib/freezed_annotation.dart (1)
87-87: Property addition looks good.Introducing
makeGeneratedClassesFinalis coherent with the PR objectives, and defaulting it to false preserves backward compatibility.packages/freezed/test/integration/finalized.dart (1)
1-63: All new sealed and abstract classes are well-structured.The usage of
@Freezed(makeGeneratedClassesFinal: true)and sealed/abstract class definitions align with the intended design. No issues found.
.vscode/settings.json
Outdated
| "explorer.fileNesting.enabled": true, | ||
| "explorer.fileNesting.expand": false, | ||
| "explorer.fileNesting.patterns": { | ||
| "*.dart": "${capture}.g.dart, ${capture}.freezed.dart", | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove trailing comma to ensure valid JSON.
The trailing comma after "*.dart": "${capture}.g.dart, ${capture}.freezed.dart", causes a parsing error. JSON does not allow trailing commas.
- "*.dart": "${capture}.g.dart, ${capture}.freezed.dart",
+ "*.dart": "${capture}.g.dart, ${capture}.freezed.dart"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "explorer.fileNesting.enabled": true, | |
| "explorer.fileNesting.expand": false, | |
| "explorer.fileNesting.patterns": { | |
| "*.dart": "${capture}.g.dart, ${capture}.freezed.dart", | |
| } | |
| { | |
| "explorer.fileNesting.enabled": true, | |
| "explorer.fileNesting.expand": false, | |
| "explorer.fileNesting.patterns": { | |
| "*.dart": "${capture}.g.dart, ${capture}.freezed.dart" | |
| } | |
| } |
🧰 Tools
🪛 Biome (1.9.4)
[error] 9-9: Expected a property but instead found '}'.
Expected a property here.
(parse)
9994dbc to
10dd80e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
packages/freezed/test/finalized_test.dart (2)
165-201: Test name appears inconsistent with the expected behavior.The test name on line 167 states "causes pattern_never_matches_value_type warning" but the assertion on line 198 expects no errors (
isEmpty). This seems inconsistent and could mislead readers.- 'causes pattern_never_matches_value_type warning when trying to match on pattern that can never match', + 'does not cause pattern_never_matches_value_type warning when trying to match on pattern that can never match',
6-7: Consider adding documentation about the feature being tested.Adding a brief comment at the top of the test group to explain the purpose of making classes final/sealed would improve clarity for future maintainers.
void main() { + // Tests for the feature that marks generated classes as 'final' for concrete classes + // or 'sealed' for abstract classes to enable compile-time warnings for impossible pattern matches. group('marks generated classes as final', () {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
.vscode/settings.json(1 hunks)packages/freezed/lib/src/models.dart(2 hunks)packages/freezed/lib/src/templates/concrete_template.dart(1 hunks)packages/freezed/test/finalized_test.dart(1 hunks)packages/freezed/test/integration/finalized.dart(1 hunks)packages/freezed_annotation/lib/freezed_annotation.dart(2 hunks)
✅ Files skipped from review due to trivial changes (1)
- .vscode/settings.json
🚧 Files skipped from review as they are similar to previous changes (4)
- packages/freezed/lib/src/templates/concrete_template.dart
- packages/freezed_annotation/lib/freezed_annotation.dart
- packages/freezed/lib/src/models.dart
- packages/freezed/test/integration/finalized.dart
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: freezed (packages/freezed_annotation, master, get)
- GitHub Check: freezed (packages/freezed_lint, master, get)
- GitHub Check: freezed (packages/freezed, master, get)
🔇 Additional comments (2)
packages/freezed/test/finalized_test.dart (2)
1-203: Well-structured and comprehensive test suite for the new feature.This test file thoroughly validates the behavior of generated classes marked as
finalandsealedin various scenarios. The tests verify that the Dart analyzer correctly raises warnings when pattern matching with incompatible types, which is exactly what the PR aims to achieve. Good job covering multiple scenarios including sealed classes, abstract classes, and their interactions with single/multiple constructors and inheritance.
133-163: Interesting behavior difference between sealed and abstract classes.This test confirms that for abstract classes with a single constructor, no warnings are raised even when matching patterns that cannot match. This differs from sealed classes where warnings are issued. This is important behavior to document as it may not be immediately obvious to users.
10dd80e to
0bd569a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
packages/freezed/test/finalized_test.dart (1)
135-135: Minor typo in test description.There's a missing apostrophe in "doesnt".
- 'doesnt cause pattern_never_matches_value_type warning when trying to match on pattern that can never match', + 'doesn\'t cause pattern_never_matches_value_type warning when trying to match on pattern that can never match',
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
.vscode/settings.json(1 hunks)packages/freezed/lib/src/models.dart(2 hunks)packages/freezed/lib/src/templates/concrete_template.dart(1 hunks)packages/freezed/test/finalized_test.dart(1 hunks)packages/freezed/test/integration/finalized.dart(1 hunks)packages/freezed_annotation/lib/freezed_annotation.dart(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
- .vscode/settings.json
- packages/freezed/lib/src/templates/concrete_template.dart
- packages/freezed_annotation/lib/freezed_annotation.dart
- packages/freezed/lib/src/models.dart
- packages/freezed/test/integration/finalized.dart
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: freezed (packages/freezed_annotation, master, get)
- GitHub Check: freezed (packages/freezed_lint, master, get)
- GitHub Check: freezed (packages/freezed, master, get)
🔇 Additional comments (2)
packages/freezed/test/finalized_test.dart (2)
1-204: Well-structured tests for the new final/sealed class feature.The test suite thoroughly validates the expected behavior of making generated Freezed classes either
final(for concrete classes) orsealed(for abstract classes). The tests correctly verify that:
- Sealed classes with a single constructor raise warnings for non-matching patterns
- Sealed classes with superclasses raise warnings for non-matching patterns
- Sealed classes with multiple constructors raise warnings for non-matching patterns
- Abstract classes (both with single and multiple constructors) don't raise warnings for non-matching patterns
This comprehensive coverage ensures the feature works as intended and will help catch pattern matching issues at compile time.
1-5: Imports look good.The test imports the necessary analyzer packages to check for compile-time errors and warnings, which is appropriate for testing this feature.
650feec to
2bdcffb
Compare
|
Updated the implementation, should be ready to review
To really test it, I integrated it with the project I am working on (which contains hundreds of freezed classes). However, due to RevenueCat/purchases-flutter#1288 I am currently stuck on version |
|
Thinking about it, I wonder if this should be slightly generalised. We could have I won't really want to move the goalpost forever though. But it feels like this change makes sense. Thought? |
|
I do like the idea of using a more general API. Of course we can just use a @Freezed(classModifiers: [FreezedClassModifier.Final])
abstract class Foo with _$Foo {
factory Foo() = _Foo;
}Might be a little more developer friendly, because one would get autocomplete. However, due to all modifiers being keywords we either have to use UpperCamelCase (like in the example) or something like When it comes to which modifiers should be supported, I think everything that makes the class effectively abstract is problematic, for example: @Freezed(classModifiers: [FreezedClassModifier.Sealed])
abstract class Foo with _$Foo {
factory Foo() = _Foo;
// ^^^^
// The redirecting constructor 'Foo' can't redirect to a constructor of the abstract class '_Foo'.
}So that would leave the modifiers That being said, I do have one "concern" with that change. Allowing more modifiers might lead to people using Freezed in unintended ways, which in turn might make future features more difficult to implement without breaking stuff. However, that is just a gut feeling that I have, and I can not support it with examples. If I would have to make the decision, I would probably start by allowing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
packages/freezed/lib/src/models.dart (1)
1330-1358: Consider refactoring validation for better maintainabilityThe validation logic works correctly, but consider a more data-driven approach for defining valid modifier combinations.
- void throwIfInvalid(Element? element) { - switch (this) { - case []: - case [FreezedClassModifier.Base]: - case [FreezedClassModifier.Base, FreezedClassModifier.Mixin]: - case [FreezedClassModifier.Mixin]: - case [FreezedClassModifier.Final]: - return; - - default: - throw InvalidGenerationSourceError( - 'invalid combination of class modifiers: ${map((modifier) => modifier.lexeme).join(' ')}', - element: element, - ); - } - } + // Define valid combinations as a static const Set for better maintainability + static const Set<Set<FreezedClassModifier>> validCombinations = { + {}, // Empty set + {FreezedClassModifier.Base}, + {FreezedClassModifier.Base, FreezedClassModifier.Mixin}, + {FreezedClassModifier.Mixin}, + {FreezedClassModifier.Final}, + }; + + void throwIfInvalid(Element? element) { + final thisSet = toSet(); + if (!validCombinations.contains(thisSet)) { + throw InvalidGenerationSourceError( + 'invalid combination of class modifiers: ${map((modifier) => modifier.lexeme).join(' ')}', + element: element, + ); + } + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
packages/freezed/lib/src/models.dart(6 hunks)packages/freezed/lib/src/templates/concrete_template.dart(1 hunks)packages/freezed/test/integration/finalized.dart(1 hunks)packages/freezed_annotation/lib/freezed_annotation.dart(3 hunks)packages/freezed_annotation/lib/freezed_annotation.g.dart(2 hunks)packages/freezed_annotation/test/freezed_test.dart(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- packages/freezed/lib/src/templates/concrete_template.dart
- packages/freezed_annotation/test/freezed_test.dart
- packages/freezed/test/integration/finalized.dart
🔇 Additional comments (10)
packages/freezed_annotation/lib/freezed_annotation.dart (3)
87-87: Well-designed API addition for class modifiersThe new
classModifiersparameter in theFreezedconstructor is properly initialized with a default empty list, ensuring backward compatibility while introducing new functionality.
344-381: Excellent documentation with practical examplesThe documentation for the
classModifiersproperty is comprehensive and includes clear examples demonstrating how adding the 'final' modifier can lead to compile-time warnings for impossible pattern matches. This addresses the main goal of the PR to enhance type safety through sealed/final classes.
575-580: Good enum design following Dart conventionsThe
FreezedClassModifierenum follows standard Dart conventions with proper annotation for field renaming. The available options (Final,Base,Mixin) cover the most common class modifiers in Dart.packages/freezed_annotation/lib/freezed_annotation.g.dart (2)
26-29: Proper deserialization of the new classModifiers fieldThe deserialization logic correctly handles the nullable list, maps each entry to the enum value, and provides an empty list as default value, consistent with the declaration in the
Freezedclass.
40-44: Well-structured enum mapping for serializationThe mapping between enum values and their string representations uses correct Dart syntax for the modifiers ('final', 'base', 'mixin'). This ensures proper serialization/deserialization of the
FreezedClassModifierenum.packages/freezed/lib/src/models.dart (5)
1070-1078: Clean implementation of the classModifiers getterThe getter appropriately handles empty modifier lists and formats non-empty lists with proper spacing for inclusion in Dart class declarations. This approach ensures seamless integration with existing code generation patterns.
1116-1116: Good integration of modifiers into ClassConfigThe
modifiersfield is properly added to theClassConfigconstructor parameter list, ensuring it's initialized when the config is created.
1141-1144: Robust validation pipeline for class modifiersThe implementation correctly normalizes and validates the modifiers before storing them, preventing invalid combinations that would lead to compilation errors in the generated code.
1207-1235: Thorough implementation of modifier decoding from annotationsThe decoding logic handles null values appropriately and correctly extracts enum values from the Dart object representation, maintaining consistency with existing patterns in the codebase.
1320-1328: Simple and effective lexeme conversionThe extension method provides a clean way to convert enum values to their string representations, following best practices for extension methods in Dart.
|
Did a quick experiment on it. Let me know what you think of it. Not sure why CI is currently failing (something with Dart Format 🤔), tests should still be passing though. |
|
Ah, I kind of forgot that we wouldn't be able to mark the generated class So we'd only gain Do you mind reverting your last commit? I'll merge this then. I've already reviewed the previous changes. |
ad558e6 to
5653ad5
Compare
|
Didn't explicitly mention it, but from my side this PR is ready. Did a revert commit for the last one, but if you want a clean history you can just squash everything into a single commit when merging |
5653ad5 to
d6d501e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
packages/freezed_annotation/test/freezed_test.dart (1)
159-187: Add negative-type and round-trip tests for make_generated_classes_finalTo strengthen our
.fromJsoncoverage:• Add a failure-path test to verify that passing a non-bool value for
make_generated_classes_finalthrows a cast error:test('.fromJson (invalid make_generated_classes_final) throws', () { expect( () => Freezed.fromJson(<Object?, Object?>{ 'make_generated_classes_final': 42, }), throwsA(isA<TypeError>()), ); });Place this alongside the existing explicit-override tests.
• Optionally, add a round-trip serialization check to guard against regressions in your generated JSON API:
test('Freezed annotation round-trip (fromJson ⇄ toJson)', () { final original = Freezed.fromJson(<Object?, Object?>{ 'fallback_union': 'test', 'make_generated_classes_final': true, // …other flags… }); final json = original.toJson(); final same = Freezed.fromJson(json); expect(same.makeGeneratedClassesFinal, isTrue); expect(same, equals(original)); });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (7)
packages/freezed/lib/src/models.dart(2 hunks)packages/freezed/lib/src/templates/concrete_template.dart(1 hunks)packages/freezed/test/finalized_test.dart(1 hunks)packages/freezed/test/integration/finalized.dart(1 hunks)packages/freezed_annotation/lib/freezed_annotation.dart(2 hunks)packages/freezed_annotation/lib/freezed_annotation.g.dart(1 hunks)packages/freezed_annotation/test/freezed_test.dart(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
- packages/freezed/lib/src/templates/concrete_template.dart
- packages/freezed/test/finalized_test.dart
- packages/freezed/lib/src/models.dart
- packages/freezed_annotation/lib/freezed_annotation.dart
- packages/freezed_annotation/lib/freezed_annotation.g.dart
- packages/freezed/test/integration/finalized.dart
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: freezed (packages/freezed, stable, get)
🔇 Additional comments (3)
packages/freezed_annotation/test/freezed_test.dart (3)
154-157: Defaults for new and existing switches look correctThe assertions for addImplicitFinal=true, genericArgumentFactories=false, and makeGeneratedClassesFinal=null align with the intended tri-state default (null) for the new option. LGTM.
262-265: freezed defaults: asserting null is appropriateAsserting that the annotation constant
freezed.makeGeneratedClassesFinalremains null by default is consistent with the tri-state behavior and backwards compatibility goals. LGTM.
270-270: unfreezed defaults: null expectation is consistentValidates parity with the
freezeddefaults for the new flag. Looks good.
|
@rrousselGit Just rebased this one onto the latest |
Adds a new configuration parameter
finalize: boolthat allows to mark generated classes asfinalin the case of concrete classes orsealedin the case of abstract classes. It is disabled by default.Enabling it allows the Dart analyzer/compiler to report patterns that will never match the type of a freezed class at compile time.
For example:
Why do I think this is a good idea?
I have recently had an issue in one of my projects where I have a
@freezed sealed class DataFromApiand I introduced a new@freezed sealed class DataFromDatabasethat was intended to be used in many but not all cases whereDataFromApiwas being used so far.It was a normal change, but I messed up due to the heavy use of pattern matching like
The changes mostly looked like this
I was expecting the Dart analyzer/compiler to yell at me when changing the type of
datain the example above toDataFromDatabase, but that was not the case. So I had to find all the now broken patterns by hand. Naturally I overlooked one case and the tests didn't catch it, so I got a Bug in production 😅.IMHO the Dart analyzer/compiler should be able to help me in this case no matter if I use freezed or not, since the classes in question were marked as
sealed. So I opened an issue over there: dart-lang/sdk#56616However, since this might never be implemented or maybe is completely impossible, I thought it should be possible and rather simple to make freezed help me out in that case. All that needs to be done is mark the generated classes as
finalorsealed, and then the analyzer will step in and report any pattern that can never match at compile time.What needs to be done before it might be merged?
Good question, I think first of all someone needs to decide if this even is a good idea or if it would mess things up. In case it should be merged, I assume a few more tests need to be written to make sure the
finalizeflag works well with other configuration options. However, so far I have a hard time figuring out what even might break due to this change, so I would need help to decide what special/edge cases to consider when writing tests.Summary by CodeRabbit
New Features
Tests