-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Proposal <switch_on_type
>
#59546
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
Comments
Here is a CL for this https://dart-review.googlesource.com/c/sdk/+/413600. |
@devoncarew I think you are someone to ask about this being added to core/recommended. Any takes on this suggestion? The CL suggests replacing the switch in |
Hi! We have a process; it's mostly just creating an issue against package:lints. We then periodically review the suggestions against our rubric for the two sets. https://github.com/dart-lang/core/tree/main/pkgs/lints#evolving-the-lint-sets For this specific lint, I'd wonder how often this might occur in practice, the severity for people w/ the 'bad' (pre-lint) code, how long the lint had existed (re: stability / maturity of the impl.), whether it had been tried against larger codebases, ... |
Thanks! Note: There is no mention of the rubric at the link. |
It's mostly here: https://github.com/dart-lang/core/tree/main/pkgs/lints#lint-sets, though that section could certainly be expanded (why would something be included? why not?). |
About your questions, most of them I'm unsure I can answer but:
This is not yet merged, but it is a lint that would encourage the usage of the "new" pattern feature so I'd say that should have some weight.
This is a really simple impl (314 lines added, 0 removed), if the
It hasn't up until now. Here are the testsWould trigger: void f1(num i) {
// Triggers
(switch (i.runtimeType) {
const (int) => null,
const (double) => null,
_ => null,
});
// Triggers
switch (i.runtimeType) {
case const (int):
break;
case const (double):
break;
default:
break;
}
}
void f2(MyClass i) {
// Triggers
(switch (i.runtimeType) {
const (MyClass) => null,
_ => null,
});
// Triggers
switch (i.runtimeType) {
case const (MyClass):
break;
default:
break;
}
}
class MyClass {
@override
Type get runtimeType => int;
} Would not trigger: void f(num i) {
(switch (i) {
int _ => null,
double _ => null,
});
switch (i) {
case int _:
break;
case double _:
break;
}
}
// @dart = 2.19
void f2(num i) {
switch (i.runtimeType) {
case int:
break;
case double:
break;
default:
break;
}
} I'm only asking you because at the CL I got these messages: @srawlins wrote:
@pq wrote:
So after the last comment, I came over to see if I could find who should I ask for opinions. |
Thanks for following up, @FMorschel. Sorry if there's any frustration. This has come up a bunch and we've not done a great job of setting expectations on what it takes for a contribution to get accepted (and there is some nuance). To help with lints, in particular, we did create a bit of process since we were getting a lot of contributions and that's what the notion of status is meant to help with. Some of that is captured in this doc here. As for this proposal in particular, it would be great to get some input from folks like @natebosch , @lrhn and @munificent. Thanks for your patience in any event. We've got a lot of balls in the air! |
We already warn about such switch cases if you're not switching on a I think it's probably a good lint. You shouldn't be using So +1 from me. |
I was skeptical that this would come up very often, but I do see a number of occurrences on github. (Also a few I think this is worth landing. |
I could test this too, I think. Not that hard. I agree it would also help |
I've added that to the lint. Please take a look at the tests to help me find any cases I missed testing, but I think we are covered. Once you all agree on landing this we can review the examples to add this as a bad one. Edit: to be very explicit. Only calls to |
|
Absolutely. Great catch. I'd be inclined to rename to Thank you! |
avoid_switch_on_runtimeType
>switch_on_runtimeType
>
Only because @bwilkerson seems a bit confused about this lint on the CL comments:
|
I don't think I was confused about the lint. I think I was asking whether we had the right semantics for it. I asked in the wrong place, though. I should have asked here rather than on the CL. Some of the comments in this issue are directly related to the use of void f(Type t) {
switch (t) {
case int: break;
}
} Why would we want to allow that when we disallow the following? void f(Object o) {
switch (0.runtimeType) {
case int: break;
}
} |
To be fully fair, we can do: class MyCustomType implements Type {} But I'm aware nobody would do this (no reason for it, I don't think).
I'm completely open to reworking this to make this work if you think this is the best course of action. I've never seen anyone do either my above example or your last, but I think it would be reasonable to trigger on anything with a static type of If you want to discuss this internally or here and tell me the decision I'll work towards making it meet the definitions you prefer. If you think no discussion is needed, please only say so. |
... or a type variable. I've seen code like ... Foo<T>(...){
switch (T) {
case String: ...
...
}
} And code that tried to parse I also believe the |
I would also like to know the team opinion on the following for me to rephrase the current message: |
I think we can stick to one consistent messaging for now. And if we change the messaging to be more prescriptive, we can do that wholesale, so that we remain consistent. |
Sorry, I'm not sure I ever got a definitive answer if should I make this trigger for any Sorry if this was answered and I didn't understand. I am concerned, actually, with @lrhn's example of doing |
I also find @lrhn 's example motivating. It seems like it should not be too hard to expand to look at any switch expression with type Type. Except the name will have to change, so much code re-organizing in the CL. And maybe change name to "switch_on_type". CC @munificent who specced patterns and considered switches and types. |
Changing to "switch_on_type" sounds good to me. Once we have an implementation it might be worth running against a big corpus like our internal code to see if anything surprising comes up. |
I've updated the CL to trigger for anything assignable to
About these cases, I added a warning to any string interpolation that has this pattern. I think it is safer to assume the user added white spaces or something manually and tests it everywhere than to have a false-negative for things like One case I didn't know how to handle was something like: void f(Object? o) {
final type = o.runtimeType.toString();
(switch (type) {
'int' => null,
_ => null,
});
} If anyone knows of a lint that handles cases like this, I'd love to take a look at it to implement this here too. |
Meh, I wouldn't worry about this one. |
Even if we thought this case was worth worrying about, handling it would require flow analysis, which needs to be a separate arc of work (assuming that we ever get around to doing it). |
I need one last help here. There is a
But none of the suggested commands through my edits updated this file. I could obviously just place the lint name at the right position alphabetically, but I'd like to know if someone knows what I should run. I was expecting Also, as a side-note, maybe this failing test could tell us what to run here? |
Yeah, the comment really ought to say what to run to generate the file. Or the file ought to not say "generated" at all, because I don't think there's a generator for this file. It's possible that we lost the generator when moving the linter package into the sdk repository, or we might have never had one. I'm not sure. If I were in your place I'd just hand-edit the file and call it good. |
Uh oh!
There was an error while loading. Please reload this page.
<
switch_on_type
>Description
Avoid using switch statements on a
Type
object. Instead, use type checks with pattern-matching expressions likeString _
orint()
.Details
Using a switch on a
Type
can lead to bad code that is prone to breaking when class hierarchies change or new types are added. This approach is not type-safe and doesn't take advantage of Dart’s strong static typing system. Instead, it's better to leverage more robust alternatives such as pattern matching (switch with exhaustiveness over sealed types) to handle multiple types in a type-safe manner.Kind
Guard against errors: This lint guards against the misuse of a switch on
Type
s, promoting more maintainable and safer code.Bad Examples
Good Examples
Discussion
The inspiration comes from #56763 and #59087.
This is motivated by cases like the SDK issue above where at the Discord discussion previous to it the user was using
runtimeType
on the switch creating some confusion as to how to use switch patterns. Since in most cases, the user is not trying to actually switch on theruntimeType
anymore now that we have expression matching like the above, it should probably be safe for us to call out on that.If this lint passes, I believe it should probably be under Effective Dart.
Discussion checklist
The text was updated successfully, but these errors were encountered: