Merge pull request #2513 from hvitved/csharp/null-maybe-capture

C#: Remove FPs from `cs/dereferenced-value-may-be-null`
This commit is contained in:
Tom Hvitved
2022-02-11 10:21:15 +01:00
committed by GitHub
7 changed files with 81 additions and 17 deletions

View File

@@ -32,12 +32,20 @@ class Guard extends Expr {
isGuardedByNode(cfn, this, sub, v)
}
/**
* Holds if `cfn` is guarded by this expression having value `v`.
*
* Note: This predicate is inlined.
*/
pragma[inline]
predicate controlsNode(ControlFlow::Nodes::ElementNode cfn, AbstractValue v) {
guardControls(this, cfn.getBasicBlock(), v)
}
/**
* Holds if basic block `bb` is guarded by this expression having value `v`.
*/
predicate controlsBasicBlock(BasicBlock bb, AbstractValue v) {
Internal::guardControls(this, bb, v)
}
predicate controlsBasicBlock(BasicBlock bb, AbstractValue v) { guardControls(this, bb, v) }
/**
* Holds if this guard is an equality test between `e1` and `e2`. If the test is
@@ -46,7 +54,7 @@ class Guard extends Expr {
*/
predicate isEquality(Expr e1, Expr e2, boolean polarity) {
exists(BooleanValue v |
this = Internal::getAnEqualityCheck(e1, v, e2) and
this = getAnEqualityCheck(e1, v, e2) and
polarity = v.getValue()
)
}

View File

@@ -177,6 +177,19 @@ private predicate isNullDefaultArgument(Ssa::ExplicitDefinition def, AlwaysNullE
)
}
/**
* Holds if `edef` is an implicit entry definition for a captured variable that
* may be guarded, because a call to the capturing callable is guarded.
*/
private predicate isMaybeGuardedCapturedDef(Ssa::ImplicitEntryDefinition edef) {
exists(Ssa::ExplicitDefinition def, ControlFlow::Nodes::ElementNode c, G::Guard g, NullValue nv |
def.isCapturedVariableDefinitionFlowIn(edef, c, _) and
g = def.getARead() and
g.controlsNode(c, nv) and
nv.isNonNull()
)
}
/** Holds if `def` is an SSA definition that may be `null`. */
private predicate defMaybeNull(Ssa::Definition def, string msg, Element reason) {
not nonNullDef(def) and
@@ -214,6 +227,7 @@ private predicate defMaybeNull(Ssa::Definition def, string msg, Element reason)
exists(Dereference d | dereferenceAt(_, _, def, d) |
d.hasNullableType() and
not def instanceof Ssa::PhiNode and
not isMaybeGuardedCapturedDef(def) and
reason = def.getSourceVariable().getAssignable() and
msg = "because it has a nullable type"
)

View File

@@ -9,9 +9,9 @@ public class E
long[][] a2 = null;
var haveA2 = ix < len && (a2 = a1[ix]) != null;
long[] a3 = null;
var haveA3 = haveA2 && (a3 = a2[ix]) != null; // GOOD (false positive)
var haveA3 = haveA2 && (a3 = a2[ix]) != null; // GOOD (FALSE POSITIVE)
if (haveA3)
a3[0] = 0; // GOOD (false positive)
a3[0] = 0; // GOOD (FALSE POSITIVE)
}
public void Ex2(bool x, bool y)
@@ -24,7 +24,7 @@ public class E
s2 = (s1 == null) ? null : "";
}
if (s2 != null)
s1.ToString(); // GOOD (false positive)
s1.ToString(); // GOOD (FALSE POSITIVE)
}
public void Ex3(IEnumerable<string> ss)
@@ -58,7 +58,7 @@ public class E
slice = new List<string>();
result.Add(slice);
}
slice.Add(str); // GOOD (false positive)
slice.Add(str); // GOOD (FALSE POSITIVE)
++index;
}
}
@@ -70,7 +70,7 @@ public class E
arrLen = arr == null ? 0 : arr.Length;
if (arrLen > 0)
arr[0] = 0; // GOOD (false positive)
arr[0] = 0; // GOOD (FALSE POSITIVE)
}
public const int MY_CONST_A = 1;
@@ -109,7 +109,7 @@ public class E
arr2 = new int[arr1.Length];
for (var i = 0; i < arr1.Length; i++)
arr2[i] = arr1[i]; // GOOD (false positive)
arr2[i] = arr1[i]; // GOOD (FALSE POSITIVE)
}
public void Ex8(int x, int lim)
@@ -122,7 +122,7 @@ public class E
int j = 0;
while (!stop && j < lim)
{
int step = (j * obj.GetHashCode()) % 10; // GOOD (false positive)
int step = (j * obj.GetHashCode()) % 10; // GOOD (FALSE POSITIVE)
if (step == 0)
{
obj.ToString(); // GOOD
@@ -156,7 +156,7 @@ public class E
cond = true;
}
if (cond)
obj2.ToString(); // GOOD (false positive)
obj2.ToString(); // GOOD (FALSE POSITIVE)
}
public void Ex10(int[] a)
@@ -164,7 +164,7 @@ public class E
int n = a == null ? 0 : a.Length;
for (var i = 0; i < n; i++)
{
int x = a[i]; // GOOD (false positive)
int x = a[i]; // GOOD (FALSE POSITIVE)
if (x > 7)
a = new int[n];
}
@@ -175,7 +175,7 @@ public class E
bool b2 = obj == null ? false : b1;
if (b2 == null)
{
obj.ToString(); // GOOD (false positive)
obj.ToString(); // GOOD (FALSE POSITIVE)
}
if (obj == null)
{
@@ -183,7 +183,7 @@ public class E
}
if (b1 == null)
{
obj.ToString(); // GOOD (false positive)
obj.ToString(); // GOOD (FALSE POSITIVE)
}
}
@@ -372,7 +372,7 @@ public class E
if (o is string)
{
var s = o as string;
return s.Length; // GOOD (false positive)
return s.Length; // GOOD (FALSE POSITIVE)
}
return -1;
}
@@ -383,7 +383,7 @@ public class E
return false;
if (e1 == null && e2 == null)
return true;
return e1.Long == e2.Long; // GOOD (false positive)
return e1.Long == e2.Long; // GOOD (FALSE POSITIVE)
}
int Ex38(int? i)
@@ -411,6 +411,26 @@ public class E
i ??= null;
return i.Value; // GOOD
}
static bool Ex42(int? i, IEnumerable<int> @is)
{
return @is.Any(j => j == i.Value); // BAD (maybe)
}
static bool Ex43(int? i, IEnumerable<int> @is)
{
if (i.HasValue)
return @is.Any(j => j == i.Value); // GOOD
return false;
}
static bool Ex44(int? i, IEnumerable<int> @is)
{
if (i.HasValue)
@is = @is.Where(j => j == i.Value); // BAD (always) (FALSE NEGATIVE)
i = null;
return @is.Any();
}
}
public static class Extensions

View File

@@ -238,6 +238,12 @@
| E.cs:384:27:384:36 | ... == ... | true | E.cs:384:33:384:36 | null | E.cs:384:27:384:28 | access to parameter e2 |
| E.cs:386:16:386:33 | ... == ... | true | E.cs:386:16:386:22 | access to property Long | E.cs:386:27:386:33 | access to property Long |
| E.cs:386:16:386:33 | ... == ... | true | E.cs:386:27:386:33 | access to property Long | E.cs:386:16:386:22 | access to property Long |
| E.cs:417:29:417:40 | ... == ... | true | E.cs:417:29:417:29 | access to parameter j | E.cs:417:34:417:40 | access to property Value |
| E.cs:417:29:417:40 | ... == ... | true | E.cs:417:34:417:40 | access to property Value | E.cs:417:29:417:29 | access to parameter j |
| E.cs:423:33:423:44 | ... == ... | true | E.cs:423:33:423:33 | access to parameter j | E.cs:423:38:423:44 | access to property Value |
| E.cs:423:33:423:44 | ... == ... | true | E.cs:423:38:423:44 | access to property Value | E.cs:423:33:423:33 | access to parameter j |
| E.cs:430:34:430:45 | ... == ... | true | E.cs:430:34:430:34 | access to parameter j | E.cs:430:39:430:45 | access to property Value |
| E.cs:430:34:430:45 | ... == ... | true | E.cs:430:39:430:45 | access to property Value | E.cs:430:34:430:34 | access to parameter j |
| Forwarding.cs:59:13:59:21 | ... == ... | true | Forwarding.cs:59:13:59:13 | access to parameter o | Forwarding.cs:59:18:59:21 | null |
| Forwarding.cs:59:13:59:21 | ... == ... | true | Forwarding.cs:59:18:59:21 | null | Forwarding.cs:59:13:59:13 | access to parameter o |
| Forwarding.cs:78:16:78:39 | call to method ReferenceEquals | true | Forwarding.cs:78:32:78:32 | access to parameter o | Forwarding.cs:78:35:78:38 | null |

View File

@@ -1292,6 +1292,14 @@
| E.cs:411:9:411:18 | ... ?? ... | null | E.cs:411:15:411:18 | null | null |
| E.cs:412:16:412:16 | access to local variable i | non-null | E.cs:411:9:411:18 | ... ?? ... | non-null |
| E.cs:412:16:412:16 | access to local variable i | null | E.cs:411:9:411:18 | ... ?? ... | null |
| E.cs:417:16:417:41 | call to method Any<Int32> | true | E.cs:417:16:417:18 | access to parameter is | non-empty |
| E.cs:422:13:422:22 | access to property HasValue | false | E.cs:422:13:422:13 | access to parameter i | null |
| E.cs:422:13:422:22 | access to property HasValue | true | E.cs:422:13:422:13 | access to parameter i | non-null |
| E.cs:423:20:423:45 | call to method Any<Int32> | true | E.cs:423:20:423:22 | access to parameter is | non-empty |
| E.cs:429:13:429:22 | access to property HasValue | false | E.cs:429:13:429:13 | access to parameter i | null |
| E.cs:429:13:429:22 | access to property HasValue | true | E.cs:429:13:429:13 | access to parameter i | non-null |
| E.cs:432:16:432:24 | call to method Any<Int32> | false | E.cs:432:16:432:18 | access to parameter is | empty |
| E.cs:432:16:432:24 | call to method Any<Int32> | true | E.cs:432:16:432:18 | access to parameter is | non-empty |
| Forwarding.cs:9:13:9:30 | !... | false | Forwarding.cs:9:14:9:30 | call to method IsNullOrEmpty | true |
| Forwarding.cs:9:13:9:30 | !... | true | Forwarding.cs:9:14:9:30 | call to method IsNullOrEmpty | false |
| Forwarding.cs:9:14:9:14 | access to local variable s | empty | Forwarding.cs:7:20:7:23 | null | empty |

View File

@@ -294,6 +294,10 @@
| E.cs:404:9:404:9 | access to local variable i | E.cs:404:9:404:9 | access to local variable i | null | true |
| E.cs:411:9:411:9 | access to local variable i | E.cs:411:9:411:9 | access to local variable i | non-null | false |
| E.cs:411:9:411:9 | access to local variable i | E.cs:411:9:411:9 | access to local variable i | null | true |
| E.cs:422:13:422:22 | access to property HasValue | E.cs:422:13:422:13 | access to parameter i | false | true |
| E.cs:422:13:422:22 | access to property HasValue | E.cs:422:13:422:13 | access to parameter i | true | false |
| E.cs:429:13:429:22 | access to property HasValue | E.cs:429:13:429:13 | access to parameter i | false | true |
| E.cs:429:13:429:22 | access to property HasValue | E.cs:429:13:429:13 | access to parameter i | true | false |
| Forwarding.cs:9:14:9:30 | call to method IsNullOrEmpty | Forwarding.cs:9:14:9:14 | access to local variable s | false | false |
| Forwarding.cs:14:13:14:32 | call to method IsNotNullOrEmpty | Forwarding.cs:14:13:14:13 | access to local variable s | true | false |
| Forwarding.cs:19:14:19:23 | call to method IsNull | Forwarding.cs:19:14:19:14 | access to local variable s | false | false |

View File

@@ -406,6 +406,8 @@ nodes
| E.cs:404:9:404:18 | SSA def(i) |
| E.cs:404:9:404:18 | SSA def(i) |
| E.cs:405:16:405:16 | access to local variable i |
| E.cs:417:24:417:40 | SSA capture def(i) |
| E.cs:417:34:417:34 | access to parameter i |
| Forwarding.cs:7:16:7:23 | SSA def(s) |
| Forwarding.cs:9:13:9:30 | [false] !... |
| Forwarding.cs:14:9:17:9 | if (...) ... |
@@ -795,6 +797,7 @@ edges
| E.cs:384:27:384:28 | access to parameter e2 | E.cs:384:13:384:36 | [false] ... && ... |
| E.cs:404:9:404:18 | SSA def(i) | E.cs:405:16:405:16 | access to local variable i |
| E.cs:404:9:404:18 | SSA def(i) | E.cs:405:16:405:16 | access to local variable i |
| E.cs:417:24:417:40 | SSA capture def(i) | E.cs:417:34:417:34 | access to parameter i |
| Forwarding.cs:7:16:7:23 | SSA def(s) | Forwarding.cs:9:13:9:30 | [false] !... |
| Forwarding.cs:9:13:9:30 | [false] !... | Forwarding.cs:14:9:17:9 | if (...) ... |
| Forwarding.cs:14:9:17:9 | if (...) ... | Forwarding.cs:19:9:22:9 | if (...) ... |
@@ -907,6 +910,7 @@ edges
| E.cs:386:27:386:28 | access to parameter e2 | E.cs:380:30:380:31 | SSA param(e2) | E.cs:386:27:386:28 | access to parameter e2 | Variable $@ may be null here as suggested by $@ null check. | E.cs:380:30:380:31 | e2 | e2 | E.cs:382:28:382:37 | ... != ... | this |
| E.cs:386:27:386:28 | access to parameter e2 | E.cs:380:30:380:31 | SSA param(e2) | E.cs:386:27:386:28 | access to parameter e2 | Variable $@ may be null here as suggested by $@ null check. | E.cs:380:30:380:31 | e2 | e2 | E.cs:382:58:382:67 | ... == ... | this |
| E.cs:386:27:386:28 | access to parameter e2 | E.cs:380:30:380:31 | SSA param(e2) | E.cs:386:27:386:28 | access to parameter e2 | Variable $@ may be null here as suggested by $@ null check. | E.cs:380:30:380:31 | e2 | e2 | E.cs:384:27:384:36 | ... == ... | this |
| E.cs:417:34:417:34 | access to parameter i | E.cs:417:24:417:40 | SSA capture def(i) | E.cs:417:34:417:34 | access to parameter i | Variable $@ may be null here because it has a nullable type. | E.cs:415:27:415:27 | i | i | E.cs:415:27:415:27 | i | this |
| GuardedString.cs:35:31:35:31 | access to local variable s | GuardedString.cs:7:16:7:32 | SSA def(s) | GuardedString.cs:35:31:35:31 | access to local variable s | Variable $@ may be null here because of $@ assignment. | GuardedString.cs:7:16:7:16 | s | s | GuardedString.cs:7:16:7:32 | String s = ... | this |
| NullMaybeBad.cs:7:27:7:27 | access to parameter o | NullMaybeBad.cs:13:17:13:20 | null | NullMaybeBad.cs:7:27:7:27 | access to parameter o | Variable $@ may be null here because of $@ null argument. | NullMaybeBad.cs:5:25:5:25 | o | o | NullMaybeBad.cs:13:17:13:20 | null | this |
| StringConcatenation.cs:16:17:16:17 | access to local variable s | StringConcatenation.cs:14:16:14:23 | SSA def(s) | StringConcatenation.cs:16:17:16:17 | access to local variable s | Variable $@ may be null here because of $@ assignment. | StringConcatenation.cs:14:16:14:16 | s | s | StringConcatenation.cs:14:16:14:23 | String s = ... | this |