You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The analyzer gives a warning if you write int x = ...; x?.toString() or x ?? 42, an "unnecessary null-aware" warning because the receiver cannot be null.
For a not-non-nullable extension type with a non-nullable representation type, like:
Should we warn about or even disallow using ?. (or other null-aware operators like ?? and ??=) in any of these cases, or not?
NeverNull
The vn?.foo should warn. The receiver is definitely non-nullable, and can never be null. Same as today, nothing new or interesting, it's just some non-nullable type.
NotNull
The nn?.foo is unnecessary, today, but if someone changes NotNull's representation type to Object? tomorrow, then the ?. will start doing something.
That change may be breaking, or it may not (if the author has documented that you should not assume anything about the representation type, then your code getting broken by making assumptions is just your own fault.)
Which means it's probaby safest to treat it the same as MaybeNull.
MaybeNull
For mmn?.foo and mny?.foo, the ?. is unnecessary for calling foo, you can do .foo directly.
But it does change the behavior, since mny?.foo is null and mnn?.foo is "foo".
If we think of ?. as an operator on T? to conditionally call members on T, then it's unnecessary of MaybeNull. And we should possibly warn about it being unnecessary.
But if we think of v?.foo as just a shorthand for v == null ? null : v.foo, with no special intent, then it works as intended here.
I think assuming "no intent" is a mistake.
The use here may not be desired since the ?. is not really working on the MaybeNull type, but on the underlying representation value, effectively breaching abstraction.
So maybe we do want a warning.
But then, we do allow, without any warning, x?.foo when x has a type variable type X with a nullable bound. That's a not-non-nullable, not-nullable type, just like MaybeNull, so if we want to warn about MaybeNull here, it's not based solely on the nullability of the receiver.
It's really based on whether we are trying to remove a ? (whether successful or not).
In that case we'd need to define some predicate on types, whether ?. is intended to be used, maybe:
allowNullAware(T?) = true
allowNullAware(X extends B) = allowNullAware(B)
allowNullAware(X & B) = allowNullAware(B)
allowNullAware(FutureOr<T>) = allowNullAware(T)
allowNullAware(T) = (Null <: T) // otherwise. Which is going to be false for all current types other than Null.
That function tries to traverse structural types to get down to a type that is either T? or not, using the bound or promotion of a static-type type variable as the type to look at, and taking the T branch of FutureOr<T>, since Future<T> is not nullable. Then it says it's OK to use null-aware operators if that type is T?.
WDYT?
Should this be how we warn about null-aware operators on extension types?
Or should we just do what we'd do with the current rules, and not warn about extension types which do not implement Object? (Since they are not non-nullable, therefore potentially nullable, and should be assumed to potentially be null).
Or should we do what we do for await and disallow using null-aware operators on extension types that are not nullable, or even only those that are non-nullable. (Which won't break any existing code, since there are no existing extension types.)
The only thing I'm certain of is that we should not be looking at the (transitive) representation type when deciding what to do.
The text was updated successfully, but these errors were encountered:
Agreed. I see a warning today in analyzer when I try it today.
NotNull
The nn?.foo is unnecessary, today, but if someone changes NotNull's representation type to Object? tomorrow, then the ?. will start doing something.
Agreed. The author of the representation type is trying to encapsulate the representation as well as extension types allow them to, so we should respect that.
MaybeNull
For mmn?.foo and mny?.foo, the ?. is unnecessary for calling foo, you can do .foo directly. But it does change the behavior, since mny?.foo is null and mnn?.foo is "foo".
If we think of ?. as an operator on T? to conditionally call members on T, then it's unnecessary of MaybeNull. And we should possibly warn about it being unnecessary.
But if we think of v?.foo as just a shorthand for v == null ? null : v.foo, with no special intent, then it works as intended here.
I think we should follow extension member dispatch. If I write:
I don't get any warnings on either call to .foo. As you say, the latter semantically is just a shorthand, and possibly one the user wants. So I wouldn't warn.
That's also how analyzer behaves today on MaybeNull.
So, I think the implementations are doing what I would want them to do in all cases here.
The analyzer gives a warning if you write
int x = ...; x?.toString()
orx ?? 42
, an "unnecessary null-aware" warning because the receiver cannot benull
.For a not-non-nullable extension type with a non-nullable representation type, like:
Should we warn about or even disallow using
?.
(or other null-aware operators like??
and??=
) in any of these cases, or not?NeverNull
The
vn?.foo
should warn. The receiver is definitely non-nullable, and can never benull
. Same as today, nothing new or interesting, it's just some non-nullable type.NotNull
The
nn?.foo
is unnecessary, today, but if someone changesNotNull
's representation type toObject?
tomorrow, then the?.
will start doing something.That change may be breaking, or it may not (if the author has documented that you should not assume anything about the representation type, then your code getting broken by making assumptions is just your own fault.)
Which means it's probaby safest to treat it the same as
MaybeNull
.MaybeNull
For
mmn?.foo
andmny?.foo
, the?.
is unnecessary for callingfoo
, you can do.foo
directly.But it does change the behavior, since
mny?.foo
isnull
andmnn?.foo
is "foo".If we think of
?.
as an operator onT?
to conditionally call members onT
, then it's unnecessary ofMaybeNull
. And we should possibly warn about it being unnecessary.But if we think of
v?.foo
as just a shorthand forv == null ? null : v.foo
, with no special intent, then it works as intended here.I think assuming "no intent" is a mistake.
The use here may not be desired since the
?.
is not really working on theMaybeNull
type, but on the underlying representation value, effectively breaching abstraction.So maybe we do want a warning.
But then, we do allow, without any warning,
x?.foo
whenx
has a type variable typeX
with a nullable bound. That's a not-non-nullable, not-nullable type, just likeMaybeNull
, so if we want to warn aboutMaybeNull
here, it's not based solely on the nullability of the receiver.It's really based on whether we are trying to remove a
?
(whether successful or not).In that case we'd need to define some predicate on types, whether
?.
is intended to be used, maybe:false
for all current types other thanNull
.That function tries to traverse structural types to get down to a type that is either
T?
or not, using the bound or promotion of a static-type type variable as the type to look at, and taking theT
branch ofFutureOr<T>
, sinceFuture<T>
is not nullable. Then it says it's OK to use null-aware operators if that type isT?
.WDYT?
Should this be how we warn about null-aware operators on extension types?
Or should we just do what we'd do with the current rules, and not warn about extension types which do not implement
Object
? (Since they are not non-nullable, therefore potentially nullable, and should be assumed to potentially benull
).Or should we do what we do for
await
and disallow using null-aware operators on extension types that are not nullable, or even only those that are non-nullable. (Which won't break any existing code, since there are no existing extension types.)The only thing I'm certain of is that we should not be looking at the (transitive) representation type when deciding what to do.
The text was updated successfully, but these errors were encountered: