-
Notifications
You must be signed in to change notification settings - Fork 29.7k
[flutter_tools] Null safe update packages #99357
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
[flutter_tools] Null safe update packages #99357
Conversation
|
It looks like this pull request may not have tests. Please make sure to add tests before merging. If you need an exemption to this rule, contact Hixie on the #hackers channel in Chat (don't just cc him here, he won't see it! He's on Discord!). If you are not sure if you need tests, consider this rule of thumb: the purpose of a test is to make sure someone doesn't accidentally revert the fix. Ask yourself, is there anything in your PR that you feel it is important we not accidentally revert back to how it was before your fix? Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. |
|
test-exempt: code refactor with no semantic change |
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.
Seems like the null should be handled if the download fails and this returns null.
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.
done
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.
Kind of weird this doesn't just pass in the checksumDependencies map, but the null migration looks fine.
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.
Are you sure packageName! is safe here?
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.
- it works
- it seems to me like a bug we should fix if we don't know the packageName by this point
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.
Is the null bool meaningful, or can this be
| bool? isDevDependency; // Whether this dependency is under the `dev dependencies` section. | |
| bool isDevDependency = false; // Whether this dependency is under the `dev dependencies` section. |
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.
null means we haven't set it yet. This shouldn't be read before it's set, so I think late would make more sense.
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.
made late
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.
I've come around to @jonahwilliams's way of thinking about late (not late final): with few exceptions it should be avoided, except in tests. It usually points to some state needing to be known before the object is initialized, but we're setting it later, leaving the object in an invalid state. For example,
final PubspecDependency? dependency = PubspecDependency.parse(line, filename: filename);
if (dependency != null) { // We got one!
// Track whether or not this a dev dependency.
dependency.isDevDependency = seenDev;could instead be:
final PubspecDependency? dependency = PubspecDependency.parse(line, filename: filename, isDevDependency: seenDev);
if (dependency != null) { // We got one!class PubspecDependency extends PubspecLine {
PubspecDependency(
String line,
this.name,
this.suffix, {
required this.isTransitive,
required DependencyKind kind,
required this.version,
required this.sourcePath,
required this.isDevDependency,
}) : _kind = kind,
super(line);
static PubspecDependency? parse(String line, { required String filename, required bool isDevDependency }) {
...
return PubspecDependency(line, package, suffix, isTransitive: isTransitive, version: version, kind: stripped.isEmpty ? DependencyKind.unknown : DependencyKind.normal, sourcePath: filename, isDevDependency: isDevDependency);
...
final bool isDevDependency;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.
I've come around to @jonahwilliams's way of thinking about
late(notlate final): with few exceptions it should be avoided, except in tests. It usually points to some state needing to be known before the object is initialized, but we're setting it later, leaving the object in an invalid state. For example,final PubspecDependency? dependency = PubspecDependency.parse(line, filename: filename); if (dependency != null) { // We got one! // Track whether or not this a dev dependency. dependency.isDevDependency = seenDev;could instead be:
final PubspecDependency? dependency = PubspecDependency.parse(line, filename: filename, isDevDependency: seenDev); if (dependency != null) { // We got one!class PubspecDependency extends PubspecLine { PubspecDependency( String line, this.name, this.suffix, { required this.isTransitive, required DependencyKind kind, required this.version, required this.sourcePath, required this.isDevDependency, }) : _kind = kind, super(line); static PubspecDependency? parse(String line, { required String filename, required bool isDevDependency }) { ... return PubspecDependency(line, package, suffix, isTransitive: isTransitive, version: version, kind: stripped.isEmpty ? DependencyKind.unknown : DependencyKind.normal, sourcePath: filename, isDevDependency: isDevDependency); ... final bool isDevDependency;
I agree with you that it is a code smell and that doing the parsing in a factory would be better. I would prefer to do refactors in a separate PR, lest I break something in them and it gets reverted. How about if I file a tracking issue to clean up this 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.
Sure. Or don't and we can just fix it if it someday breaks. This is an internal-ish development feature we don't need to spend too much time on it.
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.
Also the isDevDependency one could be done without much refactoring by just adding it to the constructor as in my code snippet. _lockIsOverride is trickier.
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.
Can this be instantiated to avoid the late?
| late bool _lockIsOverride; | |
| bool _lockIsOverride = false; |
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.
by making it late, we ensure that it has been initialized before reading it. Is there a valid reason to read this value before we've parsed the lockfile in parseLock()?
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.
This one is trickier since (without a bigger refactor) it does seem to need to be set after initialization. "Not yet set" could be a nullable boolean, where null means it hasn't yet been parsed. It seems loaded to have a lockIsOverride interface that the consumer of the API needs to understand can only be called after another method? And that, for example describeForFakePubspec calls it, so you need to also never call that method unless you've called parseLock first? It's a confusing contract.
If I were to build it from scratch I might have parseLock not mutate itself, but instead produce a new object that has what we need, and properties for lockLine and lockIsOverride. But I haven't looked too closely at this.
However I get you want to minimize the refactoring, so follow your heart. In either case, it seems like _lockLine and _lockIsOverride should either both be nullable, or both be late.
|
Filed #99928 to track refactoring to remove |
Jasguerrero
left a comment
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.
LGTM
56fff17 to
3684099
Compare
Migrating this file earlier would have caught #98284
Part of #71511