Skip to content
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

Consider cast_nullable_to_non_nullable for core lints #28

Open
stuartmorgan opened this issue May 12, 2021 · 11 comments
Open

Consider cast_nullable_to_non_nullable for core lints #28

stuartmorgan opened this issue May 12, 2021 · 11 comments
Assignees

Comments

@stuartmorgan
Copy link

stuartmorgan commented May 12, 2021

See discussion in dart-lang/language#1547. Casting Foo? to Bar (vs Bar?) with as is an easy mistake to miss.

@goderbauer
Copy link
Contributor

For reference, the lint is enabled in the flutter framework code base and has successfully caught some issues there, I believe.

@mit-mit mit-mit added this to Triage backlog in Lints triage Jan 10, 2022
@mit-mit
Copy link
Member

mit-mit commented Jan 11, 2022

Should update GOOD to also include this 2nd case:

class A {}
class B extends A {}

A? a;
var v = a as B?;

cc @pq

@mit-mit
Copy link
Member

mit-mit commented Jan 11, 2022

Would like to undetstand how this affects Google3 before making a choice.

@mit-mit mit-mit closed this as completed Jan 11, 2022
@mit-mit mit-mit reopened this Jan 11, 2022
@mit-mit mit-mit moved this from Triage backlog to Needs work/investigation in Lints triage Jan 11, 2022
@goderbauer
Copy link
Contributor

@mit-mit Do you know somebody who could drive trying this out in g3?

@mit-mit
Copy link
Member

mit-mit commented Jan 14, 2022

@pq are you looking at that?

@pq
Copy link
Member

pq commented Jan 14, 2022

I can take a look. 👍

@pq pq self-assigned this Jan 14, 2022
@pq
Copy link
Member

pq commented Jan 17, 2022

I did a run and sent results out to a handful of folks for further review. At first blush the results seem valuable to me. If you want access (and are a Googler, sorry), let me know!

copybara-service bot pushed a commit to dart-lang/sdk that referenced this issue May 17, 2023
See: dart-lang/lints#28

Change-Id: Ic0dac81467a3a3fbe7f7bf805ee59e3844edb6a1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/303681
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Phil Quitslund <pquitslund@google.com>
@devoncarew devoncarew changed the title Consider 'cast_nullable_to_non_nullable' for core lints Consider cast_nullable_to_non_nullable for core lints Jun 1, 2023
@lrhn
Copy link
Member

lrhn commented Jun 7, 2023

I'm not strongly convinced that this lint is a win.
It may help now, during nullability-migration, but I'm less convinced that it's a win in the long run, and that there won't be a significant number of false positives.

I deliberately write T? current; .... => current as T; to remove the extra nullability from something where I don't know if it's nullable or not. If that's not allowed, we have a problem.
(The documentation talks about casting a nullable value to non-nullable, it doesn't say anything about potentially nullable or non-nullable. It's not clear what it does.)

If I cast from int? to int, it seems pretty clear-cut that that's what I want (but I agree that I should use ! for that).

If I cast from num? to int, it maybe be because I know something, and doing x! as int will look completely ridiculous.

So, should this be more about unrelated casts or down-casts?

(All in all, not getting my vote for adding to core lints.)

@stuartmorgan
Copy link
Author

If I cast from num? to int, it maybe be because I know something

But it may also be that that you meant to cast num to int and didn't realize that you had a nullable value. Or that you meant to cast to int? but forgot the ?. That is exactly the sort of case that has caused actual bugs.

and doing x! as int will look completely ridiculous.

Why? Clearly indicating that you know that you are both removing nullability and changing the base type doesn't seem ridiculous to me, since as a reader I can be confident that both casts are intentional, vs the case above where I can't.

@lrhn
Copy link
Member

lrhn commented Jun 8, 2023

I'm generally concerned about as, since it allows anything. And I'm convinced that nullability is that much of an orthogonal concept in typing that it requires special handling.

That said, if x! as int is guaranteed to be as efficient as x as int, and the lint is properly specificed so I know what it does with potentially nullable types on either or both sides, I think it's a reasonable lint to have for people who want it.

I'd still be against including it in the core lints. To get there, I'd want it to either:

  • Catch something which is very likely a mistake. As in >50% of occurrences are mistakes. Not just that a mistake is possible. And not triggering on something that is the obvious thing to write if you happen to know that x does indeed contain an int. Or
  • Catch something which is a very dangerous, subtle and hard to find mistake. Not something which will just throw the first time a null value gets there, I'd expect testing to catch that. (Not awaiting a future made it, because it's invisible at the point where it happens, and an unhandled error will be reported somewhere else entirely.) Or
  • Something that is a very common mistake. Even if only way less than 50% of occurrences are wrong, there being lots of them numerically makes it worth to avoid the error. I haven't seen enough occurrences of this bug to consider that warranted.

Yes, you can make mistakes with as. Lots of them, it's as unsafe as dynamic, and optimally you should never use as. But as is like an assertion, which should never fail, you only use it when you know you're right, and in those cases, it's the shortest expression which does what you need of it. Writing assert(x is int); ... (x is int ? x : (throw "Not an int")) isn't adding to readability either, and is just as unsafe.

Which raises the question, if num? x = ...; ... x as int is bad, why isn't ... x is int ? ... : someFailure(). Why not require x != null && x is int there too?

@stuartmorgan
Copy link
Author

I'm generally concerned about as, since it allows anything.

Yes, that's why my preferred solution would be dart-lang/language#1547. That didn't go anywhere though, and if we're going to be stuck with having to use an operator that can do anything, even the things I did not intend to do, a default lint was the next best option.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: More investigation needed
Lints triage
Needs work/investigation
Development

No branches or pull requests

5 participants