C#: Update nullness analyses

Port the SSA-based logic from the Java nullness analyses.
This commit is contained in:
Tom Hvitved
2018-11-16 14:39:55 +01:00
parent d2a431e6f3
commit 80144a00c8
26 changed files with 1527 additions and 583 deletions

View File

@@ -2,13 +2,39 @@
"-//Semmle//qhelp//EN" "-//Semmle//qhelp//EN"
"qhelp.dtd"> "qhelp.dtd">
<qhelp> <qhelp>
<overview> <overview>
<p>If a variable is dereferenced, and the variable has a null value on all possible execution paths <p>If a variable is dereferenced, and the variable has a <code>null</code>
leading to the dereferencing, it is guaranteed to result in a <code>NullReferenceException</code>. value on all possible execution paths leading to the dereferencing, the dereferencing is
guaranteed to result in a <code>NullReferenceException</code>.
</p> </p>
</overview> </overview>
<recommendation> <recommendation>
<p>Examine the code to check for possible errors.</p>
<p>Ensure that the variable does not have a <code>null</code> value when it is dereferenced.
</p>
</recommendation> </recommendation>
<example>
<p>
In the following examples, the condition <code>s.Length > 0</code> is only
executed if <code>s</code> is <code>null</code>.
</p>
<sample src="NullAlwaysBad.cs" />
<p>
In the revised example, the condition is guarded correctly by using <code>&amp;&amp;</code> instead of
<code>||</code>.
</p>
<sample src="NullAlwaysGood.cs" />
</example>
<references>
<li>Microsoft, <a href="https://docs.microsoft.com/en-us/dotnet/api/system.nullreferenceexception">NullReferenceException Class</a>.</li>
</references>
</qhelp> </qhelp>

View File

@@ -1,9 +1,9 @@
/** /**
* @name Dereferenced variable is always null * @name Dereferenced variable is always null
* @description Finds uses of a variable that may cause a NullPointerException * @description Dereferencing a variable whose value is 'null' causes a 'NullReferenceException'.
* @kind problem * @kind problem
* @problem.severity error * @problem.severity error
* @precision medium * @precision very-high
* @id cs/dereferenced-value-is-always-null * @id cs/dereferenced-value-is-always-null
* @tags reliability * @tags reliability
* correctness * correctness
@@ -14,6 +14,6 @@
import csharp import csharp
import semmle.code.csharp.dataflow.Nullness import semmle.code.csharp.dataflow.Nullness
from VariableAccess access, LocalVariable var from Dereference d, Ssa::SourceVariable v
where access = unguardedNullDereference(var) where d.isFirstAlwaysNull(v)
select access, "Variable $@ is always null here.", var, var.getName() select d, "Variable '$@' is always null here.", v, v.toString()

View File

@@ -0,0 +1,13 @@
using System;
namespace NullAlways
{
class Bad
{
void DoPrint(string s)
{
if (s != null || s.Length > 0)
Console.WriteLine(s);
}
}
}

View File

@@ -0,0 +1,13 @@
using System;
namespace NullAlways
{
class Good
{
void DoPrint(string s)
{
if (s != null && s.Length > 0)
Console.WriteLine(s);
}
}
}

View File

@@ -2,13 +2,41 @@
"-//Semmle//qhelp//EN" "-//Semmle//qhelp//EN"
"qhelp.dtd"> "qhelp.dtd">
<qhelp> <qhelp>
<overview> <overview>
<p>If a variable is dereferenced, and the variable may have a null value on some execution paths <p>If a variable is dereferenced, and the variable may have a <code>null</code>
leading to the dereferencing, the dereferencing may result in a <code>NullReferenceException</code>. value on some execution paths leading to the dereferencing, the dereferencing
may result in a <code>NullReferenceException</code>.
</p> </p>
</overview> </overview>
<recommendation> <recommendation>
<p>Examine the code to check for possible errors.</p>
<p>Ensure that the variable does not have a <code>null</code> value when it is dereferenced.
</p>
</recommendation> </recommendation>
<example>
<p>
In the following example, the method <code>DoPrint()</code> dereferences its parameter
<code>o</code> unconditionally, resulting in a <code>NullReferenceException</code> via
the call <code>DoPrint(null)</code>.
</p>
<sample src="NullMaybeBad.cs" />
<p>
In the revised example, the method <code>DoPrint()</code> guards the dereferencing with
a <code>null</code> check.
</p>
<sample src="NullMaybeGood.cs" />
</example>
<references>
<li>Microsoft, <a href="https://docs.microsoft.com/en-us/dotnet/api/system.nullreferenceexception">NullReferenceException Class</a>.</li>
</references>
</qhelp> </qhelp>

View File

@@ -1,9 +1,10 @@
/** /**
* @name Dereferenced variable may be null * @name Dereferenced variable may be null
* @description Finds uses of a variable that may cause a NullPointerException * @description Dereferencing a variable whose value may be 'null' may cause a
* 'NullReferenceException'.
* @kind problem * @kind problem
* @problem.severity warning * @problem.severity warning
* @precision medium * @precision high
* @id cs/dereferenced-value-may-be-null * @id cs/dereferenced-value-may-be-null
* @tags reliability * @tags reliability
* correctness * correctness
@@ -14,8 +15,6 @@
import csharp import csharp
import semmle.code.csharp.dataflow.Nullness import semmle.code.csharp.dataflow.Nullness
from VariableAccess access, LocalVariable var from Dereference d, Ssa::SourceVariable v, string msg, Element reason
where access = unguardedMaybeNullDereference(var) where d.isFirstMaybeNull(v.getAnSsaDefinition(), msg, reason)
// do not flag definite nulls here; these are already flagged by NullAlways.ql select d, "Variable '$@' may be null here " + msg + ".", v, v.toString(), reason, "this"
and not access = unguardedNullDereference(var)
select access, "Variable $@ may be null here.", var, var.getName()

View File

@@ -0,0 +1,15 @@
using System;
class Bad
{
void DoPrint(object o)
{
Console.WriteLine(o.ToString());
}
void M()
{
DoPrint("Hello");
DoPrint(null);
}
}

View File

@@ -0,0 +1,16 @@
using System;
class Good
{
void DoPrint(object o)
{
if (o != null)
Console.WriteLine(o.ToString());
}
void M()
{
DoPrint("Hello");
DoPrint(null);
}
}

View File

@@ -112,6 +112,8 @@ private import AbstractValues
* an expression that may evaluate to `null`. * an expression that may evaluate to `null`.
*/ */
class DereferenceableExpr extends Expr { class DereferenceableExpr extends Expr {
private boolean isNullableType;
DereferenceableExpr() { DereferenceableExpr() {
exists(Expr e, Type t | exists(Expr e, Type t |
// There is currently a bug in the extractor: the type of `x?.Length` is // There is currently a bug in the extractor: the type of `x?.Length` is
@@ -119,12 +121,19 @@ class DereferenceableExpr extends Expr {
// `getNullEquivParent()` as a workaround // `getNullEquivParent()` as a workaround
this = getNullEquivParent*(e) and this = getNullEquivParent*(e) and
t = e.getType() | t = e.getType() |
t instanceof NullableType t instanceof NullableType and
isNullableType = true
or or
t instanceof RefType t instanceof RefType and
isNullableType = false
) )
} }
/** Holds if this expression has a nullable type `T?`. */
predicate hasNullableType() {
isNullableType = true
}
/** /**
* Gets an expression that directly tests whether this expression is `null`. * Gets an expression that directly tests whether this expression is `null`.
* *
@@ -177,9 +186,22 @@ class DereferenceableExpr extends Expr {
if ie.(IsConstantExpr).getConstant() instanceof NullLiteral then if ie.(IsConstantExpr).getConstant() instanceof NullLiteral then
// E.g. `x is null` // E.g. `x is null`
isNull = branch isNull = branch
else else (
// E.g. `x is string` or `x is ""` // E.g. `x is string` or `x is ""`
(branch = true and isNull = false) branch = true and isNull = false
or
// E.g. `x is string` where `x` has type `string`
ie = any(IsTypeExpr ite | ite.getCheckedType() = ite.getExpr().getType()) and
branch = false and
isNull = true
)
)
or
this.hasNullableType() and
result = any(PropertyAccess pa |
pa.getQualifier() = this and
pa.getTarget().hasName("HasValue") and
if branch = true then isNull = false else isNull = true
) )
or or
isCustomNullCheck(result, this, v, isNull) isCustomNullCheck(result, this, v, isNull)
@@ -307,19 +329,23 @@ class AccessOrCallExpr extends Expr {
} }
private Declaration getDeclarationTarget(Expr e) { private Declaration getDeclarationTarget(Expr e) {
e = any(AssignableRead ar | result = ar.getTarget()) or e = any(AssignableAccess aa | result = aa.getTarget()) or
result = e.(Call).getTarget() result = e.(Call).getTarget()
} }
private Ssa::Definition getAnSsaQualifier(Expr e) { private Ssa::Definition getAnSsaQualifier(Expr e) {
e = getATrackedRead(result) e = getATrackedAccess(result)
or or
not e = getATrackedRead(_) and not e = getATrackedAccess(_) and
result = getAnSsaQualifier(e.(QualifiableExpr).getQualifier()) result = getAnSsaQualifier(e.(QualifiableExpr).getQualifier())
} }
private AssignableRead getATrackedRead(Ssa::Definition def) { private AssignableAccess getATrackedAccess(Ssa::Definition def) {
result = def.getARead() and (
result = def.getARead()
or
result = def.(Ssa::ExplicitDefinition).getADefinition().getTargetAccess()
) and
not def instanceof Ssa::ImplicitUntrackedDefinition not def instanceof Ssa::ImplicitUntrackedDefinition
} }
@@ -384,6 +410,15 @@ class GuardedExpr extends AccessOrCallExpr {
v = v0 v = v0
} }
/**
* Holds if this expression must have abstract value `v`. That is, this
* expression is guarded by a structurally equal expression having abstract
* value `v`.
*/
predicate mustHaveValue(AbstractValue v) {
exists(Expr e | e = this.getAGuard(e, v))
}
/** /**
* Holds if this expression is guarded by expression `cond`, which must * Holds if this expression is guarded by expression `cond`, which must
* evaluate to `b`. The expression `sub` is a sub expression of `cond` * evaluate to `b`. The expression `sub` is a sub expression of `cond`
@@ -401,7 +436,7 @@ class GuardedExpr extends AccessOrCallExpr {
/** An expression guarded by a `null` check. */ /** An expression guarded by a `null` check. */
class NullGuardedExpr extends GuardedExpr { class NullGuardedExpr extends GuardedExpr {
NullGuardedExpr() { NullGuardedExpr() {
exists(Expr e, NullValue v | e = this.getAGuard(e, v) | not v.isNull()) this.mustHaveValue(any(NullValue v | not v.isNull()))
} }
} }
@@ -420,11 +455,28 @@ module Internal {
/** Holds if expression `e` is a non-`null` value. */ /** Holds if expression `e` is a non-`null` value. */
predicate nonNullValue(Expr e) { predicate nonNullValue(Expr e) {
e.stripCasts() = any(Expr s | s.hasValue() and not s instanceof NullLiteral) e instanceof ObjectCreation
or
e instanceof ArrayCreation
or
e.hasValue() and
not e instanceof NullLiteral
or
e instanceof ThisAccess
or
e instanceof AddExpr and
e.getType() instanceof StringType
}
/** Holds if expression `e2` is a non-`null` value whenever `e1` is. */
predicate nonNullValueImplied(Expr e1, Expr e2) {
e1 = e2.(CastExpr).getExpr()
or
e1 = e2.(AssignExpr).getRValue()
} }
/** /**
* Gets the parent expression of `e` which is `null` only if `e` is `null`, * Gets the parent expression of `e` which is `null` iff `e` is `null`,
* if any. For example, `result = x?.y` and `e = x`, or `result = x + 1` * if any. For example, `result = x?.y` and `e = x`, or `result = x + 1`
* and `e = x`. * and `e = x`.
*/ */
@@ -433,6 +485,8 @@ module Internal {
qe.getQualifier() = e and qe.getQualifier() = e and
qe.isConditional() and qe.isConditional() and
( (
// The accessed declaration must have a value type in order
// for `only if` to hold
result.(FieldAccess).getTarget().getType() instanceof ValueType result.(FieldAccess).getTarget().getType() instanceof ValueType
or or
result.(Call).getTarget().getReturnType() instanceof ValueType result.(Call).getTarget().getReturnType() instanceof ValueType
@@ -444,11 +498,28 @@ module Internal {
result = bao and result = bao and
bao.getAnOperand() = e and bao.getAnOperand() = e and
bao.getAnOperand() = o and bao.getAnOperand() = o and
// The other operand must be provably non-null in order
// for `only if` to hold
nonNullValue(o) and nonNullValue(o) and
e != o e != o
) )
} }
/**
* Gets a child expression of `e` which is `null` only if `e` is `null`.
*/
Expr getANullImplyingChild(Expr e) {
e = any(QualifiableExpr qe |
qe.isConditional() and
result = qe.getQualifier()
)
or
// In C#, `null + 1` has type `int?` with value `null`
e = any(BinaryArithmeticOperation bao |
result = bao.getAnOperand()
)
}
/** An expression whose value may control the execution of another element. */ /** An expression whose value may control the execution of another element. */
class Guard extends Expr { class Guard extends Expr {
private AbstractValue val; private AbstractValue val;
@@ -785,6 +856,23 @@ module Internal {
v1 instanceof NullValue and v1 instanceof NullValue and
v2 = v1 v2 = v1
or or
g1 instanceof DereferenceableExpr and
g2 = getANullImplyingChild(g1) and
v1 = any(NullValue nv | not nv.isNull()) and
v2 = v1
or
g2 = g1.(AssignExpr).getRValue() and
v1 = g1.getAValue() and
v2 = v1
or
g2 = g1.(Assignment).getLValue() and
v1 = g1.getAValue() and
v2 = v1
or
g2 = g1.(CastExpr).getExpr() and
v1 = g1.getAValue() and
v2 = v1.(NullValue)
or
exists(PreSsa::Definition def | exists(PreSsa::Definition def |
def.getDefinition().getSource() = g2 | def.getDefinition().getSource() = g2 |
g1 = def.getARead() and g1 = def.getARead() and

View File

@@ -2,7 +2,7 @@
* Provides predicates for performing nullness analyses. * Provides predicates for performing nullness analyses.
* *
* Nullness analyses are used to identify places in a program where * Nullness analyses are used to identify places in a program where
* a null pointer exception (`NullReferenceException`) may be thrown. * a `null` pointer exception (`NullReferenceException`) may be thrown.
* Example: * Example:
* *
* ``` * ```
@@ -18,493 +18,394 @@
*/ */
import csharp import csharp
private import ControlFlow
private import semmle.code.csharp.commons.Assertions private import semmle.code.csharp.commons.Assertions
private import semmle.code.csharp.commons.ComparisonTest private import semmle.code.csharp.commons.ComparisonTest
private import semmle.code.csharp.controlflow.Guards private import semmle.code.csharp.controlflow.Guards as G
private import semmle.code.csharp.controlflow.Guards::AbstractValues
private import semmle.code.csharp.dataflow.SSA private import semmle.code.csharp.dataflow.SSA
private import semmle.code.csharp.frameworks.System private import semmle.code.csharp.frameworks.System
private import semmle.code.csharp.frameworks.Test
/** An expression that may be `null`. */ /** An expression that may be `null`. */
private class NullExpr extends Expr { class MaybeNullExpr extends Expr {
NullExpr() { MaybeNullExpr() {
this instanceof NullLiteral or this instanceof NullLiteral
this.(ParenthesizedExpr).getExpr() instanceof NullExpr or or
this.(ConditionalExpr).getThen() instanceof NullExpr or this.(ConditionalExpr).getThen() instanceof MaybeNullExpr
this.(ConditionalExpr).getElse() instanceof NullExpr or
this.(ConditionalExpr).getElse() instanceof MaybeNullExpr
or
this.(AssignExpr).getRValue() instanceof MaybeNullExpr
or
this.(Cast).getExpr() instanceof MaybeNullExpr
} }
} }
/** An expression that may be non-`null`. */ /** An expression that is always `null`. */
private class NonNullExpr extends Expr { class AlwaysNullExpr extends Expr {
AlwaysNullExpr() {
this instanceof NullLiteral
or
this = any(ConditionalExpr ce |
ce.getThen() instanceof AlwaysNullExpr and
ce.getElse() instanceof AlwaysNullExpr
)
or
this.(AssignExpr).getRValue() instanceof AlwaysNullExpr
or
this.(Cast).getExpr() instanceof AlwaysNullExpr
}
}
/** An expression that is never `null`. */
class NonNullExpr extends Expr {
NonNullExpr() { NonNullExpr() {
not (this instanceof NullLiteral or this instanceof ConditionalExpr or this instanceof ParenthesizedExpr) or G::Internal::nonNullValue(this)
this.(ParenthesizedExpr).getExpr() instanceof NonNullExpr or
this.(ConditionalExpr).getThen() instanceof NonNullExpr or
this.(ConditionalExpr).getElse() instanceof NonNullExpr
}
}
/** Gets an assignment to the variable `var` that may be `null`. */
private AssignExpr nullSet(LocalScopeVariable var) {
var.getAnAccess() = result.getLValue() and
result.getRValue() instanceof NullExpr
}
/** Gets an assignment to the variable `var` that may be non-`null`. */
private Assignment nonNullSet(LocalScopeVariable var) {
var.getAnAccess() = result.getLValue() and
result.getRValue() instanceof NonNullExpr
}
/**
* Gets an expression that will result in a `NullReferenceException` if the
* variable access `access` is `null`.
*/
private Expr nonNullAccess(LocalScopeVariableAccess access) {
access.getType() instanceof RefType
and (
result.(ArrayAccess).getQualifier() = access or
exists (MemberAccess ma | result=ma and not ma.isConditional() | access = ma.getQualifier()) or
exists (MethodCall mc | result=mc and not mc.isConditional() | access = mc.getQualifier()) or
exists (LockStmt stmt | stmt.getExpr() = access and result = access)
)
}
/**
* Gets an expression that accesses the variable `var` such that it will
* result in a `NullReferenceException` if the variable is `null`.
*/
private Expr nonNullUse(LocalScopeVariable var) {
result = nonNullAccess(var.getAnAccess())
}
/**
* Gets a local variable declaration expression that may
* initialize the variable `var` with `null`.
*/
private LocalVariableDeclExpr initialNull(LocalVariable var) {
result.getVariable() = var and
result.getInitializer() instanceof NullExpr
}
/**
* Gets a local variable declaration expression that may
* initialize the variable `var` with a non-`null` expression.
*/
private LocalVariableDeclExpr initialNonNull(LocalVariable var) {
result.getVariable() = var and
result.getInitializer() instanceof NonNullExpr
}
/**
* Gets an expression that either asserts that the variable `var`
* is `null` or that may assign `null` to `var`.
*/
private Expr nullDef(LocalScopeVariable var) {
nullSet(var) = result or
initialNull(var) = result or
exists(MethodCall mc, AssertNullMethod m, Expr arg |
// E.g. `Assert.IsNull(var)`
mc = result and
mc.getTarget() = m and
mc.getArgument(m.getAssertionIndex()) = arg and
sameValue(arg, var.getAnAccess())
) or
exists(MethodCall mc, AssertTrueMethod m, Expr arg |
// E.g. `Assert.IsTrue(var == null)`
mc = result and
arg = nullTest(var) and
arg = mc.getArgument(m.getAssertionIndex()) and
mc.getTarget() = m
) or
exists(MethodCall mc, AssertFalseMethod m, Expr arg |
// E.g. `Assert.IsFalse(var != null)`
mc = result and
arg = failureIsNullTest(var) and
arg = mc.getArgument(m.getAssertionIndex()) and
mc.getTarget() = m
)
}
/**
* Gets an expression that either asserts that the variable `var` is
* non-`null`, dereferences it, or may assign a non-`null` expression to it.
*/
private Expr nonNullDef(LocalScopeVariable var) {
nonNullSet(var) = result or
nonNullUse(var) = result or
initialNonNull(var) = result or
useAsOutParameter(var) = result or
nonNullSettingLambda(var) = result or
exists(MethodCall mc, AssertNonNullMethod m, Expr arg |
// E.g. `Assert.IsNotNull(arg)`
mc = result and
mc.getTarget() = m and
mc.getArgument(m.getAssertionIndex()) = arg and
sameValue(arg, var.getAnAccess())
) or
exists(MethodCall mc, AssertTrueMethod m, Expr arg |
// E.g. `Assert.IsTrue(arg != null)`
mc = result and
arg = nonNullTest(var) and
arg = mc.getArgument(m.getAssertionIndex()) and
mc.getTarget() = m
) or
exists(MethodCall mc, AssertFalseMethod m, Expr arg |
// E.g. `Assert.IsFalse(arg == null)`
mc = result and
arg = failureIsNonNullTest(var) and
arg = mc.getArgument(m.getAssertionIndex()) and
mc.getTarget() = m
)
}
private Call useAsOutParameter(LocalScopeVariable var) {
exists(LocalScopeVariableAccess a |
a = result.getAnArgument() and a = var.getAnAccess() |
a.isOutArgument() or a.isRefArgument())
}
private AnonymousFunctionExpr nonNullSettingLambda(LocalScopeVariable var) {
result = nonNullDef(var).getEnclosingCallable()
}
/**
* Gets a logical 'or' expression in which the expression `e` is a
* (possibly nested) operand.
*/
private LogicalOrExpr orParent(Expr e) {
e = result.getAnOperand()
or or
exists(LogicalOrExpr orexpr | result = orParent(orexpr) and e = orexpr.getAnOperand()) exists(NonNullExpr mid |
} G::Internal::nonNullValueImplied(mid, this)
)
/**
* Gets a logical 'and' expression in which the expression `e` is a
* (possibly nested) operand.
*/
private LogicalAndExpr andParent(Expr e) {
e = result.getAnOperand()
or or
exists(LogicalAndExpr andexpr | result = andParent(andexpr) and e = andexpr.getAnOperand()) this instanceof G::NullGuardedExpr
or
exists(Ssa::Definition def | nonNullDef(def) | this = def.getARead())
}
}
/** Holds if SSA definition `def` is never `null`. */
private predicate nonNullDef(Ssa::Definition v) {
v.(Ssa::ExplicitDefinition).getADefinition().getSource() instanceof NonNullExpr
or
exists(AssignableDefinition ad |
ad = v.(Ssa::ExplicitDefinition).getADefinition() |
ad instanceof AssignableDefinitions::IsPatternDefinition
or
ad instanceof AssignableDefinitions::TypeCasePatternDefinition
or
ad = any(AssignableDefinitions::LocalVariableDefinition d |
d.getExpr() = any(SpecificCatchClause scc).getVariableDeclExpr()
or
d.getExpr() = any(ForeachStmt fs).getAVariableDeclExpr()
)
)
} }
/** /**
* Holds if variable access `access` has the "same value" as expression `expr`: * Holds if the `i`th node of basic block `bb` is a dereference `d` of SSA
* definition `def`.
*/
private predicate dereferenceAt(BasicBlock bb, int i, Ssa::Definition def, Dereference d) {
d = def.getAReadAtNode(bb.getNode(i))
}
/**
* Holds if `e` having abstract value `vExpr` implies that SSA definition `def`
* has abstract value `vDef`.
*/
private predicate exprImpliesSsaDef(Expr e, G::AbstractValue vExpr, Ssa::Definition def, G::AbstractValue vDef) {
exists(G::Internal::Guard g |
G::Internal::impliesSteps(e, vExpr, g, vDef) |
g = def.getARead()
or
g = def.(Ssa::ExplicitDefinition).getADefinition().getTargetAccess()
)
}
/**
* Holds if the `i`th node of basic block `bb` ensures that SSA definition
* `def` is not `null` in any subsequent uses.
*/
private predicate ensureNotNullAt(BasicBlock bb, int i, Ssa::Definition def) {
exists(Expr e, G::AbstractValue v, NullValue nv |
G::Internal::asserts(bb.getNode(i).getElement(), e, v) |
exprImpliesSsaDef(e, v, def, nv) and
not nv.isNull()
)
}
/**
* Holds if the `i`th node of basic block `bb` is a dereference `d` of SSA
* definition `def`, and `def` may potentially be `null`.
*/
private predicate potentialNullDereferenceAt(BasicBlock bb, int i, Ssa::Definition def, Dereference d) {
dereferenceAt(bb, i, def, d) and
not exists(int j | ensureNotNullAt(bb, j, def) | j < i)
}
/**
* Gets an element that tests whether a given SSA definition, `def`, is
* `null` or not.
* *
* - `access` is equal to `expr`, or * If the returned element takes the `s` branch, then `def` is guaranteed to be
* - `expr` is an assignment and the `access` is its left-hand side, or * `null` if `nv.isNull()` holds, and non-`null` otherwise.
* - `expr` is an assignment and the `access` has the same value as its right-hand
* side.
*/ */
private predicate sameValue(Expr expr, LocalScopeVariableAccess access) { private ControlFlowElement getANullCheck(Ssa::Definition def, SuccessorTypes::ConditionalSuccessor s, NullValue nv) {
access = expr.stripCasts() or exists(Expr e, G::AbstractValue v |
access = expr.(AssignExpr).getLValue() or v.branchImplies(result, s, e) |
sameValue(expr.(AssignExpr).getRValue(), access) or exprImpliesSsaDef(e, v, def, nv)
sameValue(expr.(ParenthesizedExpr).getExpr(), access)
}
/**
* Gets an `is` expression in which the left-hand side is an access to the
* variable `var`.
*/
private Expr instanceOfTest(LocalScopeVariable var) {
exists(IsExpr e | result = e and
sameValue(e.getExpr() , var.getAnAccess()))
}
/**
* Gets an expression performing a `null` check on the variable `var`:
*
* - either via a reference equality test with `null`, or
* - by passing it as an argument to a method that performs the test.
*/
private Expr directNullTest(LocalScopeVariable var) {
exists(ComparisonTest ct |
result = ct.getExpr() and
sameValue(ct.getAnArgument(), var.getAnAccess()) and
ct.getAnArgument() instanceof NullLiteral |
ct.(ComparisonOperationComparisonTest).getComparisonKind().isEquality() or
ct.(StaticEqualsCallComparisonTest).isReferenceEquals() or
ct.(OperatorCallComparisonTest).getComparisonKind().isEquality()
)
or
exists(Call call, int i | result = call |
call.getRuntimeArgument(i) = var.getAnAccess() and
forex(Callable callable |
call.getARuntimeTarget() = callable |
nullTestInCallable(callable.getSourceDeclaration(), i)
)
)
or
// seems redundant, because all methods that use this method also peel ParenthesizedExpr
// However, removing this line causes an increase of memory usage
result.(ParenthesizedExpr).getExpr() = directNullTest(var)
}
/**
* Holds if callable `c` performs a `null` test on its `i`th argument and
* returns the result.
*/
private predicate nullTestInCallable(Callable c, int i) {
exists(Parameter p |
p = c.getParameter(i) and
not p.isOverwritten() and
forex(Expr e | c.canReturn(e) | stripConditionalExpr(e) = nullTest(p))
)
or
nullTestInLibraryMethod(c, i)
}
/**
* Holds if library method `m` performs a `null` test on its `i`th argument and
* returns the result.
*/
private predicate nullTestInLibraryMethod(Method m, int i) {
m.fromLibrary() and
m.getName().toLowerCase().regexpMatch("(is)?null(orempty|orwhitespace)?") and
m.getReturnType() instanceof BoolType and
m.getNumberOfParameters() = 1 and
i = 0
}
private Expr stripConditionalExpr(Expr e) {
if e instanceof ConditionalExpr then
result = stripConditionalExpr(e.(ConditionalExpr).getThen()) or
result = stripConditionalExpr(e.(ConditionalExpr).getElse())
else
result = e
}
/**
* Gets an expression performing a non-`null` check on the variable `var`:
*
* - either via an inequality test with `null`, or
* - by performing an `is` test, or
* - by passing it as an argument to a method that performs the test.
*/
private Expr directNonNullTest(LocalScopeVariable var) {
exists(ComparisonTest ct |
result = ct.getExpr() and
sameValue(ct.getAnArgument(), var.getAnAccess()) and
ct.getAnArgument() instanceof NullLiteral |
ct.(ComparisonOperationComparisonTest).getComparisonKind().isInequality() or
ct.(OperatorCallComparisonTest).getComparisonKind().isInequality()
)
or
instanceOfTest(var) = result
or
exists(Call call, int i | result = call |
call.getRuntimeArgument(i) = var.getAnAccess() and
exists(call.getARuntimeTarget()) and
forall(Callable callable |
call.getARuntimeTarget() = callable |
nonNullTestInCallable(callable.getSourceDeclaration(), i)
)
)
or
// seems redundant, because all methods that use this method also peel ParenthesizedExpr
// However, removing this line causes an increase of memory usage
result.(ParenthesizedExpr).getExpr() = directNonNullTest(var)
}
/**
* Holds if callable `c` performs a non-`null` test on its `i`th argument and
* returns the result.
*/
private predicate nonNullTestInCallable(Callable c, int i) {
exists(Parameter p |
p = c.getParameter(i) and
not p.isOverwritten() and
forex(Expr e | c.canReturn(e) | stripConditionalExpr(e) = nonNullTest(p))
)
or
nonNullTestInLibraryMethod(c, i)
}
/**
* Holds if library method `m` performs a non-`null` test on its `i`th argument
* and returns the result.
*/
private predicate nonNullTestInLibraryMethod(Method m, int i) {
m.fromLibrary() and
m.getName().toLowerCase().regexpMatch("(is)?no(t|n)null") and
m.getReturnType() instanceof BoolType and
m.getNumberOfParameters() = 1 and
i = 0
}
/**
* Gets a `null` test in a _positive_ position for the variable `var`.
*/
private Expr nullTest(LocalScopeVariable var) {
directNullTest(var) = result
or
result.(ParenthesizedExpr).getExpr() = nullTest(var)
or
exists(LogicalNotExpr notexpr | result = notexpr and
notexpr.getAChildExpr() = failureIsNullTest(var))
or
result = andParent(nullTest(var))
or
exists(LogicalOrExpr orexpr | result = orexpr and
orexpr.getLeftOperand() = nullTest(var) and
orexpr.getRightOperand() = nullTest(var))
}
/**
* Gets a non-`null` test in a _positive_ position for the variable `var`.
*/
private Expr nonNullTest(LocalScopeVariable var) {
directNonNullTest(var) = result
or
result.(ParenthesizedExpr).getExpr() = nonNullTest(var)
or
exists(LogicalNotExpr notexpr | result = notexpr and
notexpr.getAChildExpr() = failureIsNonNullTest(var))
or
result = andParent(nonNullTest(var))
or
exists(LogicalOrExpr orexpr | result = orexpr and
orexpr.getLeftOperand() = nonNullTest(var) and
orexpr.getRightOperand() = nonNullTest(var))
}
/**
* Gets a non-`null` test in a _negative_ position for the variable `var`.
*/
private Expr failureIsNullTest(LocalScopeVariable var) {
directNonNullTest(var) = result
or
result.(ParenthesizedExpr).getExpr() = failureIsNullTest(var)
or
exists(LogicalNotExpr notexpr | result = notexpr and
notexpr.getAChildExpr() = failureIsNonNullTest(var))
or
result = orParent(failureIsNullTest(var))
or
exists(LogicalAndExpr andexpr | result = andexpr and
andexpr.getLeftOperand() = failureIsNullTest(var) and
andexpr.getRightOperand() = failureIsNullTest(var))
}
/**
* Gets a `null` test in a _negative_ position for the variable `var`.
*/
private Expr failureIsNonNullTest(LocalScopeVariable var) {
directNullTest(var) = result
or
result.(ParenthesizedExpr).getExpr() = failureIsNonNullTest(var)
or
exists(LogicalNotExpr notexpr | result = notexpr and
notexpr.getAChildExpr() = failureIsNullTest(var))
or
result = orParent(directNullTest(var))
or
exists(LogicalAndExpr andexpr | result = andexpr and
andexpr.getLeftOperand() = failureIsNonNullTest(var) and
andexpr.getRightOperand() = failureIsNonNullTest(var))
}
/**
* Gets an immediate successor node of the conditional node `cfgnode` where
* the condition implies that the variable `var` is `null`.
*/
private ControlFlow::Node nullBranchKill(LocalScopeVariable var, ControlFlow::Node cfgnode) {
(cfgnode.getElement() = nullTest(var) and result = cfgnode.getATrueSuccessor())
or
(cfgnode.getElement() = failureIsNullTest(var) and result = cfgnode.getAFalseSuccessor())
}
/**
* Gets an immediate successor node of the conditional node `cfgnode` where
* the condition implies that the variable `var` is non-`null`.
*/
private ControlFlow::Node nonNullBranchKill(LocalScopeVariable var, ControlFlow::Node cfgnode) {
(cfgnode.getElement() = nonNullTest(var) and result = cfgnode.getATrueSuccessor())
or
(cfgnode.getElement() = failureIsNonNullTest(var) and result = cfgnode.getAFalseSuccessor())
}
/** Gets a node where the variable `var` may be `null`. */
ControlFlow::Node maybeNullNode(LocalScopeVariable var) {
result = nullDef(var).getAControlFlowNode().getASuccessor()
or
exists(ControlFlow::Node mid |
mid = maybeNullNode(var) and
not mid.getElement() = nonNullDef(var) and
mid.getASuccessor() = result and
not result = nonNullBranchKill(var, mid)
) )
} }
/** Gets a node where the variable `var` may be non-`null`. */ /** Holds if `def` is an SSA definition that may be `null`. */
ControlFlow::Node maybeNonNullNode(LocalScopeVariable var) { private predicate defMaybeNull(Ssa::Definition def, string msg, Element reason) {
result = nonNullDef(var).getAControlFlowNode().getASuccessor() // A variable compared to `null` might be `null`
exists(G::DereferenceableExpr de |
de = def.getARead() |
reason = de.getANullCheck(_, true) and
msg = "as suggested by $@ null check" and
not def instanceof Ssa::PseudoDefinition and
not nonNullDef(def) and
// Don't use a check as reason if there is a `null` assignment
not def.(Ssa::ExplicitDefinition).getADefinition().getSource() instanceof MaybeNullExpr
)
or or
exists(ControlFlow::Node mid | // A parameter might be `null` if there is a `null` argument somewhere
mid = maybeNonNullNode(var) and exists(AssignableDefinitions::ImplicitParameterDefinition pdef, Parameter p, MaybeNullExpr arg |
not mid.getElement() = nullDef(var) and pdef = def.(Ssa::ExplicitDefinition).getADefinition() |
mid.getASuccessor() = result and p = pdef.getParameter().getSourceDeclaration() and
not result = nullBranchKill(var, mid) p.getAnAssignedArgument() = arg and
reason = arg and
msg = "because of $@ null argument" and
not arg.getEnclosingCallable().getEnclosingCallable*() instanceof TestMethod
)
or
// If the source of a variable is `null` then the variable may be `null`
exists(AssignableDefinition adef |
adef = def.(Ssa::ExplicitDefinition).getADefinition() |
adef.getSource() instanceof MaybeNullExpr and
reason = adef.getExpr() and
msg = "because of $@ assignment"
)
or
// A variable of nullable type may be null
exists(Dereference d |
dereferenceAt(_, _, def, d) |
d.hasNullableType() and
not def instanceof Ssa::PseudoDefinition and
reason = def.getSourceVariable().getAssignable() and
msg = "because it has a nullable type"
) )
} }
/** /**
* Gets an expression whose evaluation may be guarded by * Holds if `def1` being `null` in basic block `bb1` implies that `def2` might
* a non-`null` check for the variable `var`. * be null in basic block `bb2`. The SSA definitions share the same source variable.
*/ */
private Expr nullGuarded(LocalScopeVariable var) { private predicate defNullImpliesStep(Ssa::Definition def1, BasicBlock bb1, Ssa::Definition def2, BasicBlock bb2) {
exists(LogicalOrExpr guard | exists(Ssa::SourceVariable v |
guard.getLeftOperand() = failureIsNonNullTest(var) and defMaybeNull(v.getAnSsaDefinition(), _, _) and
result = guard.getRightOperand()) def1.getSourceVariable() = v
|
def2.(Ssa::PseudoDefinition).getAnInput() = def1 and
def2.definesAt(bb2, _)
or or
exists(LogicalAndExpr guard | def2 = def1 and
guard.getLeftOperand() = nonNullTest(var) and not exists(Ssa::PseudoDefinition def |
result = guard.getRightOperand()) def.getSourceVariable() = v and
or def.definesAt(bb2, _)
exists(ConditionalExpr cond | )
cond.getCondition() = nullTest(var) and ) and
result = cond.getElse()) def1.isLiveAtEndOfBlock(bb1) and
or not ensureNotNullAt(bb1, _, def1) and
exists(ConditionalExpr cond | bb2 = bb1.getASuccessor() and
cond.getCondition() = nonNullTest(var) and not exists(SuccessorTypes::ConditionalSuccessor s, NullValue nv |
result = cond.getThen()) bb1.getLastNode() = getANullCheck(def1, s, nv).getAControlFlowNode() |
or bb2 = bb1.getASuccessorByType(s) and
result = any(NullGuardedExpr nge | nge = var.getAnAccess()) not nv.isNull()
or )
result.getParent() = nullGuarded(var)
} }
/** /**
* Gets a variable access that must be non-`null` to avoid a * The transitive closure of `defNullImpliesStep()` originating from `defMaybeNull()`.
* `NullReferenceException`. * That is, those basic blocks for which the SSA definition is suspected of being `null`.
*/ */
private predicate dereferenced(LocalScopeVariableAccess access) { private predicate defMaybeNullInBlock(Ssa::Definition def, Ssa::SourceVariable v, BasicBlock bb) {
exists(nonNullAccess(access)) defMaybeNull(def, _, _) and
def.definesAt(bb, _) and
v = def.getSourceVariable()
or
exists(BasicBlock mid, Ssa::Definition midDef |
defMaybeNullInBlock(midDef, v, mid) |
defNullImpliesStep(midDef, mid, def, bb)
)
} }
/** /**
* Gets a dereferenced access to the variable `var` that * Holds if `v` is a source variable that might reach a potential `null`
* * dereference.
* - does not occur within a `null`-guarded expression, but
* - occurs within an expression where the variable may be `null`.
*/ */
LocalScopeVariableAccess unguardedMaybeNullDereference(LocalScopeVariable var) { private predicate nullDerefCandidateVariable(Ssa::SourceVariable v) {
var.getAnAccess() = result and exists(Ssa::Definition def, BasicBlock bb |
maybeNullNode(var).getElement() = result and potentialNullDereferenceAt(bb, _, def, _) |
dereferenced(result) and defMaybeNullInBlock(def, v, bb)
not result = nullGuarded(var) )
}
private predicate defMaybeNullInBlockOrigin(Ssa::Definition origin, Ssa::Definition def, BasicBlock bb) {
nullDerefCandidateVariable(def.getSourceVariable()) and
defMaybeNull(def, _, _) and
def.definesAt(bb, _) and
origin = def
or
exists(BasicBlock mid, Ssa::Definition midDef |
defMaybeNullInBlockOrigin(origin, midDef, mid) and
defNullImpliesStep(midDef, mid, def, bb)
)
}
private Ssa::Definition getAPseudoInput(Ssa::Definition def) {
result = def.(Ssa::PseudoDefinition).getAnInput()
}
// `def.getAnUltimateDefinition()` includes inputs into uncertain
// definitions, but we only want inputs into pseudo nodes
private Ssa::Definition getAnUltimateDefinition(Ssa::Definition def) {
result = getAPseudoInput*(def) and
not result instanceof Ssa::PseudoDefinition
} }
/** /**
* Gets a dereferenced access to the variable `var` that * Holds if SSA definition `def` can reach a read `ar`, without passing
* * through an intermediate dereference that always (`always = true`) or
* - does not occur within a `null`-guarded expression, but * maybe (`always = false`) throws a null reference exception.
* - occurs within an expression where the variable may be `null`, and
* - does not occur within an expression where the variable may be non-`null`.
*/ */
LocalScopeVariableAccess unguardedNullDereference(LocalScopeVariable var) { private predicate defReaches(Ssa::Definition def, AssignableRead ar, boolean always) {
unguardedMaybeNullDereference(var) = result and ar = def.getAFirstRead() and
not maybeNonNullNode(var).getElement() = result (always = true or always = false)
or
exists(AssignableRead mid |
defReaches(def, mid, always) |
ar = mid.getANextRead() and
not mid = any(Dereference d |
if always = true then d.isAlwaysNull(def.getSourceVariable()) else d.isMaybeNull(def, _, _)
)
)
}
/**
* An expression that dereferences a value. That is, an expression that may
* result in a `NullReferenceException` if the value is `null`.
*/
class Dereference extends G::DereferenceableExpr {
Dereference() {
if this.hasNullableType() then (
// Strictly speaking, these throw `InvalidOperationException`s and not
// `NullReferenceException`s
this = any(PropertyAccess pa | pa.getTarget().hasName("Value")).getQualifier()
or
exists(Type underlyingType |
this = any(CastExpr ce | ce.getTargetType() = underlyingType).getExpr() |
underlyingType = this.getType().(NullableType).getUnderlyingType()
or
underlyingType = this.getType() and
not underlyingType instanceof NullableType
)
)
else (
this = any(QualifiableExpr qe | not qe.isConditional()).getQualifier() and
not this instanceof ThisAccess and
not this instanceof BaseAccess and
not this instanceof TypeAccess
or
this = any(LockStmt stmt).getExpr()
or
this = any(ForeachStmt stmt).getIterableExpr()
or
exists(ExtensionMethodCall emc, Parameter p |
this = emc.getArgumentForParameter(p) and
p.hasExtensionMethodModifier() and
not emc.isConditional() |
p.fromSource() // assume all non-source extension methods perform a dereference
implies
exists(Ssa::ExplicitDefinition def, AssignableDefinitions::ImplicitParameterDefinition pdef |
pdef = def.getADefinition() |
p.getSourceDeclaration() = pdef.getParameter() and
def.getARead() instanceof Dereference
)
)
)
}
private predicate isAlwaysNull0(Ssa::Definition def) {
forall(Ssa::Definition input |
input = getAnUltimateDefinition(def) |
input.(Ssa::ExplicitDefinition).getADefinition().getSource() instanceof AlwaysNullExpr
) and
not nonNullDef(def) and
this = def.getARead() and
not this instanceof G::NullGuardedExpr
}
/**
* Holds if this expression dereferences SSA source variable `v`, which is
* always `null`.
*/
predicate isAlwaysNull(Ssa::SourceVariable v) {
this = v.getAnAccess() and
// Exclude fields, properties, and captured variables, as they may not have an
// accurate SSA representation
v.getAssignable() = any(LocalScopeVariable lsv |
strictcount(Callable c |
c = any(AssignableDefinition ad | ad.getTarget() = lsv).getEnclosingCallable()
) = 1
) and
(
forex(Ssa::Definition def0 |
this = def0.getARead() |
this.isAlwaysNull0(def0)
)
or
exists(NullValue nv |
this.(G::GuardedExpr).mustHaveValue(nv) and
nv.isNull()
)
) and
not this instanceof G::NullGuardedExpr
}
/**
* Holds if this expression dereferences SSA source variable `v`, which is
* always `null`, and this expression can be reached from an SSA definition
* for `v` without passing through another such dereference.
*/
predicate isFirstAlwaysNull(Ssa::SourceVariable v) {
this.isAlwaysNull(v) and
defReaches(v.getAnSsaDefinition(), this, true)
}
/**
* Holds if assignable access `aa` is the first dereference of an assignable
* in a block, where it is suspected of being `null`.
*/
pragma[noinline]
private predicate nullDerefCandidate(Ssa::Definition origin) {
exists(Ssa::Definition ssa, BasicBlock bb |
potentialNullDereferenceAt(bb, _, ssa, this) |
defMaybeNullInBlockOrigin(origin, ssa, bb)
)
}
/**
* Holds if this expression dereferences SSA definition `def`, which may
* be `null`.
*/
predicate isMaybeNull(Ssa::Definition def, string msg, Element reason) {
exists(Ssa::Definition origin, BasicBlock bb |
this.nullDerefCandidate(origin) and
defMaybeNull(origin, msg, reason) and
potentialNullDereferenceAt(bb, _, def, this)
) and
not this.isAlwaysNull(def.getSourceVariable())
}
/**
* Holds if this expression dereferences SSA definition `def`, which may
* be `null`, and this expression can be reached from `def` without passing
* through another such dereference.
*/
predicate isFirstMaybeNull(Ssa::Definition def, string msg, Element reason) {
this.isMaybeNull(def, msg, reason) and
defReaches(def, this, false)
}
} }

View File

@@ -422,9 +422,18 @@ module Ssa {
* Gets an SSA definition that has this variable as its underlying * Gets an SSA definition that has this variable as its underlying
* source variable. * source variable.
*/ */
deprecated
Definition getAnDefinition() { Definition getAnDefinition() {
result.getSourceVariable() = this result.getSourceVariable() = this
} }
/**
* Gets an SSA definition that has this variable as its underlying
* source variable.
*/
Definition getAnSsaDefinition() {
result.getSourceVariable() = this
}
} }
/** Provides different types of `SourceVariable`s. */ /** Provides different types of `SourceVariable`s. */
@@ -1113,7 +1122,8 @@ module Ssa {
call = succ | call = succ |
callable = call.getTarget() or callable = call.getTarget() or
callable = call.getTarget().(Method).getAnOverrider+() or callable = call.getTarget().(Method).getAnOverrider+() or
callable = call.getTarget().(Method).getAnUltimateImplementor() callable = call.getTarget().(Method).getAnUltimateImplementor() or
callable = getARuntimeDelegateTarget(call)
) )
or or
pred = succ.(DelegateCreation).getArgument() pred = succ.(DelegateCreation).getArgument()

View File

@@ -126,6 +126,7 @@
| Guards.cs:132:16:132:16 | access to parameter s | Guards.cs:130:13:130:21 | ... is ... | Guards.cs:130:13:130:13 | access to parameter s | false | | Guards.cs:132:16:132:16 | access to parameter s | Guards.cs:130:13:130:21 | ... is ... | Guards.cs:130:13:130:13 | access to parameter s | false |
| Guards.cs:138:20:138:20 | access to parameter s | Guards.cs:137:13:137:13 | access to parameter s | Guards.cs:137:13:137:13 | access to parameter s | non-null | | Guards.cs:138:20:138:20 | access to parameter s | Guards.cs:137:13:137:13 | access to parameter s | Guards.cs:137:13:137:13 | access to parameter s | non-null |
| Guards.cs:138:20:138:20 | access to parameter s | Guards.cs:137:13:137:23 | ... is ... | Guards.cs:137:13:137:13 | access to parameter s | true | | Guards.cs:138:20:138:20 | access to parameter s | Guards.cs:137:13:137:23 | ... is ... | Guards.cs:137:13:137:13 | access to parameter s | true |
| Guards.cs:139:16:139:16 | access to parameter s | Guards.cs:137:13:137:13 | access to parameter s | Guards.cs:137:13:137:13 | access to parameter s | null |
| Guards.cs:139:16:139:16 | access to parameter s | Guards.cs:137:13:137:23 | ... is ... | Guards.cs:137:13:137:13 | access to parameter s | false | | Guards.cs:139:16:139:16 | access to parameter s | Guards.cs:137:13:137:23 | ... is ... | Guards.cs:137:13:137:13 | access to parameter s | false |
| Guards.cs:146:16:146:16 | access to parameter o | Guards.cs:144:13:144:25 | ... is ... | Guards.cs:144:13:144:13 | access to parameter o | false | | Guards.cs:146:16:146:16 | access to parameter o | Guards.cs:144:13:144:25 | ... is ... | Guards.cs:144:13:144:13 | access to parameter o | false |
| Guards.cs:154:24:154:24 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | match case Action<Object>: | | Guards.cs:154:24:154:24 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | Guards.cs:151:17:151:17 | access to parameter o | match case Action<Object>: |

View File

@@ -1,41 +1,57 @@
| Assert.cs:9:16:9:32 | String s = ... | non-null | Assert.cs:9:16:9:16 | access to local variable s | non-null |
| Assert.cs:9:16:9:32 | String s = ... | null | Assert.cs:9:16:9:16 | access to local variable s | null |
| Assert.cs:10:22:10:22 | access to local variable s | non-null | Assert.cs:9:20:9:32 | ... ? ... : ... | non-null | | Assert.cs:10:22:10:22 | access to local variable s | non-null | Assert.cs:9:20:9:32 | ... ? ... : ... | non-null |
| Assert.cs:10:22:10:22 | access to local variable s | null | Assert.cs:9:20:9:32 | ... ? ... : ... | null | | Assert.cs:10:22:10:22 | access to local variable s | null | Assert.cs:9:20:9:32 | ... ? ... : ... | null |
| Assert.cs:10:22:10:30 | ... != ... | false | Assert.cs:10:22:10:22 | access to local variable s | null | | Assert.cs:10:22:10:30 | ... != ... | false | Assert.cs:10:22:10:22 | access to local variable s | null |
| Assert.cs:10:22:10:30 | ... != ... | true | Assert.cs:10:22:10:22 | access to local variable s | non-null | | Assert.cs:10:22:10:30 | ... != ... | true | Assert.cs:10:22:10:22 | access to local variable s | non-null |
| Assert.cs:11:27:11:27 | access to local variable s | non-null | Assert.cs:9:20:9:32 | ... ? ... : ... | non-null | | Assert.cs:11:27:11:27 | access to local variable s | non-null | Assert.cs:9:20:9:32 | ... ? ... : ... | non-null |
| Assert.cs:11:27:11:27 | access to local variable s | null | Assert.cs:9:20:9:32 | ... ? ... : ... | null | | Assert.cs:11:27:11:27 | access to local variable s | null | Assert.cs:9:20:9:32 | ... ? ... : ... | null |
| Assert.cs:16:16:16:32 | String s = ... | non-null | Assert.cs:16:16:16:16 | access to local variable s | non-null |
| Assert.cs:16:16:16:32 | String s = ... | null | Assert.cs:16:16:16:16 | access to local variable s | null |
| Assert.cs:17:23:17:23 | access to local variable s | non-null | Assert.cs:16:20:16:32 | ... ? ... : ... | non-null | | Assert.cs:17:23:17:23 | access to local variable s | non-null | Assert.cs:16:20:16:32 | ... ? ... : ... | non-null |
| Assert.cs:17:23:17:23 | access to local variable s | null | Assert.cs:16:20:16:32 | ... ? ... : ... | null | | Assert.cs:17:23:17:23 | access to local variable s | null | Assert.cs:16:20:16:32 | ... ? ... : ... | null |
| Assert.cs:18:27:18:27 | access to local variable s | non-null | Assert.cs:16:20:16:32 | ... ? ... : ... | non-null | | Assert.cs:18:27:18:27 | access to local variable s | non-null | Assert.cs:16:20:16:32 | ... ? ... : ... | non-null |
| Assert.cs:18:27:18:27 | access to local variable s | null | Assert.cs:16:20:16:32 | ... ? ... : ... | null | | Assert.cs:18:27:18:27 | access to local variable s | null | Assert.cs:16:20:16:32 | ... ? ... : ... | null |
| Assert.cs:23:16:23:32 | String s = ... | non-null | Assert.cs:23:16:23:16 | access to local variable s | non-null |
| Assert.cs:23:16:23:32 | String s = ... | null | Assert.cs:23:16:23:16 | access to local variable s | null |
| Assert.cs:24:26:24:26 | access to local variable s | non-null | Assert.cs:23:20:23:32 | ... ? ... : ... | non-null | | Assert.cs:24:26:24:26 | access to local variable s | non-null | Assert.cs:23:20:23:32 | ... ? ... : ... | non-null |
| Assert.cs:24:26:24:26 | access to local variable s | null | Assert.cs:23:20:23:32 | ... ? ... : ... | null | | Assert.cs:24:26:24:26 | access to local variable s | null | Assert.cs:23:20:23:32 | ... ? ... : ... | null |
| Assert.cs:25:27:25:27 | access to local variable s | non-null | Assert.cs:23:20:23:32 | ... ? ... : ... | non-null | | Assert.cs:25:27:25:27 | access to local variable s | non-null | Assert.cs:23:20:23:32 | ... ? ... : ... | non-null |
| Assert.cs:25:27:25:27 | access to local variable s | null | Assert.cs:23:20:23:32 | ... ? ... : ... | null | | Assert.cs:25:27:25:27 | access to local variable s | null | Assert.cs:23:20:23:32 | ... ? ... : ... | null |
| Assert.cs:30:16:30:32 | String s = ... | non-null | Assert.cs:30:16:30:16 | access to local variable s | non-null |
| Assert.cs:30:16:30:32 | String s = ... | null | Assert.cs:30:16:30:16 | access to local variable s | null |
| Assert.cs:31:23:31:23 | access to local variable s | non-null | Assert.cs:30:20:30:32 | ... ? ... : ... | non-null | | Assert.cs:31:23:31:23 | access to local variable s | non-null | Assert.cs:30:20:30:32 | ... ? ... : ... | non-null |
| Assert.cs:31:23:31:23 | access to local variable s | null | Assert.cs:30:20:30:32 | ... ? ... : ... | null | | Assert.cs:31:23:31:23 | access to local variable s | null | Assert.cs:30:20:30:32 | ... ? ... : ... | null |
| Assert.cs:31:23:31:31 | ... == ... | false | Assert.cs:31:23:31:23 | access to local variable s | non-null | | Assert.cs:31:23:31:31 | ... == ... | false | Assert.cs:31:23:31:23 | access to local variable s | non-null |
| Assert.cs:31:23:31:31 | ... == ... | true | Assert.cs:31:23:31:23 | access to local variable s | null | | Assert.cs:31:23:31:31 | ... == ... | true | Assert.cs:31:23:31:23 | access to local variable s | null |
| Assert.cs:32:27:32:27 | access to local variable s | non-null | Assert.cs:30:20:30:32 | ... ? ... : ... | non-null | | Assert.cs:32:27:32:27 | access to local variable s | non-null | Assert.cs:30:20:30:32 | ... ? ... : ... | non-null |
| Assert.cs:32:27:32:27 | access to local variable s | null | Assert.cs:30:20:30:32 | ... ? ... : ... | null | | Assert.cs:32:27:32:27 | access to local variable s | null | Assert.cs:30:20:30:32 | ... ? ... : ... | null |
| Assert.cs:37:16:37:32 | String s = ... | non-null | Assert.cs:37:16:37:16 | access to local variable s | non-null |
| Assert.cs:37:16:37:32 | String s = ... | null | Assert.cs:37:16:37:16 | access to local variable s | null |
| Assert.cs:38:23:38:23 | access to local variable s | non-null | Assert.cs:37:20:37:32 | ... ? ... : ... | non-null | | Assert.cs:38:23:38:23 | access to local variable s | non-null | Assert.cs:37:20:37:32 | ... ? ... : ... | non-null |
| Assert.cs:38:23:38:23 | access to local variable s | null | Assert.cs:37:20:37:32 | ... ? ... : ... | null | | Assert.cs:38:23:38:23 | access to local variable s | null | Assert.cs:37:20:37:32 | ... ? ... : ... | null |
| Assert.cs:38:23:38:31 | ... != ... | false | Assert.cs:38:23:38:23 | access to local variable s | null | | Assert.cs:38:23:38:31 | ... != ... | false | Assert.cs:38:23:38:23 | access to local variable s | null |
| Assert.cs:38:23:38:31 | ... != ... | true | Assert.cs:38:23:38:23 | access to local variable s | non-null | | Assert.cs:38:23:38:31 | ... != ... | true | Assert.cs:38:23:38:23 | access to local variable s | non-null |
| Assert.cs:39:27:39:27 | access to local variable s | non-null | Assert.cs:37:20:37:32 | ... ? ... : ... | non-null | | Assert.cs:39:27:39:27 | access to local variable s | non-null | Assert.cs:37:20:37:32 | ... ? ... : ... | non-null |
| Assert.cs:39:27:39:27 | access to local variable s | null | Assert.cs:37:20:37:32 | ... ? ... : ... | null | | Assert.cs:39:27:39:27 | access to local variable s | null | Assert.cs:37:20:37:32 | ... ? ... : ... | null |
| Assert.cs:44:16:44:32 | String s = ... | non-null | Assert.cs:44:16:44:16 | access to local variable s | non-null |
| Assert.cs:44:16:44:32 | String s = ... | null | Assert.cs:44:16:44:16 | access to local variable s | null |
| Assert.cs:45:24:45:24 | access to local variable s | non-null | Assert.cs:44:20:44:32 | ... ? ... : ... | non-null | | Assert.cs:45:24:45:24 | access to local variable s | non-null | Assert.cs:44:20:44:32 | ... ? ... : ... | non-null |
| Assert.cs:45:24:45:24 | access to local variable s | null | Assert.cs:44:20:44:32 | ... ? ... : ... | null | | Assert.cs:45:24:45:24 | access to local variable s | null | Assert.cs:44:20:44:32 | ... ? ... : ... | null |
| Assert.cs:45:24:45:32 | ... != ... | false | Assert.cs:45:24:45:24 | access to local variable s | null | | Assert.cs:45:24:45:32 | ... != ... | false | Assert.cs:45:24:45:24 | access to local variable s | null |
| Assert.cs:45:24:45:32 | ... != ... | true | Assert.cs:45:24:45:24 | access to local variable s | non-null | | Assert.cs:45:24:45:32 | ... != ... | true | Assert.cs:45:24:45:24 | access to local variable s | non-null |
| Assert.cs:46:27:46:27 | access to local variable s | non-null | Assert.cs:44:20:44:32 | ... ? ... : ... | non-null | | Assert.cs:46:27:46:27 | access to local variable s | non-null | Assert.cs:44:20:44:32 | ... ? ... : ... | non-null |
| Assert.cs:46:27:46:27 | access to local variable s | null | Assert.cs:44:20:44:32 | ... ? ... : ... | null | | Assert.cs:46:27:46:27 | access to local variable s | null | Assert.cs:44:20:44:32 | ... ? ... : ... | null |
| Assert.cs:51:16:51:32 | String s = ... | non-null | Assert.cs:51:16:51:16 | access to local variable s | non-null |
| Assert.cs:51:16:51:32 | String s = ... | null | Assert.cs:51:16:51:16 | access to local variable s | null |
| Assert.cs:52:24:52:24 | access to local variable s | non-null | Assert.cs:51:20:51:32 | ... ? ... : ... | non-null | | Assert.cs:52:24:52:24 | access to local variable s | non-null | Assert.cs:51:20:51:32 | ... ? ... : ... | non-null |
| Assert.cs:52:24:52:24 | access to local variable s | null | Assert.cs:51:20:51:32 | ... ? ... : ... | null | | Assert.cs:52:24:52:24 | access to local variable s | null | Assert.cs:51:20:51:32 | ... ? ... : ... | null |
| Assert.cs:52:24:52:32 | ... == ... | false | Assert.cs:52:24:52:24 | access to local variable s | non-null | | Assert.cs:52:24:52:32 | ... == ... | false | Assert.cs:52:24:52:24 | access to local variable s | non-null |
| Assert.cs:52:24:52:32 | ... == ... | true | Assert.cs:52:24:52:24 | access to local variable s | null | | Assert.cs:52:24:52:32 | ... == ... | true | Assert.cs:52:24:52:24 | access to local variable s | null |
| Assert.cs:53:27:53:27 | access to local variable s | non-null | Assert.cs:51:20:51:32 | ... ? ... : ... | non-null | | Assert.cs:53:27:53:27 | access to local variable s | non-null | Assert.cs:51:20:51:32 | ... ? ... : ... | non-null |
| Assert.cs:53:27:53:27 | access to local variable s | null | Assert.cs:51:20:51:32 | ... ? ... : ... | null | | Assert.cs:53:27:53:27 | access to local variable s | null | Assert.cs:51:20:51:32 | ... ? ... : ... | null |
| Assert.cs:58:16:58:32 | String s = ... | non-null | Assert.cs:58:16:58:16 | access to local variable s | non-null |
| Assert.cs:58:16:58:32 | String s = ... | null | Assert.cs:58:16:58:16 | access to local variable s | null |
| Assert.cs:59:23:59:23 | access to local variable s | non-null | Assert.cs:58:20:58:32 | ... ? ... : ... | non-null | | Assert.cs:59:23:59:23 | access to local variable s | non-null | Assert.cs:58:20:58:32 | ... ? ... : ... | non-null |
| Assert.cs:59:23:59:23 | access to local variable s | null | Assert.cs:58:20:58:32 | ... ? ... : ... | null | | Assert.cs:59:23:59:23 | access to local variable s | null | Assert.cs:58:20:58:32 | ... ? ... : ... | null |
| Assert.cs:59:23:59:31 | ... != ... | false | Assert.cs:59:23:59:23 | access to local variable s | null | | Assert.cs:59:23:59:31 | ... != ... | false | Assert.cs:59:23:59:23 | access to local variable s | null |
@@ -44,6 +60,8 @@
| Assert.cs:59:23:59:36 | ... && ... | true | Assert.cs:59:36:59:36 | access to parameter b | true | | Assert.cs:59:23:59:36 | ... && ... | true | Assert.cs:59:36:59:36 | access to parameter b | true |
| Assert.cs:60:27:60:27 | access to local variable s | non-null | Assert.cs:58:20:58:32 | ... ? ... : ... | non-null | | Assert.cs:60:27:60:27 | access to local variable s | non-null | Assert.cs:58:20:58:32 | ... ? ... : ... | non-null |
| Assert.cs:60:27:60:27 | access to local variable s | null | Assert.cs:58:20:58:32 | ... ? ... : ... | null | | Assert.cs:60:27:60:27 | access to local variable s | null | Assert.cs:58:20:58:32 | ... ? ... : ... | null |
| Assert.cs:65:16:65:32 | String s = ... | non-null | Assert.cs:65:16:65:16 | access to local variable s | non-null |
| Assert.cs:65:16:65:32 | String s = ... | null | Assert.cs:65:16:65:16 | access to local variable s | null |
| Assert.cs:66:24:66:24 | access to local variable s | non-null | Assert.cs:65:20:65:32 | ... ? ... : ... | non-null | | Assert.cs:66:24:66:24 | access to local variable s | non-null | Assert.cs:65:20:65:32 | ... ? ... : ... | non-null |
| Assert.cs:66:24:66:24 | access to local variable s | null | Assert.cs:65:20:65:32 | ... ? ... : ... | null | | Assert.cs:66:24:66:24 | access to local variable s | null | Assert.cs:65:20:65:32 | ... ? ... : ... | null |
| Assert.cs:66:24:66:32 | ... == ... | false | Assert.cs:66:24:66:24 | access to local variable s | non-null | | Assert.cs:66:24:66:32 | ... == ... | false | Assert.cs:66:24:66:24 | access to local variable s | non-null |
@@ -52,6 +70,8 @@
| Assert.cs:66:24:66:37 | ... \|\| ... | false | Assert.cs:66:37:66:37 | access to parameter b | false | | Assert.cs:66:24:66:37 | ... \|\| ... | false | Assert.cs:66:37:66:37 | access to parameter b | false |
| Assert.cs:67:27:67:27 | access to local variable s | non-null | Assert.cs:65:20:65:32 | ... ? ... : ... | non-null | | Assert.cs:67:27:67:27 | access to local variable s | non-null | Assert.cs:65:20:65:32 | ... ? ... : ... | non-null |
| Assert.cs:67:27:67:27 | access to local variable s | null | Assert.cs:65:20:65:32 | ... ? ... : ... | null | | Assert.cs:67:27:67:27 | access to local variable s | null | Assert.cs:65:20:65:32 | ... ? ... : ... | null |
| Assert.cs:72:16:72:32 | String s = ... | non-null | Assert.cs:72:16:72:16 | access to local variable s | non-null |
| Assert.cs:72:16:72:32 | String s = ... | null | Assert.cs:72:16:72:16 | access to local variable s | null |
| Assert.cs:73:23:73:23 | access to local variable s | non-null | Assert.cs:72:20:72:32 | ... ? ... : ... | non-null | | Assert.cs:73:23:73:23 | access to local variable s | non-null | Assert.cs:72:20:72:32 | ... ? ... : ... | non-null |
| Assert.cs:73:23:73:23 | access to local variable s | null | Assert.cs:72:20:72:32 | ... ? ... : ... | null | | Assert.cs:73:23:73:23 | access to local variable s | null | Assert.cs:72:20:72:32 | ... ? ... : ... | null |
| Assert.cs:73:23:73:31 | ... == ... | false | Assert.cs:73:23:73:23 | access to local variable s | non-null | | Assert.cs:73:23:73:31 | ... == ... | false | Assert.cs:73:23:73:23 | access to local variable s | non-null |
@@ -60,6 +80,8 @@
| Assert.cs:73:23:73:36 | ... && ... | true | Assert.cs:73:36:73:36 | access to parameter b | true | | Assert.cs:73:23:73:36 | ... && ... | true | Assert.cs:73:36:73:36 | access to parameter b | true |
| Assert.cs:74:27:74:27 | access to local variable s | non-null | Assert.cs:72:20:72:32 | ... ? ... : ... | non-null | | Assert.cs:74:27:74:27 | access to local variable s | non-null | Assert.cs:72:20:72:32 | ... ? ... : ... | non-null |
| Assert.cs:74:27:74:27 | access to local variable s | null | Assert.cs:72:20:72:32 | ... ? ... : ... | null | | Assert.cs:74:27:74:27 | access to local variable s | null | Assert.cs:72:20:72:32 | ... ? ... : ... | null |
| Assert.cs:79:16:79:32 | String s = ... | non-null | Assert.cs:79:16:79:16 | access to local variable s | non-null |
| Assert.cs:79:16:79:32 | String s = ... | null | Assert.cs:79:16:79:16 | access to local variable s | null |
| Assert.cs:80:24:80:24 | access to local variable s | non-null | Assert.cs:79:20:79:32 | ... ? ... : ... | non-null | | Assert.cs:80:24:80:24 | access to local variable s | non-null | Assert.cs:79:20:79:32 | ... ? ... : ... | non-null |
| Assert.cs:80:24:80:24 | access to local variable s | null | Assert.cs:79:20:79:32 | ... ? ... : ... | null | | Assert.cs:80:24:80:24 | access to local variable s | null | Assert.cs:79:20:79:32 | ... ? ... : ... | null |
| Assert.cs:80:24:80:32 | ... != ... | false | Assert.cs:80:24:80:24 | access to local variable s | null | | Assert.cs:80:24:80:32 | ... != ... | false | Assert.cs:80:24:80:24 | access to local variable s | null |
@@ -85,12 +107,16 @@
| Guards.cs:32:40:32:51 | !... | true | Guards.cs:32:42:32:50 | ... == ... | false | | Guards.cs:32:40:32:51 | !... | true | Guards.cs:32:42:32:50 | ... == ... | false |
| Guards.cs:32:42:32:50 | ... == ... | false | Guards.cs:32:42:32:42 | access to parameter y | non-null | | Guards.cs:32:42:32:50 | ... == ... | false | Guards.cs:32:42:32:42 | access to parameter y | non-null |
| Guards.cs:32:42:32:50 | ... == ... | true | Guards.cs:32:42:32:42 | access to parameter y | null | | Guards.cs:32:42:32:50 | ... == ... | true | Guards.cs:32:42:32:42 | access to parameter y | null |
| Guards.cs:33:31:33:35 | ... + ... | non-null | Guards.cs:33:31:33:31 | access to parameter x | non-null |
| Guards.cs:33:31:33:35 | ... + ... | non-null | Guards.cs:33:35:33:35 | access to parameter y | non-null |
| Guards.cs:35:13:35:21 | ... == ... | false | Guards.cs:35:13:35:13 | access to parameter x | non-null | | Guards.cs:35:13:35:21 | ... == ... | false | Guards.cs:35:13:35:13 | access to parameter x | non-null |
| Guards.cs:35:13:35:21 | ... == ... | true | Guards.cs:35:13:35:13 | access to parameter x | null | | Guards.cs:35:13:35:21 | ... == ... | true | Guards.cs:35:13:35:13 | access to parameter x | null |
| Guards.cs:35:13:35:34 | ... \|\| ... | false | Guards.cs:35:13:35:21 | ... == ... | false | | Guards.cs:35:13:35:34 | ... \|\| ... | false | Guards.cs:35:13:35:21 | ... == ... | false |
| Guards.cs:35:13:35:34 | ... \|\| ... | false | Guards.cs:35:26:35:34 | ... == ... | false | | Guards.cs:35:13:35:34 | ... \|\| ... | false | Guards.cs:35:26:35:34 | ... == ... | false |
| Guards.cs:35:26:35:34 | ... == ... | false | Guards.cs:35:26:35:26 | access to parameter y | non-null | | Guards.cs:35:26:35:34 | ... == ... | false | Guards.cs:35:26:35:26 | access to parameter y | non-null |
| Guards.cs:35:26:35:34 | ... == ... | true | Guards.cs:35:26:35:26 | access to parameter y | null | | Guards.cs:35:26:35:34 | ... == ... | true | Guards.cs:35:26:35:26 | access to parameter y | null |
| Guards.cs:36:32:36:36 | ... + ... | non-null | Guards.cs:36:32:36:32 | access to parameter x | non-null |
| Guards.cs:36:32:36:36 | ... + ... | non-null | Guards.cs:36:36:36:36 | access to parameter y | non-null |
| Guards.cs:38:13:38:37 | !... | false | Guards.cs:38:15:38:36 | ... \|\| ... | true | | Guards.cs:38:13:38:37 | !... | false | Guards.cs:38:15:38:36 | ... \|\| ... | true |
| Guards.cs:38:13:38:37 | !... | true | Guards.cs:38:15:38:36 | ... \|\| ... | false | | Guards.cs:38:13:38:37 | !... | true | Guards.cs:38:15:38:36 | ... \|\| ... | false |
| Guards.cs:38:15:38:23 | ... == ... | false | Guards.cs:38:15:38:15 | access to parameter x | non-null | | Guards.cs:38:15:38:23 | ... == ... | false | Guards.cs:38:15:38:15 | access to parameter x | non-null |
@@ -99,6 +125,8 @@
| Guards.cs:38:15:38:36 | ... \|\| ... | false | Guards.cs:38:28:38:36 | ... == ... | false | | Guards.cs:38:15:38:36 | ... \|\| ... | false | Guards.cs:38:28:38:36 | ... == ... | false |
| Guards.cs:38:28:38:36 | ... == ... | false | Guards.cs:38:28:38:28 | access to parameter y | non-null | | Guards.cs:38:28:38:36 | ... == ... | false | Guards.cs:38:28:38:28 | access to parameter y | non-null |
| Guards.cs:38:28:38:36 | ... == ... | true | Guards.cs:38:28:38:28 | access to parameter y | null | | Guards.cs:38:28:38:36 | ... == ... | true | Guards.cs:38:28:38:28 | access to parameter y | null |
| Guards.cs:39:31:39:35 | ... + ... | non-null | Guards.cs:39:31:39:31 | access to parameter x | non-null |
| Guards.cs:39:31:39:35 | ... + ... | non-null | Guards.cs:39:35:39:35 | access to parameter y | non-null |
| Guards.cs:41:13:41:39 | !... | false | Guards.cs:41:14:41:39 | !... | true | | Guards.cs:41:13:41:39 | !... | false | Guards.cs:41:14:41:39 | !... | true |
| Guards.cs:41:13:41:39 | !... | true | Guards.cs:41:14:41:39 | !... | false | | Guards.cs:41:13:41:39 | !... | true | Guards.cs:41:14:41:39 | !... | false |
| Guards.cs:41:14:41:39 | !... | false | Guards.cs:41:15:41:39 | !... | true | | Guards.cs:41:14:41:39 | !... | false | Guards.cs:41:15:41:39 | !... | true |
@@ -111,6 +139,8 @@
| Guards.cs:41:17:41:38 | ... && ... | true | Guards.cs:41:30:41:38 | ... != ... | true | | Guards.cs:41:17:41:38 | ... && ... | true | Guards.cs:41:30:41:38 | ... != ... | true |
| Guards.cs:41:30:41:38 | ... != ... | false | Guards.cs:41:30:41:30 | access to parameter y | null | | Guards.cs:41:30:41:38 | ... != ... | false | Guards.cs:41:30:41:30 | access to parameter y | null |
| Guards.cs:41:30:41:38 | ... != ... | true | Guards.cs:41:30:41:30 | access to parameter y | non-null | | Guards.cs:41:30:41:38 | ... != ... | true | Guards.cs:41:30:41:30 | access to parameter y | non-null |
| Guards.cs:42:32:42:36 | ... + ... | non-null | Guards.cs:42:32:42:32 | access to parameter x | non-null |
| Guards.cs:42:32:42:36 | ... + ... | non-null | Guards.cs:42:36:42:36 | access to parameter y | non-null |
| Guards.cs:44:13:44:25 | ... != ... | false | Guards.cs:44:13:44:17 | access to field Field | null | | Guards.cs:44:13:44:25 | ... != ... | false | Guards.cs:44:13:44:17 | access to field Field | null |
| Guards.cs:44:13:44:25 | ... != ... | true | Guards.cs:44:13:44:17 | access to field Field | non-null | | Guards.cs:44:13:44:25 | ... != ... | true | Guards.cs:44:13:44:17 | access to field Field | non-null |
| Guards.cs:47:13:47:25 | ... != ... | false | Guards.cs:47:13:47:17 | access to field Field | null | | Guards.cs:47:13:47:25 | ... != ... | false | Guards.cs:47:13:47:17 | access to field Field | null |
@@ -121,6 +151,10 @@
| Guards.cs:60:13:60:45 | ... == ... | true | Guards.cs:60:13:60:37 | access to field Field | null | | Guards.cs:60:13:60:45 | ... == ... | true | Guards.cs:60:13:60:37 | access to field Field | null |
| Guards.cs:68:16:68:24 | ... != ... | false | Guards.cs:68:16:68:16 | access to parameter s | null | | Guards.cs:68:16:68:24 | ... != ... | false | Guards.cs:68:16:68:16 | access to parameter s | null |
| Guards.cs:68:16:68:24 | ... != ... | true | Guards.cs:68:16:68:16 | access to parameter s | non-null | | Guards.cs:68:16:68:24 | ... != ... | true | Guards.cs:68:16:68:16 | access to parameter s | non-null |
| Guards.cs:71:13:71:20 | ... = ... | non-null | Guards.cs:71:13:71:13 | access to parameter s | non-null |
| Guards.cs:71:13:71:20 | ... = ... | non-null | Guards.cs:71:17:71:20 | null | non-null |
| Guards.cs:71:13:71:20 | ... = ... | null | Guards.cs:71:13:71:13 | access to parameter s | null |
| Guards.cs:71:13:71:20 | ... = ... | null | Guards.cs:71:17:71:20 | null | null |
| Guards.cs:72:31:72:31 | access to parameter s | non-null | Guards.cs:71:17:71:20 | null | non-null | | Guards.cs:72:31:72:31 | access to parameter s | non-null | Guards.cs:71:17:71:20 | null | non-null |
| Guards.cs:72:31:72:31 | access to parameter s | null | Guards.cs:71:17:71:20 | null | null | | Guards.cs:72:31:72:31 | access to parameter s | null | Guards.cs:71:17:71:20 | null | null |
| Guards.cs:78:13:78:26 | ... == ... | true | Guards.cs:78:15:78:21 | access to property Length | non-null | | Guards.cs:78:13:78:26 | ... == ... | true | Guards.cs:78:15:78:21 | access to property Length | non-null |
@@ -143,6 +177,7 @@
| Guards.cs:88:15:88:21 | access to property Length | non-null | Guards.cs:88:13:88:13 | access to parameter s | non-null | | Guards.cs:88:15:88:21 | access to property Length | non-null | Guards.cs:88:13:88:13 | access to parameter s | non-null |
| Guards.cs:88:15:88:21 | access to property Length | null | Guards.cs:88:13:88:13 | access to parameter s | null | | Guards.cs:88:15:88:21 | access to property Length | null | Guards.cs:88:13:88:13 | access to parameter s | null |
| Guards.cs:92:13:92:25 | ... - ... | non-null | Guards.cs:92:15:92:21 | access to property Length | non-null | | Guards.cs:92:13:92:25 | ... - ... | non-null | Guards.cs:92:15:92:21 | access to property Length | non-null |
| Guards.cs:92:13:92:25 | ... - ... | non-null | Guards.cs:92:25:92:25 | (...) ... | non-null |
| Guards.cs:92:13:92:25 | ... - ... | null | Guards.cs:92:15:92:21 | access to property Length | null | | Guards.cs:92:13:92:25 | ... - ... | null | Guards.cs:92:15:92:21 | access to property Length | null |
| Guards.cs:92:13:92:30 | ... != ... | false | Guards.cs:92:13:92:25 | ... - ... | non-null | | Guards.cs:92:13:92:30 | ... != ... | false | Guards.cs:92:13:92:25 | ... - ... | non-null |
| Guards.cs:92:15:92:21 | access to property Length | non-null | Guards.cs:92:13:92:13 | access to parameter s | non-null | | Guards.cs:92:15:92:21 | access to property Length | non-null | Guards.cs:92:13:92:13 | access to parameter s | non-null |
@@ -150,10 +185,29 @@
| Guards.cs:96:13:96:19 | ... == ... | true | Guards.cs:96:13:96:13 | access to parameter s | non-null | | Guards.cs:96:13:96:19 | ... == ... | true | Guards.cs:96:13:96:13 | access to parameter s | non-null |
| Guards.cs:104:13:104:45 | ... == ... | false | Guards.cs:104:13:104:37 | access to field Field | non-null | | Guards.cs:104:13:104:45 | ... == ... | false | Guards.cs:104:13:104:37 | access to field Field | non-null |
| Guards.cs:104:13:104:45 | ... == ... | true | Guards.cs:104:13:104:37 | access to field Field | null | | Guards.cs:104:13:104:45 | ... == ... | true | Guards.cs:104:13:104:37 | access to field Field | null |
| Guards.cs:106:9:106:25 | ... = ... | non-null | Guards.cs:106:9:106:18 | access to property Property | non-null |
| Guards.cs:106:9:106:25 | ... = ... | non-null | Guards.cs:106:22:106:25 | null | non-null |
| Guards.cs:106:9:106:25 | ... = ... | null | Guards.cs:106:9:106:18 | access to property Property | null |
| Guards.cs:106:9:106:25 | ... = ... | null | Guards.cs:106:22:106:25 | null | null |
| Guards.cs:113:13:114:38 | String dummy = ... | non-null | Guards.cs:113:13:113:17 | access to local variable dummy | non-null |
| Guards.cs:113:13:114:38 | String dummy = ... | null | Guards.cs:113:13:113:17 | access to local variable dummy | null |
| Guards.cs:115:9:115:55 | ... = ... | non-null | Guards.cs:115:9:115:13 | access to local variable dummy | non-null |
| Guards.cs:115:9:115:55 | ... = ... | non-null | Guards.cs:115:17:115:55 | ... ?? ... | non-null |
| Guards.cs:115:9:115:55 | ... = ... | null | Guards.cs:115:9:115:13 | access to local variable dummy | null |
| Guards.cs:115:9:115:55 | ... = ... | null | Guards.cs:115:17:115:55 | ... ?? ... | null |
| Guards.cs:117:9:117:25 | ... = ... | non-null | Guards.cs:117:9:117:18 | access to property Property | non-null |
| Guards.cs:117:9:117:25 | ... = ... | non-null | Guards.cs:117:22:117:25 | null | non-null |
| Guards.cs:117:9:117:25 | ... = ... | null | Guards.cs:117:9:117:18 | access to property Property | null |
| Guards.cs:117:9:117:25 | ... = ... | null | Guards.cs:117:22:117:25 | null | null |
| Guards.cs:124:13:124:30 | Boolean b1 = ... | false | Guards.cs:124:13:124:14 | access to local variable b1 | false |
| Guards.cs:124:13:124:30 | Boolean b1 = ... | true | Guards.cs:124:13:124:14 | access to local variable b1 | true |
| Guards.cs:125:13:125:31 | Nullable<Boolean> b2 = ... | non-null | Guards.cs:125:13:125:14 | access to local variable b2 | non-null |
| Guards.cs:125:13:125:31 | Nullable<Boolean> b2 = ... | null | Guards.cs:125:13:125:14 | access to local variable b2 | null |
| Guards.cs:125:21:125:31 | call to method Equals | non-null | Guards.cs:125:18:125:19 | access to parameter s1 | non-null | | Guards.cs:125:21:125:31 | call to method Equals | non-null | Guards.cs:125:18:125:19 | access to parameter s1 | non-null |
| Guards.cs:125:21:125:31 | call to method Equals | null | Guards.cs:125:18:125:19 | access to parameter s1 | null | | Guards.cs:125:21:125:31 | call to method Equals | null | Guards.cs:125:18:125:19 | access to parameter s1 | null |
| Guards.cs:130:13:130:21 | ... is ... | false | Guards.cs:130:13:130:13 | access to parameter s | non-null | | Guards.cs:130:13:130:21 | ... is ... | false | Guards.cs:130:13:130:13 | access to parameter s | non-null |
| Guards.cs:130:13:130:21 | ... is ... | true | Guards.cs:130:13:130:13 | access to parameter s | null | | Guards.cs:130:13:130:21 | ... is ... | true | Guards.cs:130:13:130:13 | access to parameter s | null |
| Guards.cs:137:13:137:23 | ... is ... | false | Guards.cs:137:13:137:13 | access to parameter s | null |
| Guards.cs:137:13:137:23 | ... is ... | true | Guards.cs:137:13:137:13 | access to parameter s | non-null | | Guards.cs:137:13:137:23 | ... is ... | true | Guards.cs:137:13:137:13 | access to parameter s | non-null |
| Guards.cs:144:13:144:25 | ... is ... | true | Guards.cs:144:13:144:13 | access to parameter o | non-null | | Guards.cs:144:13:144:25 | ... is ... | true | Guards.cs:144:13:144:13 | access to parameter o | non-null |
| Guards.cs:145:20:145:20 | access to local variable s | non-null | Guards.cs:144:13:144:13 | access to parameter o | non-null | | Guards.cs:145:20:145:20 | access to local variable s | non-null | Guards.cs:144:13:144:13 | access to parameter o | non-null |
@@ -224,7 +278,13 @@
| Splitting.cs:105:22:105:30 | ... != ... | true | Splitting.cs:105:22:105:22 | access to parameter o | non-null | | Splitting.cs:105:22:105:30 | ... != ... | true | Splitting.cs:105:22:105:22 | access to parameter o | non-null |
| Splitting.cs:116:22:116:30 | ... != ... | false | Splitting.cs:116:22:116:22 | access to parameter o | null | | Splitting.cs:116:22:116:30 | ... != ... | false | Splitting.cs:116:22:116:22 | access to parameter o | null |
| Splitting.cs:116:22:116:30 | ... != ... | true | Splitting.cs:116:22:116:22 | access to parameter o | non-null | | Splitting.cs:116:22:116:30 | ... != ... | true | Splitting.cs:116:22:116:22 | access to parameter o | non-null |
| Splitting.cs:125:16:125:23 | Object o = ... | non-null | Splitting.cs:125:16:125:16 | access to local variable o | non-null |
| Splitting.cs:125:16:125:23 | Object o = ... | null | Splitting.cs:125:16:125:16 | access to local variable o | null |
| Splitting.cs:128:17:128:25 | ... != ... | false | Splitting.cs:128:17:128:17 | access to local variable o | null | | Splitting.cs:128:17:128:25 | ... != ... | false | Splitting.cs:128:17:128:17 | access to local variable o | null |
| Splitting.cs:128:17:128:25 | ... != ... | true | Splitting.cs:128:17:128:17 | access to local variable o | non-null | | Splitting.cs:128:17:128:25 | ... != ... | true | Splitting.cs:128:17:128:17 | access to local variable o | non-null |
| Splitting.cs:132:17:132:29 | ... = ... | non-null | Splitting.cs:132:17:132:17 | access to local variable o | non-null |
| Splitting.cs:132:17:132:29 | ... = ... | non-null | Splitting.cs:132:21:132:29 | call to method M11 | non-null |
| Splitting.cs:132:17:132:29 | ... = ... | null | Splitting.cs:132:17:132:17 | access to local variable o | null |
| Splitting.cs:132:17:132:29 | ... = ... | null | Splitting.cs:132:21:132:29 | call to method M11 | null |
| Splitting.cs:133:17:133:17 | access to local variable o | non-null | Splitting.cs:132:21:132:29 | call to method M11 | non-null | | Splitting.cs:133:17:133:17 | access to local variable o | non-null | Splitting.cs:132:21:132:29 | call to method M11 | non-null |
| Splitting.cs:133:17:133:17 | access to local variable o | null | Splitting.cs:132:21:132:29 | call to method M11 | null | | Splitting.cs:133:17:133:17 | access to local variable o | null | Splitting.cs:132:21:132:29 | call to method M11 | null |

View File

@@ -47,7 +47,7 @@ class A
object varRef = null; object varRef = null;
TestMethod2(ref varRef); TestMethod2(ref varRef);
varRef.ToString(); // BAD (always) (false negative) varRef.ToString(); // BAD (always)
varRef = null; varRef = null;
TestMethod3(ref varRef); TestMethod3(ref varRef);

View File

@@ -101,7 +101,7 @@ public class C
{ {
if (Maybe()) if (Maybe())
list = null; list = null;
foreach (var x in list) // BAD (maybe) (false negative) foreach (var x in list) // BAD (maybe)
{ {
x.ToString(); // GOOD x.ToString(); // GOOD
list.ToString(); // GOOD list.ToString(); // GOOD
@@ -168,7 +168,7 @@ public class C
s = null; s = null;
do do
{ {
s.ToString(); // BAD (always) (reported as maybe) s.ToString(); // BAD (always)
} }
while (s != null); while (s != null);

View File

@@ -20,7 +20,7 @@ public class D
public void Callee1(object param) public void Callee1(object param)
{ {
param.ToString(); // BAD (maybe) (false negative) param.ToString(); // BAD (maybe)
} }
public void Callee2(object param) public void Callee2(object param)
@@ -29,7 +29,7 @@ public class D
{ {
param.ToString(); // GOOD param.ToString(); // GOOD
} }
param.ToString(); // BAD (maybe) (false negative) param.ToString(); // BAD (maybe)
} }
private static bool CustomIsNull(object x) private static bool CustomIsNull(object x)
@@ -99,7 +99,7 @@ public class D
} }
if (i > 4) if (i > 4)
foreach (var _ in xs) ; // BAD (maybe) (false negative) foreach (var _ in xs) ; // BAD (maybe)
if (i > 5) if (i > 5)
lock (xs) // BAD (maybe) lock (xs) // BAD (maybe)
@@ -117,7 +117,7 @@ public class D
var x = b ? null : "abc"; var x = b ? null : "abc";
x = x == null ? "" : x; x = x == null ? "" : x;
if (x == null) if (x == null)
x.ToString(); // BAD (always) (false negative) x.ToString(); // BAD (always)
else else
x.ToString(); // GOOD x.ToString(); // GOOD
} }
@@ -131,8 +131,8 @@ public class D
{ {
for (int i = 0; i < alen; i++) for (int i = 0; i < alen; i++)
{ {
sum += a[i]; // GOOD sum += a[i]; // GOOD (false positive)
sum += b[i]; // GOOD sum += b[i]; // GOOD (false positive)
} }
} }
int alen2; int alen2;
@@ -142,13 +142,13 @@ public class D
alen2 = 0; alen2 = 0;
for (int i = 1; i <= alen2; ++i) for (int i = 1; i <= alen2; ++i)
{ {
sum += a[i - 1]; // GOOD sum += a[i - 1]; // GOOD (false positive)
} }
} }
public void MissedGuard(object obj) public void MissedGuard(object obj)
{ {
obj.ToString(); // BAD (maybe) (false negative) obj.ToString(); // BAD (maybe)
var x = obj != null ? 1 : 0; var x = obj != null ? 1 : 0;
} }
@@ -194,7 +194,7 @@ public class D
{ {
var o = new Object(); var o = new Object();
if (o == null) if (o == null)
o.ToString(); // BAD (always) (false negative) o.ToString(); // BAD (always)
o.ToString(); // GOOD o.ToString(); // GOOD
try try
@@ -204,7 +204,7 @@ public class D
catch (Exception e) catch (Exception e)
{ {
if (e == null) if (e == null)
e.ToString(); // BAD (always) (false negative) e.ToString(); // BAD (always)
e.ToString(); // GOOD e.ToString(); // GOOD
} }
@@ -214,12 +214,12 @@ public class D
var o3 = "abc"; var o3 = "abc";
if (o3 == null) if (o3 == null)
o3.ToString(); // BAD (always) (false negative) o3.ToString(); // BAD (always)
o3.ToString(); // GOOD o3.ToString(); // GOOD
var o4 = "" + null; var o4 = "" + null;
if (o4 == null) if (o4 == null)
o4.ToString(); // BAD (always) (false negative) o4.ToString(); // BAD (always)
o4.ToString(); // GOOD o4.ToString(); // GOOD
} }
@@ -343,7 +343,7 @@ public class D
msg += "foobar"; msg += "foobar";
throw new Exception(msg); throw new Exception(msg);
} }
obj.ToString(); // GOOD obj.ToString(); // GOOD (false positive)
} }
public void LoopCorr(int iters) public void LoopCorr(int iters)
@@ -392,12 +392,12 @@ public class D
int i; int i;
for (i = 0; i < alen; i++) for (i = 0; i < alen; i++)
{ {
sum += a[i]; // GOOD sum += a[i]; // GOOD (false positive)
} }
int blen = b == null ? 0 : b.Length; // GOOD int blen = b == null ? 0 : b.Length; // GOOD
for (i = 0; i < blen; i++) for (i = 0; i < blen; i++)
{ {
sum += b[i]; // GOOD sum += b[i]; // GOOD (false positive)
} }
i = -3; i = -3;
} }
@@ -407,8 +407,8 @@ public class D
if ((x != null && y == null) || (x == null && y != null)) if ((x != null && y == null) || (x == null && y != null))
return; return;
if (x != null) if (x != null)
y.ToString(); // GOOD y.ToString(); // GOOD (false positive)
if (y != null) if (y != null)
x.ToString(); // GOOD x.ToString(); // GOOD (false positive)
} }
} }

View File

@@ -70,7 +70,7 @@ public class E
arrLen = arr == null ? 0 : arr.Length; arrLen = arr == null ? 0 : arr.Length;
if (arrLen > 0) if (arrLen > 0)
arr[0] = 0; // GOOD arr[0] = 0; // GOOD (false positive)
} }
public const int MY_CONST_A = 1; public const int MY_CONST_A = 1;
@@ -90,12 +90,12 @@ public class E
switch (switchguard) switch (switchguard)
{ {
case MY_CONST_A: case MY_CONST_A:
vals[0] = 0; // GOOD vals[0] = 0; // GOOD (false positive)
break; break;
case MY_CONST_C: case MY_CONST_C:
break; break;
case MY_CONST_B: case MY_CONST_B:
vals[0] = 0; // GOOD vals[0] = 0; // GOOD (false positive)
break; break;
default: default:
throw new Exception(); throw new Exception();
@@ -156,7 +156,7 @@ public class E
cond = true; cond = true;
} }
if (cond) if (cond)
obj2.ToString(); // GOOD obj2.ToString(); // GOOD (false positive)
} }
public void Ex10(int[] a) public void Ex10(int[] a)
@@ -164,7 +164,7 @@ public class E
int n = a == null ? 0 : a.Length; int n = a == null ? 0 : a.Length;
for (var i = 0; i < n; i++) for (var i = 0; i < n; i++)
{ {
int x = a[i]; // GOOD int x = a[i]; // GOOD (false positive)
if (x > 7) if (x > 7)
a = new int[n]; a = new int[n];
} }
@@ -175,7 +175,7 @@ public class E
bool b2 = obj == null ? false : b1; bool b2 = obj == null ? false : b1;
if (b2 == null) if (b2 == null)
{ {
obj.ToString(); // GOOD obj.ToString(); // GOOD (false positive)
} }
if (obj == null) if (obj == null)
{ {
@@ -183,13 +183,13 @@ public class E
} }
if (b1 == null) if (b1 == null)
{ {
obj.ToString(); // GOOD obj.ToString(); // GOOD (false positive)
} }
} }
public void Ex12(object o) public void Ex12(object o)
{ {
var i = o.GetHashCode(); // BAD (maybe) (false negative) var i = o.GetHashCode(); // BAD (maybe)
var s = o?.ToString(); var s = o?.ToString();
} }
@@ -198,16 +198,16 @@ public class E
var o = b ? null : ""; var o = b ? null : "";
o.M1(); // GOOD o.M1(); // GOOD
if (b) if (b)
o.M2(); // BAD (maybe) (false negative) o.M2(); // BAD (maybe)
else else
o.Select(x => x); // BAD (maybe) (false negative) o.Select(x => x); // BAD (maybe)
} }
public int Ex14(string s) public int Ex14(string s)
{ {
if (s is string) if (s is string)
return s.Length; return s.Length;
return s.GetHashCode(); // BAD (always) (false negative) return s.GetHashCode(); // BAD (always)
} }
public void Ex15(bool b) public void Ex15(bool b)
@@ -217,7 +217,7 @@ public class E
x = null; x = null;
x.ToString(); // BAD (maybe) x.ToString(); // BAD (maybe)
if (b) if (b)
x.ToString(); // BAD (always) (false negative) x.ToString(); // BAD (always)
} }
public void Ex16(bool b) public void Ex16(bool b)
@@ -227,17 +227,17 @@ public class E
x = null; x = null;
if (b) if (b)
x.ToString(); // BAD (always) x.ToString(); // BAD (always)
x.ToString(); // BAD (maybe) (false negative) x.ToString(); // BAD (maybe)
} }
public int Ex17(int? i) public int Ex17(int? i)
{ {
return i.Value; // BAD (maybe) (false negative) return i.Value; // BAD (maybe)
} }
public int Ex18(int? i) public int Ex18(int? i)
{ {
return (int)i; // BAD (maybe) (false negative) return (int)i; // BAD (maybe)
} }
public int Ex19(int? i) public int Ex19(int? i)

View File

@@ -33,7 +33,7 @@ class ForwardingTests
if (IsNotNullWrong(s)) if (IsNotNullWrong(s))
{ {
Console.WriteLine(s.Length); // BAD (always) (reported as maybe) Console.WriteLine(s.Length); // BAD (always)
} }
AssertIsNotNull(s); AssertIsNotNull(s);

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1,38 @@
| A.cs:8:15:8:32 | access to local variable synchronizedAlways | Variable $@ is always null here. | A.cs:7:16:7:33 | synchronizedAlways | synchronizedAlways | | A.cs:8:15:8:32 | access to local variable synchronizedAlways | Variable '$@' is always null here. | A.cs:7:16:7:33 | synchronizedAlways | synchronizedAlways |
| A.cs:17:9:17:17 | access to local variable arrayNull | Variable $@ is always null here. | A.cs:16:15:16:23 | arrayNull | arrayNull | | A.cs:17:9:17:17 | access to local variable arrayNull | Variable '$@' is always null here. | A.cs:16:15:16:23 | arrayNull | arrayNull |
| A.cs:31:27:31:37 | access to local variable arrayAccess | Variable $@ is always null here. | A.cs:26:15:26:25 | arrayAccess | arrayAccess | | A.cs:31:27:31:37 | access to local variable arrayAccess | Variable '$@' is always null here. | A.cs:26:15:26:25 | arrayAccess | arrayAccess |
| A.cs:32:27:32:37 | access to local variable fieldAccess | Variable $@ is always null here. | A.cs:27:18:27:28 | fieldAccess | fieldAccess | | A.cs:32:27:32:37 | access to local variable fieldAccess | Variable '$@' is always null here. | A.cs:27:18:27:28 | fieldAccess | fieldAccess |
| A.cs:33:28:33:39 | access to local variable methodAccess | Variable $@ is always null here. | A.cs:28:16:28:27 | methodAccess | methodAccess | | A.cs:33:28:33:39 | access to local variable methodAccess | Variable '$@' is always null here. | A.cs:28:16:28:27 | methodAccess | methodAccess |
| A.cs:34:27:34:36 | access to local variable methodCall | Variable $@ is always null here. | A.cs:29:16:29:25 | methodCall | methodCall | | A.cs:34:27:34:36 | access to local variable methodCall | Variable '$@' is always null here. | A.cs:29:16:29:25 | methodCall | methodCall |
| Assert.cs:15:27:15:27 | access to local variable s | Variable $@ is always null here. | Assert.cs:9:16:9:16 | s | s | | A.cs:50:9:50:14 | access to local variable varRef | Variable '$@' is always null here. | A.cs:48:16:48:21 | varRef | varRef |
| Assert.cs:23:27:23:27 | access to local variable s | Variable $@ is always null here. | Assert.cs:9:16:9:16 | s | s | | Assert.cs:15:27:15:27 | access to local variable s | Variable '$@' is always null here. | Assert.cs:9:16:9:16 | s | s |
| Assert.cs:31:27:31:27 | access to local variable s | Variable $@ is always null here. | Assert.cs:9:16:9:16 | s | s | | Assert.cs:23:27:23:27 | access to local variable s | Variable '$@' is always null here. | Assert.cs:9:16:9:16 | s | s |
| Assert.cs:47:27:47:27 | access to local variable s | Variable $@ is always null here. | Assert.cs:9:16:9:16 | s | s | | Assert.cs:31:27:31:27 | access to local variable s | Variable '$@' is always null here. | Assert.cs:9:16:9:16 | s | s |
| Assert.cs:51:27:51:27 | access to local variable s | Variable $@ is always null here. | Assert.cs:9:16:9:16 | s | s | | Assert.cs:47:27:47:27 | access to local variable s | Variable '$@' is always null here. | Assert.cs:9:16:9:16 | s | s |
| B.cs:13:13:13:24 | access to local variable eqCallAlways | Variable $@ is always null here. | B.cs:7:11:7:22 | eqCallAlways | eqCallAlways | | Assert.cs:51:27:51:27 | access to local variable s | Variable '$@' is always null here. | Assert.cs:9:16:9:16 | s | s |
| B.cs:24:13:24:25 | access to local variable neqCallAlways | Variable $@ is always null here. | B.cs:10:11:10:23 | neqCallAlways | neqCallAlways | | B.cs:13:13:13:24 | access to local variable eqCallAlways | Variable '$@' is always null here. | B.cs:7:11:7:22 | eqCallAlways | eqCallAlways |
| C.cs:18:13:18:13 | access to local variable o | Variable $@ is always null here. | C.cs:10:16:10:16 | o | o | | B.cs:24:13:24:25 | access to local variable neqCallAlways | Variable '$@' is always null here. | B.cs:10:11:10:23 | neqCallAlways | neqCallAlways |
| C.cs:42:9:42:9 | access to local variable s | Variable $@ is always null here. | C.cs:40:13:40:13 | s | s | | C.cs:18:13:18:13 | access to local variable o | Variable '$@' is always null here. | C.cs:10:16:10:16 | o | o |
| C.cs:57:9:57:10 | access to local variable o2 | Variable $@ is always null here. | C.cs:55:13:55:14 | o2 | o2 | | C.cs:42:9:42:9 | access to local variable s | Variable '$@' is always null here. | C.cs:40:13:40:13 | s | s |
| C.cs:163:13:163:13 | access to local variable s | Variable $@ is always null here. | C.cs:152:13:152:13 | s | s | | C.cs:57:9:57:10 | access to local variable o2 | Variable '$@' is always null here. | C.cs:55:13:55:14 | o2 | o2 |
| C.cs:197:13:197:13 | access to local variable s | Variable $@ is always null here. | C.cs:186:13:186:13 | s | s | | C.cs:163:13:163:13 | access to local variable s | Variable '$@' is always null here. | C.cs:152:13:152:13 | s | s |
| C.cs:219:13:219:13 | access to local variable s | Variable $@ is always null here. | C.cs:211:13:211:13 | s | s | | C.cs:171:13:171:13 | access to local variable s | Variable '$@' is always null here. | C.cs:152:13:152:13 | s | s |
| C.cs:234:9:234:9 | access to local variable s | Variable $@ is always null here. | C.cs:229:16:229:16 | s | s | | C.cs:197:13:197:13 | access to local variable s | Variable '$@' is always null here. | C.cs:186:13:186:13 | s | s |
| C.cs:238:13:238:13 | access to local variable s | Variable $@ is always null here. | C.cs:229:16:229:16 | s | s | | C.cs:219:13:219:13 | access to local variable s | Variable '$@' is always null here. | C.cs:211:13:211:13 | s | s |
| C.cs:250:9:250:9 | access to local variable a | Variable $@ is always null here. | C.cs:249:15:249:15 | a | a | | C.cs:234:9:234:9 | access to local variable s | Variable '$@' is always null here. | C.cs:229:16:229:16 | s | s |
| C.cs:261:9:261:10 | access to local variable ia | Variable $@ is always null here. | C.cs:258:15:258:16 | ia | ia | | C.cs:238:13:238:13 | access to local variable s | Variable '$@' is always null here. | C.cs:229:16:229:16 | s | s |
| C.cs:262:20:262:21 | access to local variable sa | Variable $@ is always null here. | C.cs:259:18:259:19 | sa | sa | | C.cs:250:9:250:9 | access to local variable a | Variable '$@' is always null here. | C.cs:249:15:249:15 | a | a |
| D.cs:385:13:385:15 | access to local variable ioe | Variable $@ is always null here. | D.cs:378:19:378:21 | ioe | ioe | | C.cs:261:9:261:10 | access to local variable ia | Variable '$@' is always null here. | C.cs:258:15:258:16 | ia | ia |
| E.cs:229:13:229:13 | access to local variable x | Variable $@ is always null here. | E.cs:225:13:225:13 | x | x | | C.cs:262:20:262:21 | access to local variable sa | Variable '$@' is always null here. | C.cs:259:18:259:19 | sa | sa |
| D.cs:120:13:120:13 | access to local variable x | Variable '$@' is always null here. | D.cs:117:13:117:13 | x | x |
| D.cs:197:13:197:13 | access to local variable o | Variable '$@' is always null here. | D.cs:195:13:195:13 | o | o |
| D.cs:207:17:207:17 | access to local variable e | Variable '$@' is always null here. | D.cs:204:26:204:26 | e | e |
| D.cs:217:13:217:14 | access to local variable o3 | Variable '$@' is always null here. | D.cs:215:13:215:14 | o3 | o3 |
| D.cs:222:13:222:14 | access to local variable o4 | Variable '$@' is always null here. | D.cs:220:13:220:14 | o4 | o4 |
| D.cs:385:13:385:15 | access to local variable ioe | Variable '$@' is always null here. | D.cs:378:19:378:21 | ioe | ioe |
| E.cs:210:16:210:16 | access to parameter s | Variable '$@' is always null here. | E.cs:206:28:206:28 | s | s |
| E.cs:220:13:220:13 | access to local variable x | Variable '$@' is always null here. | E.cs:215:13:215:13 | x | x |
| E.cs:229:13:229:13 | access to local variable x | Variable '$@' is always null here. | E.cs:225:13:225:13 | x | x |
| Forwarding.cs:36:31:36:31 | access to local variable s | Variable '$@' is always null here. | Forwarding.cs:7:16:7:16 | s | s |
| Forwarding.cs:40:27:40:27 | access to local variable s | Variable '$@' is always null here. | Forwarding.cs:7:16:7:16 | s | s |
| NullAlwaysBad.cs:9:30:9:30 | access to parameter s | Variable '$@' is always null here. | NullAlwaysBad.cs:7:29:7:29 | s | s |

View File

@@ -0,0 +1,13 @@
using System;
namespace NullAlways
{
class Bad
{
void DoPrint(string s)
{
if (s != null || s.Length > 0)
Console.WriteLine(s);
}
}
}

View File

@@ -0,0 +1,13 @@
using System;
namespace NullAlways
{
class Good
{
void DoPrint(string s)
{
if (s != null && s.Length > 0)
Console.WriteLine(s);
}
}
}

View File

@@ -82,6 +82,7 @@
| D.cs:39:16:39:24 | ... == ... | D.cs:39:16:39:16 | access to parameter x | true | true | | D.cs:39:16:39:24 | ... == ... | D.cs:39:16:39:16 | access to parameter x | true | true |
| D.cs:45:13:45:22 | ... != ... | D.cs:45:13:45:14 | access to local variable o1 | false | true | | D.cs:45:13:45:22 | ... != ... | D.cs:45:13:45:14 | access to local variable o1 | false | true |
| D.cs:45:13:45:22 | ... != ... | D.cs:45:13:45:14 | access to local variable o1 | true | false | | D.cs:45:13:45:22 | ... != ... | D.cs:45:13:45:14 | access to local variable o1 | true | false |
| D.cs:48:13:48:24 | ... is ... | D.cs:48:13:48:14 | access to local variable o2 | false | true |
| D.cs:48:13:48:24 | ... is ... | D.cs:48:13:48:14 | access to local variable o2 | true | false | | D.cs:48:13:48:24 | ... is ... | D.cs:48:13:48:14 | access to local variable o2 | true | false |
| D.cs:51:13:51:44 | ... != ... | D.cs:51:14:51:35 | ... = ... | false | true | | D.cs:51:13:51:44 | ... != ... | D.cs:51:14:51:35 | ... = ... | false | true |
| D.cs:51:13:51:44 | ... != ... | D.cs:51:14:51:35 | ... = ... | true | false | | D.cs:51:13:51:44 | ... != ... | D.cs:51:14:51:35 | ... = ... | true | false |
@@ -184,7 +185,10 @@
| E.cs:184:13:184:22 | ... == ... | E.cs:184:13:184:14 | (...) ... | true | true | | E.cs:184:13:184:22 | ... == ... | E.cs:184:13:184:14 | (...) ... | true | true |
| E.cs:193:17:193:17 | access to parameter o | E.cs:193:17:193:17 | access to parameter o | non-null | false | | E.cs:193:17:193:17 | access to parameter o | E.cs:193:17:193:17 | access to parameter o | non-null | false |
| E.cs:193:17:193:17 | access to parameter o | E.cs:193:17:193:17 | access to parameter o | null | true | | E.cs:193:17:193:17 | access to parameter o | E.cs:193:17:193:17 | access to parameter o | null | true |
| E.cs:208:13:208:23 | ... is ... | E.cs:208:13:208:13 | access to parameter s | false | true |
| E.cs:208:13:208:23 | ... is ... | E.cs:208:13:208:13 | access to parameter s | true | false | | E.cs:208:13:208:23 | ... is ... | E.cs:208:13:208:13 | access to parameter s | true | false |
| E.cs:245:13:245:22 | access to property HasValue | E.cs:245:13:245:13 | access to parameter i | false | true |
| E.cs:245:13:245:22 | access to property HasValue | E.cs:245:13:245:13 | access to parameter i | true | false |
| E.cs:252:13:252:21 | ... != ... | E.cs:252:13:252:13 | access to parameter i | false | true | | E.cs:252:13:252:21 | ... != ... | E.cs:252:13:252:13 | access to parameter i | false | true |
| E.cs:252:13:252:21 | ... != ... | E.cs:252:13:252:13 | access to parameter i | true | false | | E.cs:252:13:252:21 | ... != ... | E.cs:252:13:252:13 | access to parameter i | true | false |
| E.cs:259:13:259:21 | ... == ... | E.cs:259:13:259:13 | access to parameter i | false | false | | E.cs:259:13:259:21 | ... == ... | E.cs:259:13:259:13 | access to parameter i | false | false |
@@ -231,5 +235,11 @@
| GuardedString.cs:34:13:34:13 | access to local variable s | GuardedString.cs:34:13:34:13 | access to local variable s | non-null | false | | GuardedString.cs:34:13:34:13 | access to local variable s | GuardedString.cs:34:13:34:13 | access to local variable s | non-null | false |
| GuardedString.cs:34:13:34:13 | access to local variable s | GuardedString.cs:34:13:34:13 | access to local variable s | null | true | | GuardedString.cs:34:13:34:13 | access to local variable s | GuardedString.cs:34:13:34:13 | access to local variable s | null | true |
| GuardedString.cs:34:13:34:26 | ... != ... | GuardedString.cs:34:15:34:21 | access to property Length | false | false | | GuardedString.cs:34:13:34:26 | ... != ... | GuardedString.cs:34:15:34:21 | access to property Length | false | false |
| NullAlwaysBad.cs:9:17:9:25 | ... != ... | NullAlwaysBad.cs:9:17:9:17 | access to parameter s | false | true |
| NullAlwaysBad.cs:9:17:9:25 | ... != ... | NullAlwaysBad.cs:9:17:9:17 | access to parameter s | true | false |
| NullAlwaysGood.cs:9:17:9:25 | ... != ... | NullAlwaysGood.cs:9:17:9:17 | access to parameter s | false | true |
| NullAlwaysGood.cs:9:17:9:25 | ... != ... | NullAlwaysGood.cs:9:17:9:17 | access to parameter s | true | false |
| NullMaybeGood.cs:9:17:9:25 | ... != ... | NullMaybeGood.cs:9:17:9:17 | access to parameter o | false | true |
| NullMaybeGood.cs:9:17:9:25 | ... != ... | NullMaybeGood.cs:9:17:9:17 | access to parameter o | true | false |
| StringConcatenation.cs:15:16:15:22 | ... != ... | StringConcatenation.cs:15:16:15:16 | access to local variable s | false | false | | StringConcatenation.cs:15:16:15:22 | ... != ... | StringConcatenation.cs:15:16:15:16 | access to local variable s | false | false |
| StringConcatenation.cs:22:16:22:22 | ... != ... | StringConcatenation.cs:22:16:22:16 | access to local variable s | false | false | | StringConcatenation.cs:22:16:22:22 | ... != ... | StringConcatenation.cs:22:16:22:16 | access to local variable s | false | false |

View File

@@ -1,45 +1,82 @@
| C.cs:64:9:64:10 | access to local variable o1 | Variable $@ may be null here. | C.cs:62:13:62:14 | o1 | o1 | | C.cs:64:9:64:10 | access to local variable o1 | Variable '$@' may be null here because of $@ assignment. | C.cs:62:13:62:14 | o1 | o1 | C.cs:62:13:62:46 | Object o1 = ... | this |
| C.cs:68:9:68:10 | access to local variable o2 | Variable $@ may be null here. | C.cs:66:13:66:14 | o2 | o2 | | C.cs:68:9:68:10 | access to local variable o2 | Variable '$@' may be null here because of $@ assignment. | C.cs:66:13:66:14 | o2 | o2 | C.cs:66:13:66:46 | Object o2 = ... | this |
| C.cs:96:15:96:15 | access to local variable o | Variable $@ may be null here. | C.cs:95:13:95:13 | o | o | | C.cs:96:15:96:15 | access to local variable o | Variable '$@' may be null here because of $@ assignment. | C.cs:95:13:95:13 | o | o | C.cs:95:13:95:45 | Object o = ... | this |
| C.cs:171:13:171:13 | access to local variable s | Variable $@ may be null here. | C.cs:152:13:152:13 | s | s | | C.cs:104:27:104:30 | access to parameter list | Variable '$@' may be null here because of $@ assignment. | C.cs:100:42:100:45 | list | list | C.cs:103:13:103:23 | ... = ... | this |
| C.cs:178:13:178:13 | access to local variable s | Variable $@ may be null here. | C.cs:152:13:152:13 | s | s | | C.cs:178:13:178:13 | access to local variable s | Variable '$@' may be null here because of $@ assignment. | C.cs:152:13:152:13 | s | s | C.cs:179:13:179:20 | ... = ... | this |
| C.cs:204:13:204:13 | access to local variable s | Variable $@ may be null here. | C.cs:186:13:186:13 | s | s | | C.cs:204:13:204:13 | access to local variable s | Variable '$@' may be null here because of $@ assignment. | C.cs:186:13:186:13 | s | s | C.cs:205:13:205:20 | ... = ... | this |
| C.cs:224:9:224:9 | access to local variable s | Variable $@ may be null here. | C.cs:211:13:211:13 | s | s | | C.cs:224:9:224:9 | access to local variable s | Variable '$@' may be null here because of $@ assignment. | C.cs:211:13:211:13 | s | s | C.cs:223:13:223:20 | ... = ... | this |
| C.cs:243:13:243:13 | access to local variable s | Variable $@ may be null here. | C.cs:229:16:229:16 | s | s | | C.cs:243:13:243:13 | access to local variable s | Variable '$@' may be null here because of $@ assignment. | C.cs:229:16:229:16 | s | s | C.cs:241:24:241:31 | ... = ... | this |
| D.cs:60:13:60:14 | access to local variable o4 | Variable $@ may be null here. | D.cs:54:13:54:14 | o4 | o4 | | D.cs:23:9:23:13 | access to parameter param | Variable '$@' may be null here because of $@ null argument. | D.cs:21:32:21:36 | param | param | D.cs:17:17:17:20 | null | this |
| D.cs:62:13:62:14 | access to local variable o5 | Variable $@ may be null here. | D.cs:58:13:58:14 | o5 | o5 | | D.cs:32:9:32:13 | access to parameter param | Variable '$@' may be null here as suggested by $@ null check. | D.cs:26:32:26:36 | param | param | D.cs:28:13:28:25 | ... != ... | this |
| D.cs:73:13:73:14 | access to local variable o7 | Variable $@ may be null here. | D.cs:68:13:68:14 | o7 | o7 | | D.cs:60:13:60:14 | access to local variable o4 | Variable '$@' may be null here because of $@ assignment. | D.cs:54:13:54:14 | o4 | o4 | D.cs:54:13:54:34 | String o4 = ... | this |
| D.cs:78:13:78:14 | access to local variable o8 | Variable $@ may be null here. | D.cs:75:13:75:14 | o8 | o8 | | D.cs:62:13:62:14 | access to local variable o5 | Variable '$@' may be null here because of $@ assignment. | D.cs:58:13:58:14 | o5 | o5 | D.cs:58:13:58:41 | String o5 = ... | this |
| D.cs:80:13:80:14 | access to local variable o8 | Variable $@ may be null here. | D.cs:75:13:75:14 | o8 | o8 | | D.cs:73:13:73:14 | access to local variable o7 | Variable '$@' may be null here because of $@ assignment. | D.cs:68:13:68:14 | o7 | o7 | D.cs:68:13:68:34 | String o7 = ... | this |
| D.cs:82:13:82:14 | access to local variable o8 | Variable $@ may be null here. | D.cs:75:13:75:14 | o8 | o8 | | D.cs:78:13:78:14 | access to local variable o8 | Variable '$@' may be null here because of $@ assignment. | D.cs:75:13:75:14 | o8 | o8 | D.cs:75:13:75:34 | String o8 = ... | this |
| D.cs:84:13:84:14 | access to local variable o8 | Variable $@ may be null here. | D.cs:75:13:75:14 | o8 | o8 | | D.cs:80:13:80:14 | access to local variable o8 | Variable '$@' may be null here because of $@ assignment. | D.cs:75:13:75:14 | o8 | o8 | D.cs:75:13:75:34 | String o8 = ... | this |
| D.cs:91:13:91:14 | access to local variable xs | Variable $@ may be null here. | D.cs:89:15:89:16 | xs | xs | | D.cs:82:13:82:14 | access to local variable o8 | Variable '$@' may be null here because of $@ assignment. | D.cs:75:13:75:14 | o8 | o8 | D.cs:75:13:75:34 | String o8 = ... | this |
| D.cs:94:21:94:22 | access to local variable xs | Variable $@ may be null here. | D.cs:89:15:89:16 | xs | xs | | D.cs:84:13:84:14 | access to local variable o8 | Variable '$@' may be null here because of $@ assignment. | D.cs:75:13:75:14 | o8 | o8 | D.cs:75:13:75:34 | String o8 = ... | this |
| D.cs:98:21:98:22 | access to local variable xs | Variable $@ may be null here. | D.cs:89:15:89:16 | xs | xs | | D.cs:91:13:91:14 | access to local variable xs | Variable '$@' may be null here because of $@ assignment. | D.cs:89:15:89:16 | xs | xs | D.cs:89:15:89:44 | Int32[] xs = ... | this |
| D.cs:105:19:105:20 | access to local variable xs | Variable $@ may be null here. | D.cs:89:15:89:16 | xs | xs | | D.cs:94:21:94:22 | access to local variable xs | Variable '$@' may be null here because of $@ assignment. | D.cs:89:15:89:16 | xs | xs | D.cs:89:15:89:44 | Int32[] xs = ... | this |
| D.cs:171:9:171:11 | access to local variable obj | Variable $@ may be null here. | D.cs:163:16:163:18 | obj | obj | | D.cs:98:21:98:22 | access to local variable xs | Variable '$@' may be null here because of $@ assignment. | D.cs:89:15:89:16 | xs | xs | D.cs:89:15:89:44 | Int32[] xs = ... | this |
| D.cs:245:13:245:13 | access to local variable o | Variable $@ may be null here. | D.cs:228:16:228:16 | o | o | | D.cs:102:31:102:32 | access to local variable xs | Variable '$@' may be null here because of $@ assignment. | D.cs:89:15:89:16 | xs | xs | D.cs:89:15:89:44 | Int32[] xs = ... | this |
| D.cs:247:13:247:13 | access to local variable o | Variable $@ may be null here. | D.cs:228:16:228:16 | o | o | | D.cs:105:19:105:20 | access to local variable xs | Variable '$@' may be null here because of $@ assignment. | D.cs:89:15:89:16 | xs | xs | D.cs:89:15:89:44 | Int32[] xs = ... | this |
| D.cs:253:13:253:14 | access to local variable o2 | Variable $@ may be null here. | D.cs:249:13:249:14 | o2 | o2 | | D.cs:134:24:134:24 | access to parameter a | Variable '$@' may be null here as suggested by $@ null check. | D.cs:125:35:125:35 | a | a | D.cs:127:20:127:28 | ... == ... | this |
| D.cs:267:13:267:13 | access to local variable o | Variable $@ may be null here. | D.cs:258:16:258:16 | o | o | | D.cs:134:24:134:24 | access to parameter a | Variable '$@' may be null here as suggested by $@ null check. | D.cs:125:35:125:35 | a | a | D.cs:139:13:139:21 | ... != ... | this |
| D.cs:291:13:291:13 | access to local variable o | Variable $@ may be null here. | D.cs:258:16:258:16 | o | o | | D.cs:135:24:135:24 | access to parameter b | Variable '$@' may be null here as suggested by $@ null check. | D.cs:125:44:125:44 | b | b | D.cs:128:20:128:28 | ... == ... | this |
| D.cs:294:13:294:13 | access to local variable o | Variable $@ may be null here. | D.cs:258:16:258:16 | o | o | | D.cs:145:20:145:20 | access to parameter a | Variable '$@' may be null here as suggested by $@ null check. | D.cs:125:35:125:35 | a | a | D.cs:127:20:127:28 | ... == ... | this |
| D.cs:300:17:300:20 | access to local variable prev | Variable $@ may be null here. | D.cs:296:16:296:19 | prev | prev | | D.cs:145:20:145:20 | access to parameter a | Variable '$@' may be null here as suggested by $@ null check. | D.cs:125:35:125:35 | a | a | D.cs:139:13:139:21 | ... != ... | this |
| D.cs:313:17:313:17 | access to local variable s | Variable $@ may be null here. | D.cs:304:16:304:16 | s | s | | D.cs:151:9:151:11 | access to parameter obj | Variable '$@' may be null here as suggested by $@ null check. | D.cs:149:36:149:38 | obj | obj | D.cs:152:17:152:27 | ... != ... | this |
| D.cs:324:9:324:9 | access to local variable r | Variable $@ may be null here. | D.cs:316:16:316:16 | r | r | | D.cs:171:9:171:11 | access to local variable obj | Variable '$@' may be null here because of $@ assignment. | D.cs:163:16:163:18 | obj | obj | D.cs:163:16:163:25 | Object obj = ... | this |
| D.cs:356:13:356:13 | access to local variable a | Variable $@ may be null here. | D.cs:351:15:351:15 | a | a | | D.cs:245:13:245:13 | access to local variable o | Variable '$@' may be null here because of $@ assignment. | D.cs:228:16:228:16 | o | o | D.cs:240:9:240:16 | ... = ... | this |
| D.cs:363:13:363:16 | access to local variable last | Variable $@ may be null here. | D.cs:360:20:360:23 | last | last | | D.cs:247:13:247:13 | access to local variable o | Variable '$@' may be null here because of $@ assignment. | D.cs:228:16:228:16 | o | o | D.cs:240:9:240:16 | ... = ... | this |
| D.cs:372:13:372:13 | access to local variable b | Variable $@ may be null here. | D.cs:366:15:366:15 | b | b | | D.cs:253:13:253:14 | access to local variable o2 | Variable '$@' may be null here because of $@ assignment. | D.cs:249:13:249:14 | o2 | o2 | D.cs:249:13:249:38 | String o2 = ... | this |
| E.cs:12:38:12:39 | access to local variable a2 | Variable $@ may be null here. | E.cs:9:18:9:19 | a2 | a2 | | D.cs:267:13:267:13 | access to local variable o | Variable '$@' may be null here because of $@ assignment. | D.cs:258:16:258:16 | o | o | D.cs:258:16:258:23 | Object o = ... | this |
| E.cs:14:13:14:14 | access to local variable a3 | Variable $@ may be null here. | E.cs:11:16:11:17 | a3 | a3 | | D.cs:291:13:291:13 | access to local variable o | Variable '$@' may be null here because of $@ assignment. | D.cs:258:16:258:16 | o | o | D.cs:269:9:269:16 | ... = ... | this |
| E.cs:27:13:27:14 | access to local variable s1 | Variable $@ may be null here. | E.cs:19:13:19:14 | s1 | s1 | | D.cs:291:13:291:13 | access to local variable o | Variable '$@' may be null here because of $@ assignment. | D.cs:258:16:258:16 | o | o | D.cs:283:17:283:24 | ... = ... | this |
| E.cs:35:9:35:12 | access to local variable last | Variable $@ may be null here. | E.cs:32:16:32:19 | last | last | | D.cs:294:13:294:13 | access to local variable o | Variable '$@' may be null here because of $@ assignment. | D.cs:258:16:258:16 | o | o | D.cs:269:9:269:16 | ... = ... | this |
| E.cs:43:13:43:16 | access to local variable last | Variable $@ may be null here. | E.cs:32:16:32:19 | last | last | | D.cs:294:13:294:13 | access to local variable o | Variable '$@' may be null here because of $@ assignment. | D.cs:258:16:258:16 | o | o | D.cs:283:17:283:24 | ... = ... | this |
| E.cs:61:13:61:17 | access to local variable slice | Variable $@ may be null here. | E.cs:51:22:51:26 | slice | slice | | D.cs:300:17:300:20 | access to local variable prev | Variable '$@' may be null here because of $@ assignment. | D.cs:296:16:296:19 | prev | prev | D.cs:296:16:296:26 | Object prev = ... | this |
| E.cs:112:13:112:16 | access to local variable arr2 | Variable $@ may be null here. | E.cs:107:15:107:18 | arr2 | arr2 | | D.cs:313:17:313:17 | access to local variable s | Variable '$@' may be null here because of $@ assignment. | D.cs:304:16:304:16 | s | s | D.cs:304:16:304:23 | String s = ... | this |
| E.cs:125:33:125:35 | access to local variable obj | Variable $@ may be null here. | E.cs:119:13:119:15 | obj | obj | | D.cs:324:9:324:9 | access to local variable r | Variable '$@' may be null here because of $@ assignment. | D.cs:316:16:316:16 | r | r | D.cs:316:16:316:23 | Object r = ... | this |
| E.cs:218:9:218:9 | access to local variable x | Variable $@ may be null here. | E.cs:215:13:215:13 | x | x | | D.cs:346:9:346:11 | access to parameter obj | Variable '$@' may be null here as suggested by $@ null check. | D.cs:333:26:333:28 | obj | obj | D.cs:336:13:336:23 | ... == ... | this |
| Forwarding.cs:36:31:36:31 | access to local variable s | Variable $@ may be null here. | Forwarding.cs:7:16:7:16 | s | s | | D.cs:356:13:356:13 | access to local variable a | Variable '$@' may be null here because of $@ assignment. | D.cs:351:15:351:15 | a | a | D.cs:351:15:351:22 | Int32[] a = ... | this |
| Forwarding.cs:40:27:40:27 | access to local variable s | Variable $@ may be null here. | Forwarding.cs:7:16:7:16 | s | s | | D.cs:363:13:363:16 | access to local variable last | Variable '$@' may be null here because of $@ assignment. | D.cs:360:20:360:23 | last | last | D.cs:360:20:360:30 | String last = ... | this |
| GuardedString.cs:35:31:35:31 | access to local variable s | Variable $@ may be null here. | GuardedString.cs:7:16:7:16 | s | s | | D.cs:372:13:372:13 | access to local variable b | Variable '$@' may be null here because of $@ assignment. | D.cs:366:15:366:15 | b | b | D.cs:366:15:366:47 | Int32[] b = ... | this |
| StringConcatenation.cs:16:17:16:17 | access to local variable s | Variable $@ may be null here. | StringConcatenation.cs:14:16:14:16 | s | s | | D.cs:395:20:395:20 | access to parameter a | Variable '$@' may be null here as suggested by $@ null check. | D.cs:388:36:388:36 | a | a | D.cs:390:20:390:28 | ... == ... | this |
| D.cs:400:20:400:20 | access to parameter b | Variable '$@' may be null here as suggested by $@ null check. | D.cs:388:45:388:45 | b | b | D.cs:397:20:397:28 | ... == ... | this |
| D.cs:410:13:410:13 | access to parameter y | Variable '$@' may be null here as suggested by $@ null check. | D.cs:405:45:405:45 | y | y | D.cs:407:27:407:35 | ... == ... | this |
| D.cs:410:13:410:13 | access to parameter y | Variable '$@' may be null here as suggested by $@ null check. | D.cs:405:45:405:45 | y | y | D.cs:407:55:407:63 | ... != ... | this |
| D.cs:410:13:410:13 | access to parameter y | Variable '$@' may be null here as suggested by $@ null check. | D.cs:405:45:405:45 | y | y | D.cs:411:13:411:21 | ... != ... | this |
| D.cs:412:13:412:13 | access to parameter x | Variable '$@' may be null here as suggested by $@ null check. | D.cs:405:35:405:35 | x | x | D.cs:407:14:407:22 | ... != ... | this |
| D.cs:412:13:412:13 | access to parameter x | Variable '$@' may be null here as suggested by $@ null check. | D.cs:405:35:405:35 | x | x | D.cs:407:42:407:50 | ... == ... | this |
| D.cs:412:13:412:13 | access to parameter x | Variable '$@' may be null here as suggested by $@ null check. | D.cs:405:35:405:35 | x | x | D.cs:409:13:409:21 | ... != ... | this |
| E.cs:12:38:12:39 | access to local variable a2 | Variable '$@' may be null here because of $@ assignment. | E.cs:9:18:9:19 | a2 | a2 | E.cs:9:18:9:26 | Int64[][] a2 = ... | this |
| E.cs:14:13:14:14 | access to local variable a3 | Variable '$@' may be null here because of $@ assignment. | E.cs:11:16:11:17 | a3 | a3 | E.cs:11:16:11:24 | Int64[] a3 = ... | this |
| E.cs:27:13:27:14 | access to local variable s1 | Variable '$@' may be null here because of $@ assignment. | E.cs:19:13:19:14 | s1 | s1 | E.cs:19:13:19:30 | String s1 = ... | this |
| E.cs:27:13:27:14 | access to local variable s1 | Variable '$@' may be null here because of $@ assignment. | E.cs:19:13:19:14 | s1 | s1 | E.cs:23:13:23:30 | ... = ... | this |
| E.cs:35:9:35:12 | access to local variable last | Variable '$@' may be null here because of $@ assignment. | E.cs:32:16:32:19 | last | last | E.cs:32:16:32:26 | String last = ... | this |
| E.cs:43:13:43:16 | access to local variable last | Variable '$@' may be null here because of $@ assignment. | E.cs:32:16:32:19 | last | last | E.cs:37:9:37:19 | ... = ... | this |
| E.cs:61:13:61:17 | access to local variable slice | Variable '$@' may be null here because of $@ assignment. | E.cs:51:22:51:26 | slice | slice | E.cs:51:22:51:33 | List<String> slice = ... | this |
| E.cs:73:13:73:15 | access to parameter arr | Variable '$@' may be null here as suggested by $@ null check. | E.cs:66:40:66:42 | arr | arr | E.cs:70:22:70:32 | ... == ... | this |
| E.cs:93:17:93:20 | access to parameter vals | Variable '$@' may be null here as suggested by $@ null check. | E.cs:80:27:80:30 | vals | vals | E.cs:83:13:83:24 | ... != ... | this |
| E.cs:93:17:93:20 | access to parameter vals | Variable '$@' may be null here as suggested by $@ null check. | E.cs:80:27:80:30 | vals | vals | E.cs:85:18:85:29 | ... != ... | this |
| E.cs:98:17:98:20 | access to parameter vals | Variable '$@' may be null here as suggested by $@ null check. | E.cs:80:27:80:30 | vals | vals | E.cs:83:13:83:24 | ... != ... | this |
| E.cs:98:17:98:20 | access to parameter vals | Variable '$@' may be null here as suggested by $@ null check. | E.cs:80:27:80:30 | vals | vals | E.cs:85:18:85:29 | ... != ... | this |
| E.cs:112:13:112:16 | access to local variable arr2 | Variable '$@' may be null here because of $@ assignment. | E.cs:107:15:107:18 | arr2 | arr2 | E.cs:107:15:107:25 | Int32[] arr2 = ... | this |
| E.cs:125:33:125:35 | access to local variable obj | Variable '$@' may be null here because of $@ assignment. | E.cs:119:13:119:15 | obj | obj | E.cs:137:25:137:34 | ... = ... | this |
| E.cs:159:13:159:16 | access to local variable obj2 | Variable '$@' may be null here as suggested by $@ null check. | E.cs:152:16:152:19 | obj2 | obj2 | E.cs:153:13:153:24 | ... != ... | this |
| E.cs:167:21:167:21 | access to parameter a | Variable '$@' may be null here as suggested by $@ null check. | E.cs:162:28:162:28 | a | a | E.cs:164:17:164:25 | ... == ... | this |
| E.cs:178:13:178:15 | access to parameter obj | Variable '$@' may be null here as suggested by $@ null check. | E.cs:173:29:173:31 | obj | obj | E.cs:175:19:175:29 | ... == ... | this |
| E.cs:178:13:178:15 | access to parameter obj | Variable '$@' may be null here as suggested by $@ null check. | E.cs:173:29:173:31 | obj | obj | E.cs:180:13:180:23 | ... == ... | this |
| E.cs:186:13:186:15 | access to parameter obj | Variable '$@' may be null here as suggested by $@ null check. | E.cs:173:29:173:31 | obj | obj | E.cs:175:19:175:29 | ... == ... | this |
| E.cs:186:13:186:15 | access to parameter obj | Variable '$@' may be null here as suggested by $@ null check. | E.cs:173:29:173:31 | obj | obj | E.cs:180:13:180:23 | ... == ... | this |
| E.cs:192:17:192:17 | access to parameter o | Variable '$@' may be null here as suggested by $@ null check. | E.cs:190:29:190:29 | o | o | E.cs:193:17:193:17 | access to parameter o | this |
| E.cs:201:11:201:11 | access to local variable o | Variable '$@' may be null here because of $@ assignment. | E.cs:198:13:198:13 | o | o | E.cs:198:13:198:29 | String o = ... | this |
| E.cs:203:11:203:11 | access to local variable o | Variable '$@' may be null here because of $@ assignment. | E.cs:198:13:198:13 | o | o | E.cs:198:13:198:29 | String o = ... | this |
| E.cs:218:9:218:9 | access to local variable x | Variable '$@' may be null here because of $@ assignment. | E.cs:215:13:215:13 | x | x | E.cs:217:13:217:20 | ... = ... | this |
| E.cs:230:9:230:9 | access to local variable x | Variable '$@' may be null here because of $@ assignment. | E.cs:225:13:225:13 | x | x | E.cs:227:13:227:20 | ... = ... | this |
| E.cs:235:16:235:16 | access to parameter i | Variable '$@' may be null here because it has a nullable type. | E.cs:233:26:233:26 | i | i | E.cs:233:26:233:26 | i | this |
| E.cs:240:21:240:21 | access to parameter i | Variable '$@' may be null here because it has a nullable type. | E.cs:238:26:238:26 | i | i | E.cs:238:26:238:26 | i | this |
| 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:9:31:9:31 | access to parameter o | Variable '$@' may be null here because of $@ null argument. | NullMaybeBad.cs:7:29:7:29 | o | o | NullMaybeBad.cs:15:21:15:24 | null | this |
| 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 |

View File

@@ -0,0 +1,18 @@
using System;
namespace NullMaybe
{
class Bad
{
void DoPrint(object o)
{
Console.WriteLine(o.ToString());
}
void M()
{
DoPrint("Hello");
DoPrint(null);
}
}
}

View File

@@ -0,0 +1,19 @@
using System;
namespace NullMaybe
{
class Good
{
void DoPrint(object o)
{
if (o != null)
Console.WriteLine(o.ToString());
}
void M()
{
DoPrint("Hello");
DoPrint(null);
}
}
}