-
-
Save controlflow/9996185 to your computer and use it in GitHub Desktop.
nullness inspection | |
{ | |
a?.M(); | |
a.F(); // <= possible NRE | |
a?.M(a != null); // <= expression always true | |
} | |
inspection "Redundant null-propagation" | |
{ | |
var a = new A(); | |
a?.M( // <= redundant ? warning | |
a?.P); // <= redundant ? warning | |
} | |
inspection "Merge sequential null-checks" | |
{ | |
if (a != null && a.P != null && a.P.U != null) | |
if (a != null && a.P.U != null) | |
if (a != null && a.P.M(x) != null) // invocation at end | |
if (_ && a?.P != null && a.P?.U != null) | |
if (a?.P != null && a.P.U != null) | |
if (a == null || a.P == null) | |
if (s.HasValue && s.Value.P != null) | |
if (d != null && d(x) != null) | |
// => | |
if (a?.P?.U != null) | |
if (a?.P.U != null) // short-circuiting | |
if (a?.P.M(x) != null) | |
if (_ && a?.P?.U != null) | |
if (a?.P?.U != null) | |
if (a?.P == null) | |
if (s?.P != null) // value-type | |
if (d?.Invoke(x) != null) // delegate | |
} | |
inspection "Null-propagating method call(s)" | |
{ | |
var t = x.Y; | |
if (t != null) { t.M(); } | |
if (t == null) { } else { t.P.M(); } | |
if (t != null) { | |
var p = t.P as X; | |
p.M(); | |
p?.U(); | |
if (p != null) { G(); } | |
} | |
if (t is X) { ((X) t).M(); } | |
// => | |
var t = x.Y; | |
t?.M(); | |
t?.P.M(); // short-circuiting | |
var p = t?.P as X; // lifted variable + as-cast | |
p?.M(); // lifted method call | |
p?.U(); | |
if (p != null) { G(); } // lifted if | |
(t as X)?.M(); | |
} | |
inspection "Null-propagating method return" | |
{ | |
if (t == null) { return null; } else { return t.M(x).P; } | |
// => | |
return t?.M(x).P; | |
} | |
inspection "Replace control instructions with null-propagation" | |
{ | |
for (;;) { | |
if (a == null) continue; | |
var b = a.B; | |
if (b == null) continue; | |
var d = b.C.D; | |
d.M(); | |
// continue | |
} | |
// => | |
for (;;) { | |
var b = a?.B | |
var d = b?.C.D; // short-circuiting | |
d?.M(); | |
} | |
} | |
inspection "Reduce nested null-checks with null-propagation" | |
{ | |
var a = GetA(); | |
if (a != null) { | |
var b = a.B; | |
if (b != null) { | |
var d = b?.C.D; | |
var e = d.E; | |
if (e != null) e.M(); | |
} | |
} | |
// => | |
var a = GetA(); | |
var b = a?.B; | |
var d = b?.C.D; // short-circuiting | |
var e = d?.E; // lifted variable | |
e?.M(); | |
} | |
inspection "Replace conditional operator with null-propagation" | |
{ | |
var b = (a == null) ? null : a.B; | |
var c = (a != null) ? a.B.C : null; | |
var d = (a != null ? a.B.C : null).D(); | |
var e = s.HasValue ? s.Value.P : null; | |
// => | |
var b = a?.B; | |
var c = a?.B.C; // short-circuiting | |
var d = (a?.B.C).D(); // parentheses required | |
var e = s?.P; // value type | |
} | |
inspection "Replace if statement with null-propagation" | |
{ | |
if (p != null) { t = p.U.T; } else { t = null; } | |
T t = null; if (p != null) { t = p.T; } | |
// => | |
t = p?.U.T; | |
T t = null; t = p?.T; | |
} | |
inspection "Replace conditional operator with null-propagation with ?? operator" | |
{ | |
// NOTE: .C is of ref/non-nullable type | |
var c = (a == null) ? 42 : a.B.C; | |
T c; if (a == null) { c = 42; } else { c = a.B.C; } | |
// => | |
var c = a?.B.C ?? 42; | |
} | |
inspection "Replace null-checked access with null-propagation" | |
{ | |
if (t != null) t = t.T; | |
// => | |
t = t?.T; | |
} | |
inspection "Merge sequential null-checks (using lifted nullable operators)" | |
{ | |
if (t != null && t.M()) | |
if (t != null && t.P == 42) | |
// => | |
if (t?.M() == true) | |
if (t?.P == 42) | |
} | |
// inside LINQ expressions | |
{ | |
xs.Where(x => x != null).Select(x => x.B.C).Where(x => x != null) | |
xs.Where(x => x.A != null).Select(x => x.A.B.C).Where(x => x != null) | |
from x in xs where x.A != null | |
where x.A.B != null select x.A.B | |
// => | |
xs.Select(x => x?.B.C).Where(x => x != null) | |
xs.Select(x => x.A?.B.C).Where(x => x != null) | |
from x in xs where x.A?.B != null select x.A.B | |
} | |
// user's .NotNull(x => x.Y)-like methods: | |
{ | |
T t = a.With(_ => _.B); | |
// U With<T,U>(Func<T, U>) where T: class, where U: class; | |
// note: false positives. Think about names like 'NotNull'/'NotNull'/'IfNotNull'/'Try' | |
// U? With<T,U>(Func<T?,U>) where T: struct, where U: struct; | |
// U? With<T,U>(Func<T?,U?>) where T: struct, where U: struct; | |
// U With<T,U>(Func<T,U>) where T: class; | |
// + note: 'struct => class' and 'class => struct' overloads | |
// + node: overloads with default values | |
// + note: parameter '_' may not be used at all | |
// => | |
T t = a?.B; | |
} | |
// the same, more complex example | |
{ | |
D d = a.NotNull(x => x.B.C).NotNull(x => x.D); | |
// => | |
D d = a?.B.C?.D; // short-circuiting | |
} | |
// delegate invoke | |
{ | |
D d = o as D; | |
d(); // possible NRE | |
// => | |
D d = o as D; | |
d?.Invoke(); | |
} |
Nope, operator
??
bring backint
type.
Yes, so it changes the type of b
if B
is int?
. Or is it only for non-nullable B
?
Kestrel combinator? Seriously? Can you provide an example?
var client = New.Client().With(c => c.Name = "ABC");
var something = new Something {
A = "A",
B = "B"
}.With(s => s.Event += E)
var something = new Something {
A = "A"
B = new SomethingLikeXmlDocument().With(d => d.LoadUsingMagic("xyz"))
};
Often useful as an object initializer replacement for features that are not supported by object initializers and APIs that do not support chaining.
Or is it only for non-nullable
B
?
Nice catch, only if it is non-nullable. Thanks!
Often useful as an object initializer replacement...
Thanks, nice point. I think we will stick with hardcoded NotNull
name or [ContractAnnotation(null => null)]
or smth like that. Or disabled-by-default inspection nobody will ever find :)
Maybe: detect expression that will behave the same after transformation to nullable type
if (x is T && ((T) x).Value == 42)
// =>
if ((x as T)?.Value == 42) // lifted ==
Offer transformation via context action, not QF.
Maybe: ability to lift whole declarations (only as context action, not suggestion)
if (person != null) {
var something = person.GetSomething(arg, 42);
var component = something["argument"].Component;
component.Foo(something);
}
=>
var something = person?.GetSomething(arg, 42);
var component = something?["argument"].Component;
component?.Foo(something /* only when T is not value type */);
TODO: reverse transformation!
var function = functionDeclaration.DeclaredElement;
if (function == null) return null;
return function.ReturnType;
=>
var function = functionDeclaration.DeclaredElement;
return function?.ReturnType;
Nope, operator
??
bring backint
type.Maybe. Maybe just with
NotNull
name. Kestrel combinator? Seriously? Can you provide an example?