-
Notifications
You must be signed in to change notification settings - Fork 170
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
implement: invalid_case_patterns
#4044
Comments
Bike-shedding names w/ @munificent, maybe:
Input welcome! /cc @bwilkerson |
Are we planning any 2.19 releases between now and 3.0? If not, then it isn't clear to me when this lint would become available for anyone to use. If we are, then I'd recommend making it a warning rather than a lint because getting users to widely enable a new lint before 3.0 might be difficult. @keertip For ideas for quick fixes in 3.0 that we should have to help users migrate their pre-3.0 code, regardless of whether we have a lint before 3.0. |
Sorry, I somehow missed this comment at the end:
We might still want to make this a warning and not a lint, but maybe that would be too noisy. |
That's my worry too. Especially internally. Maybe this is a candidate for a lint that after some bake-time graduates to a warning? |
Can you expand on that? I don't think think there is an issue there. I think this would be good as a default-on Warning. But they take a lot of work. I have probably regretted most default-on warnings I've implemented. 🤷 maybe a Lint would be more pragmatic in view of our current CI burdens. |
Just a gut sense. @munificent did some analysis and found that there were in fact some breaking cases in the wild and if they'd be anywhere, I'd expect them internally! All of that said,
That's a part of my concession too. In general it seems like ecosystem impact is nearly always more than we anticipated. |
I was actually thinking about it potentially being too breaking for external users if we made it a warning. We know there's code that would break, but I don't remember what percentage of code would be impacted. It might be too high, necessitating making it a lint. |
invalid_case_patterns
Putting aside the lint vs. warning question for a minute, there's another question I want to ask: This isn't the only breaking change in 3.0, there are at least two others (at least potentially):
Do we want to have a separate check for those cases, or do we want a single 'not_forward_compatible' check that covers all of the cases? I think we can bulk fix all of the pattern and annotation issues prior to migration. I don't think we can offer a bulk fix for the mixin issue, and in some cases (where adding |
The thing about patterns is that there exists code that will compile both before and after but will mean different things. So, more than just a breaking change. (Is there anything else like that?) |
Actually, the transition to null-safety was kind of like that. An application can have libraries that opt back to a pre-null safety state. We were/are in a state where For null-safety we handled the problem by defining the semantics of mixed-mode programs. We needed to do that because the effects of the language choice could be seen outside of a library. In this case the effects of the language level are local to a single library. My ideal is that when a package is migrated to 3.0, users would run I know that my ideal process won't work internally, and I expect that we'll need to also have a plan for how we're going to make the transition internally. Your idea of a transition period might well be the right approach. Whatever approach we take, though, this lint could minimally be part of that discussion by allowing us to measure ahead of time just how much code there is that will be broken. |
I don't see how it's possible to rely on |
If the lint is available only in a release that coincides with 3.0, then we'll be expecting users to download that, mark every file with language version 2.19, run dart fix, then unmark the files. Then yes, dart fix can work. I'm just saying I wish there was a good way to access this fix well in advance of 3.0 |
Preserve the current semantics. Always. In this case by adding |
My understanding is that the 3.0 release will allow users to set the minimum language version to any version greater than or equal to 2.12. They won't be required to immediately set the language version to 3.0. The only requirement we're placing is that you can't use a 3.0 (or higher) version of the SDK on code that hasn't yet been migrated to 2.12 (null-safety). I don't know whether that helps us internally, but it should ease the migration path externally. |
Thanks for all the feedback! A first cut is done and ready to land and integrate into the SDK so we can do broader testing (and associate quick-fixes). Pre 3.0, we'll want to update the docs and improve the error messages. (Will add tasks above.) |
See: dart-lang/linter#4044 Change-Id: Ifdf36b66221e322f262ff683eb46073cb69bf12b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/281784 Reviewed-by: Keerti Parthasarathy <[email protected]> Commit-Queue: Phil Quitslund <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]>
Closing this in favor of sub-issues for the remaining work. |
☂️ Tasks
invalid_case_patterns
#4047invalid_case_patterns
#4172invalid_case_patterns
docs when there's a dart.dev doc link #4055Description
The new patterns language feature in Dart is a breaking change to a very small number of switch cases. To help users migrate and mitigate the breakage, I think it would help to have a lint that warns on switch cases in libraries whose language version is 2.19 or lower if the case will become an error or have its semantics change when the library is upgraded to 3.0.
Almost all existing switch cases are fine as far as I can tell. If the case is one of these constant expressions:
_
null
as
expression containing any of the aboveThen it behaves exactly the same with patterns as it did before. There’s no breaking and nothing to migrate. Looking at a corpus of open source code, that covers >99.9% of all switch cases.
Some valid switch cases today will become compile errors in Dart 3.0:
identical()
.!
,-
, or~
(except for-
before an integer literal, which is a valid pattern and is fine)!=
,==
,&
,|
,^
,~/
,>>
,>>>
,<<
,+
,-
,*
,/
,%
,<
,<=
,>
,>=
,??
.?:
.length
calls on stringsis
andis!
expressionsExamples of all of them:
Some valid switch cases today are also syntactically valid patterns, but the pattern matching behavior may be different from the current constant equality behavior. They are:
List and map literals. A list or map literal can appear as a constant in a case:
Currently, the case will only match if the incoming value has the same identity as the constant. So:
With patterns, a list or map literal becomes a list or map pattern. The pattern destructures the incoming object and matches if the subpatterns all match. In other words, list and map pattern match using something more like deep equality.
With Dart 3.0, the above program prints "Matched" twice.
Constant constructor calls. Similar to collections, you can construct a constant instance of a class in a case:
Again, like collections, the case currently only matches if the incoming value has the same identity. With patterns, the
Point(...)
syntax becomes an object pattern that destructures the incoming point, calls thex
andy
getters on it and then matches the results of those against the corresponding subpatterns.In this example, it will print "Matched" twice.
Note that object patterns only support named fields. So any constant constructor in a case today that has positional arguments will become a compile-time error when parsed as a pattern. A constant constructor call with no arguments is a valid object pattern and only does a type test:
When interpreted as a pattern, this prints "Matched" twice.
Wildcards. Today, you can have a constant named
_
:With patterns, the identifier
_
is treated as a pattern that matches all values, so this prints "Matched" twice.Logic operators. The logic operators
&&
and||
are valid constant expressions and also valid patterns. As a constant expression, they simply evaluate the expression to a boolean and match if the incoming value is equal to that boolean value. So:With Dart 3.0, these become patterns. The above example prints "Did not match" twice because no boolean value can be both true and false.
Lint
All of the cases whose behavior might change can be detected syntactically. If the constant expression in a case is any of the above expression forms, then the lint should warn the user that the code will become an error or (worse) possibly behave differently in Dart 3.0.
Quick fixes
Many of these can be mechanically changed to something that is valid both in Dart today and valid and means the same in Dart 3.0.
Parenthesized expressions: Provided the inner expression is one that's not broken in Dart 3.0, just discard the parentheses.
List literals, map literals, set literals, and constant constructor calls: Put
const
before the literal or call. This turns it into a constant pattern which preserves the current behavior:We'll probably want to tweak the "unnecessary_const" lint to not treat these
const
modifiers as unnecessary now.Wildcards: Rename the constant from
_
to something else. Since the name is private, this can be done locally in the library without affecting other code.Everything else: For any other invalid expression, you have to hoist the expression out into a new named constant. For example, if you have code like this:
It can be fixed by changing it to:
This might be a little harder to generate an automatic fix for since it will have to conjure up some name for the constant. But expressions like this are very rare, so it's probably sufficient for the lint to just warn and ask the user to manually fix it.
I think it's still useful to add this lint even if it only ships in Dart 3.0. Because even when Dart 3.0 comes out and users upgrade to it, their libraries will still by default be on some language version < 3.0 and the lint will fire.
The text was updated successfully, but these errors were encountered: