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"
"qhelp.dtd">
<qhelp>
<overview>
<p>If a variable is dereferenced, and the variable has a null value on all possible execution paths
leading to the dereferencing, it is guaranteed to result in a <code>NullReferenceException</code>.
<p>If a variable is dereferenced, and the variable has a <code>null</code>
value on all possible execution paths leading to the dereferencing, the dereferencing is
guaranteed to result in a <code>NullReferenceException</code>.
</p>
</overview>
<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>
<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>

View File

@@ -1,9 +1,9 @@
/**
* @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
* @problem.severity error
* @precision medium
* @precision very-high
* @id cs/dereferenced-value-is-always-null
* @tags reliability
* correctness
@@ -14,6 +14,6 @@
import csharp
import semmle.code.csharp.dataflow.Nullness
from VariableAccess access, LocalVariable var
where access = unguardedNullDereference(var)
select access, "Variable $@ is always null here.", var, var.getName()
from Dereference d, Ssa::SourceVariable v
where d.isFirstAlwaysNull(v)
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"
"qhelp.dtd">
<qhelp>
<overview>
<p>If a variable is dereferenced, and the variable may have a null value on some execution paths
leading to the dereferencing, the dereferencing may result in a <code>NullReferenceException</code>.
<p>If a variable is dereferenced, and the variable may have a <code>null</code>
value on some execution paths leading to the dereferencing, the dereferencing
may result in a <code>NullReferenceException</code>.
</p>
</overview>
<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>
<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>

View File

@@ -1,9 +1,10 @@
/**
* @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
* @problem.severity warning
* @precision medium
* @precision high
* @id cs/dereferenced-value-may-be-null
* @tags reliability
* correctness
@@ -14,8 +15,6 @@
import csharp
import semmle.code.csharp.dataflow.Nullness
from VariableAccess access, LocalVariable var
where access = unguardedMaybeNullDereference(var)
// do not flag definite nulls here; these are already flagged by NullAlways.ql
and not access = unguardedNullDereference(var)
select access, "Variable $@ may be null here.", var, var.getName()
from Dereference d, Ssa::SourceVariable v, string msg, Element reason
where d.isFirstMaybeNull(v.getAnSsaDefinition(), msg, reason)
select d, "Variable '$@' may be null here " + msg + ".", v, v.toString(), reason, "this"

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`.
*/
class DereferenceableExpr extends Expr {
private boolean isNullableType;
DereferenceableExpr() {
exists(Expr e, Type t |
// 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
this = getNullEquivParent*(e) and
t = e.getType() |
t instanceof NullableType
t instanceof NullableType and
isNullableType = true
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`.
*
@@ -177,9 +186,22 @@ class DereferenceableExpr extends Expr {
if ie.(IsConstantExpr).getConstant() instanceof NullLiteral then
// E.g. `x is null`
isNull = branch
else
else (
// 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
isCustomNullCheck(result, this, v, isNull)
@@ -307,19 +329,23 @@ class AccessOrCallExpr extends Expr {
}
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()
}
private Ssa::Definition getAnSsaQualifier(Expr e) {
e = getATrackedRead(result)
e = getATrackedAccess(result)
or
not e = getATrackedRead(_) and
not e = getATrackedAccess(_) and
result = getAnSsaQualifier(e.(QualifiableExpr).getQualifier())
}
private AssignableRead getATrackedRead(Ssa::Definition def) {
result = def.getARead() and
private AssignableAccess getATrackedAccess(Ssa::Definition def) {
(
result = def.getARead()
or
result = def.(Ssa::ExplicitDefinition).getADefinition().getTargetAccess()
) and
not def instanceof Ssa::ImplicitUntrackedDefinition
}
@@ -384,6 +410,15 @@ class GuardedExpr extends AccessOrCallExpr {
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
* 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. */
class NullGuardedExpr extends GuardedExpr {
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. */
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`
* and `e = x`.
*/
@@ -433,6 +485,8 @@ module Internal {
qe.getQualifier() = e 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
or
result.(Call).getTarget().getReturnType() instanceof ValueType
@@ -444,11 +498,28 @@ module Internal {
result = bao and
bao.getAnOperand() = e and
bao.getAnOperand() = o and
// The other operand must be provably non-null in order
// for `only if` to hold
nonNullValue(o) and
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. */
class Guard extends Expr {
private AbstractValue val;
@@ -785,6 +856,23 @@ module Internal {
v1 instanceof NullValue and
v2 = v1
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 |
def.getDefinition().getSource() = g2 |
g1 = def.getARead() and

View File

@@ -2,7 +2,7 @@
* Provides predicates for performing nullness analyses.
*
* 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:
*
* ```
@@ -18,493 +18,394 @@
*/
import csharp
private import ControlFlow
private import semmle.code.csharp.commons.Assertions
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.frameworks.System
private import semmle.code.csharp.frameworks.Test
/** An expression that may be `null`. */
private class NullExpr extends Expr {
NullExpr() {
this instanceof NullLiteral or
this.(ParenthesizedExpr).getExpr() instanceof NullExpr or
this.(ConditionalExpr).getThen() instanceof NullExpr or
this.(ConditionalExpr).getElse() instanceof NullExpr
class MaybeNullExpr extends Expr {
MaybeNullExpr() {
this instanceof NullLiteral
or
this.(ConditionalExpr).getThen() instanceof MaybeNullExpr
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`. */
private class NonNullExpr extends Expr {
/** An expression that is always `null`. */
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() {
not (this instanceof NullLiteral or this instanceof ConditionalExpr or this instanceof ParenthesizedExpr) or
this.(ParenthesizedExpr).getExpr() instanceof NonNullExpr or
this.(ConditionalExpr).getThen() instanceof NonNullExpr or
this.(ConditionalExpr).getElse() instanceof NonNullExpr
G::Internal::nonNullValue(this)
or
exists(NonNullExpr mid |
G::Internal::nonNullValueImplied(mid, this)
)
or
this instanceof G::NullGuardedExpr
or
exists(Ssa::Definition def | nonNullDef(def) | this = def.getARead())
}
}
/** 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()
/** Holds if SSA definition `def` is never `null`. */
private predicate nonNullDef(Ssa::Definition v) {
v.(Ssa::ExplicitDefinition).getADefinition().getSource() instanceof NonNullExpr
or
exists(LogicalOrExpr orexpr | result = orParent(orexpr) and e = orexpr.getAnOperand())
}
/**
* Gets a logical 'and' expression in which the expression `e` is a
* (possibly nested) operand.
*/
private LogicalAndExpr andParent(Expr e) {
e = result.getAnOperand()
exists(AssignableDefinition ad |
ad = v.(Ssa::ExplicitDefinition).getADefinition() |
ad instanceof AssignableDefinitions::IsPatternDefinition
or
exists(LogicalAndExpr andexpr | result = andParent(andexpr) and e = andexpr.getAnOperand())
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
* - `expr` is an assignment and the `access` is its left-hand side, or
* - `expr` is an assignment and the `access` has the same value as its right-hand
* side.
* If the returned element takes the `s` branch, then `def` is guaranteed to be
* `null` if `nv.isNull()` holds, and non-`null` otherwise.
*/
private predicate sameValue(Expr expr, LocalScopeVariableAccess access) {
access = expr.stripCasts() or
access = expr.(AssignExpr).getLValue() or
sameValue(expr.(AssignExpr).getRValue(), access) or
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)
private ControlFlowElement getANullCheck(Ssa::Definition def, SuccessorTypes::ConditionalSuccessor s, NullValue nv) {
exists(Expr e, G::AbstractValue v |
v.branchImplies(result, s, e) |
exprImpliesSsaDef(e, v, def, nv)
)
}
/** Gets a node where the variable `var` may be non-`null`. */
ControlFlow::Node maybeNonNullNode(LocalScopeVariable var) {
result = nonNullDef(var).getAControlFlowNode().getASuccessor()
/** Holds if `def` is an SSA definition that may be `null`. */
private predicate defMaybeNull(Ssa::Definition def, string msg, Element reason) {
// 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
exists(ControlFlow::Node mid |
mid = maybeNonNullNode(var) and
not mid.getElement() = nullDef(var) and
mid.getASuccessor() = result and
not result = nullBranchKill(var, mid)
// A parameter might be `null` if there is a `null` argument somewhere
exists(AssignableDefinitions::ImplicitParameterDefinition pdef, Parameter p, MaybeNullExpr arg |
pdef = def.(Ssa::ExplicitDefinition).getADefinition() |
p = pdef.getParameter().getSourceDeclaration() and
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
* a non-`null` check for the variable `var`.
* Holds if `def1` being `null` in basic block `bb1` implies that `def2` might
* be null in basic block `bb2`. The SSA definitions share the same source variable.
*/
private Expr nullGuarded(LocalScopeVariable var) {
exists(LogicalOrExpr guard |
guard.getLeftOperand() = failureIsNonNullTest(var) and
result = guard.getRightOperand())
private predicate defNullImpliesStep(Ssa::Definition def1, BasicBlock bb1, Ssa::Definition def2, BasicBlock bb2) {
exists(Ssa::SourceVariable v |
defMaybeNull(v.getAnSsaDefinition(), _, _) and
def1.getSourceVariable() = v
|
def2.(Ssa::PseudoDefinition).getAnInput() = def1 and
def2.definesAt(bb2, _)
or
exists(LogicalAndExpr guard |
guard.getLeftOperand() = nonNullTest(var) and
result = guard.getRightOperand())
or
exists(ConditionalExpr cond |
cond.getCondition() = nullTest(var) and
result = cond.getElse())
or
exists(ConditionalExpr cond |
cond.getCondition() = nonNullTest(var) and
result = cond.getThen())
or
result = any(NullGuardedExpr nge | nge = var.getAnAccess())
or
result.getParent() = nullGuarded(var)
def2 = def1 and
not exists(Ssa::PseudoDefinition def |
def.getSourceVariable() = v and
def.definesAt(bb2, _)
)
) and
def1.isLiveAtEndOfBlock(bb1) and
not ensureNotNullAt(bb1, _, def1) and
bb2 = bb1.getASuccessor() and
not exists(SuccessorTypes::ConditionalSuccessor s, NullValue nv |
bb1.getLastNode() = getANullCheck(def1, s, nv).getAControlFlowNode() |
bb2 = bb1.getASuccessorByType(s) and
not nv.isNull()
)
}
/**
* Gets a variable access that must be non-`null` to avoid a
* `NullReferenceException`.
* The transitive closure of `defNullImpliesStep()` originating from `defMaybeNull()`.
* That is, those basic blocks for which the SSA definition is suspected of being `null`.
*/
private predicate dereferenced(LocalScopeVariableAccess access) {
exists(nonNullAccess(access))
private predicate defMaybeNullInBlock(Ssa::Definition def, Ssa::SourceVariable v, BasicBlock bb) {
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
*
* - does not occur within a `null`-guarded expression, but
* - occurs within an expression where the variable may be `null`.
* Holds if `v` is a source variable that might reach a potential `null`
* dereference.
*/
LocalScopeVariableAccess unguardedMaybeNullDereference(LocalScopeVariable var) {
var.getAnAccess() = result and
maybeNullNode(var).getElement() = result and
dereferenced(result) and
not result = nullGuarded(var)
private predicate nullDerefCandidateVariable(Ssa::SourceVariable v) {
exists(Ssa::Definition def, BasicBlock bb |
potentialNullDereferenceAt(bb, _, def, _) |
defMaybeNullInBlock(def, v, bb)
)
}
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
*
* - does not occur within a `null`-guarded expression, but
* - occurs within an expression where the variable may be `null`, and
* - does not occur within an expression where the variable may be non-`null`.
* Holds if SSA definition `def` can reach a read `ar`, without passing
* through an intermediate dereference that always (`always = true`) or
* maybe (`always = false`) throws a null reference exception.
*/
LocalScopeVariableAccess unguardedNullDereference(LocalScopeVariable var) {
unguardedMaybeNullDereference(var) = result and
not maybeNonNullNode(var).getElement() = result
private predicate defReaches(Ssa::Definition def, AssignableRead ar, boolean always) {
ar = def.getAFirstRead() and
(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
* source variable.
*/
deprecated
Definition getAnDefinition() {
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. */
@@ -1113,7 +1122,8 @@ module Ssa {
call = succ |
callable = call.getTarget() or
callable = call.getTarget().(Method).getAnOverrider+() or
callable = call.getTarget().(Method).getAnUltimateImplementor()
callable = call.getTarget().(Method).getAnUltimateImplementor() or
callable = getARuntimeDelegateTarget(call)
)
or
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: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: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: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>: |

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 | 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 | ... != ... | 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 | 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 | 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 | 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 | 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 | 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 | 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 | ... == ... | 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 | 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 | 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 | ... != ... | 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 | 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 | 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 | ... != ... | 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 | 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 | 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 | ... == ... | 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 | 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 | 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 |
@@ -44,6 +60,8 @@
| 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 | 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 | 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 |
@@ -52,6 +70,8 @@
| 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 | 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 | 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 |
@@ -60,6 +80,8 @@
| 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 | 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 | 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 |
@@ -85,12 +107,16 @@
| 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 | ... == ... | 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 | ... == ... | 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: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 | ... == ... | 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 | !... | 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 |
@@ -99,6 +125,8 @@
| 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 | ... == ... | 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 | !... | true | Guards.cs:41:14:41:39 | !... | false |
| 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: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: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 | ... != ... | 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 |
@@ -121,6 +151,10 @@
| 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 | ... != ... | 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 | 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 |
@@ -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 | 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: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: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 |
@@ -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: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: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 | 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 ... | 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: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 |
@@ -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: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: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 | ... != ... | 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 | null | Splitting.cs:132:21:132:29 | call to method M11 | null |

View File

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

View File

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

View File

@@ -20,7 +20,7 @@ public class D
public void Callee1(object param)
{
param.ToString(); // BAD (maybe) (false negative)
param.ToString(); // BAD (maybe)
}
public void Callee2(object param)
@@ -29,7 +29,7 @@ public class D
{
param.ToString(); // GOOD
}
param.ToString(); // BAD (maybe) (false negative)
param.ToString(); // BAD (maybe)
}
private static bool CustomIsNull(object x)
@@ -99,7 +99,7 @@ public class D
}
if (i > 4)
foreach (var _ in xs) ; // BAD (maybe) (false negative)
foreach (var _ in xs) ; // BAD (maybe)
if (i > 5)
lock (xs) // BAD (maybe)
@@ -117,7 +117,7 @@ public class D
var x = b ? null : "abc";
x = x == null ? "" : x;
if (x == null)
x.ToString(); // BAD (always) (false negative)
x.ToString(); // BAD (always)
else
x.ToString(); // GOOD
}
@@ -131,8 +131,8 @@ public class D
{
for (int i = 0; i < alen; i++)
{
sum += a[i]; // GOOD
sum += b[i]; // GOOD
sum += a[i]; // GOOD (false positive)
sum += b[i]; // GOOD (false positive)
}
}
int alen2;
@@ -142,13 +142,13 @@ public class D
alen2 = 0;
for (int i = 1; i <= alen2; ++i)
{
sum += a[i - 1]; // GOOD
sum += a[i - 1]; // GOOD (false positive)
}
}
public void MissedGuard(object obj)
{
obj.ToString(); // BAD (maybe) (false negative)
obj.ToString(); // BAD (maybe)
var x = obj != null ? 1 : 0;
}
@@ -194,7 +194,7 @@ public class D
{
var o = new Object();
if (o == null)
o.ToString(); // BAD (always) (false negative)
o.ToString(); // BAD (always)
o.ToString(); // GOOD
try
@@ -204,7 +204,7 @@ public class D
catch (Exception e)
{
if (e == null)
e.ToString(); // BAD (always) (false negative)
e.ToString(); // BAD (always)
e.ToString(); // GOOD
}
@@ -214,12 +214,12 @@ public class D
var o3 = "abc";
if (o3 == null)
o3.ToString(); // BAD (always) (false negative)
o3.ToString(); // BAD (always)
o3.ToString(); // GOOD
var o4 = "" + null;
if (o4 == null)
o4.ToString(); // BAD (always) (false negative)
o4.ToString(); // BAD (always)
o4.ToString(); // GOOD
}
@@ -343,7 +343,7 @@ public class D
msg += "foobar";
throw new Exception(msg);
}
obj.ToString(); // GOOD
obj.ToString(); // GOOD (false positive)
}
public void LoopCorr(int iters)
@@ -392,12 +392,12 @@ public class D
int 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
for (i = 0; i < blen; i++)
{
sum += b[i]; // GOOD
sum += b[i]; // GOOD (false positive)
}
i = -3;
}
@@ -407,8 +407,8 @@ public class D
if ((x != null && y == null) || (x == null && y != null))
return;
if (x != null)
y.ToString(); // GOOD
y.ToString(); // GOOD (false positive)
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;
if (arrLen > 0)
arr[0] = 0; // GOOD
arr[0] = 0; // GOOD (false positive)
}
public const int MY_CONST_A = 1;
@@ -90,12 +90,12 @@ public class E
switch (switchguard)
{
case MY_CONST_A:
vals[0] = 0; // GOOD
vals[0] = 0; // GOOD (false positive)
break;
case MY_CONST_C:
break;
case MY_CONST_B:
vals[0] = 0; // GOOD
vals[0] = 0; // GOOD (false positive)
break;
default:
throw new Exception();
@@ -156,7 +156,7 @@ public class E
cond = true;
}
if (cond)
obj2.ToString(); // GOOD
obj2.ToString(); // GOOD (false positive)
}
public void Ex10(int[] a)
@@ -164,7 +164,7 @@ public class E
int n = a == null ? 0 : a.Length;
for (var i = 0; i < n; i++)
{
int x = a[i]; // GOOD
int x = a[i]; // GOOD (false positive)
if (x > 7)
a = new int[n];
}
@@ -175,7 +175,7 @@ public class E
bool b2 = obj == null ? false : b1;
if (b2 == null)
{
obj.ToString(); // GOOD
obj.ToString(); // GOOD (false positive)
}
if (obj == null)
{
@@ -183,13 +183,13 @@ public class E
}
if (b1 == null)
{
obj.ToString(); // GOOD
obj.ToString(); // GOOD (false positive)
}
}
public void Ex12(object o)
{
var i = o.GetHashCode(); // BAD (maybe) (false negative)
var i = o.GetHashCode(); // BAD (maybe)
var s = o?.ToString();
}
@@ -198,16 +198,16 @@ public class E
var o = b ? null : "";
o.M1(); // GOOD
if (b)
o.M2(); // BAD (maybe) (false negative)
o.M2(); // BAD (maybe)
else
o.Select(x => x); // BAD (maybe) (false negative)
o.Select(x => x); // BAD (maybe)
}
public int Ex14(string s)
{
if (s is string)
return s.Length;
return s.GetHashCode(); // BAD (always) (false negative)
return s.GetHashCode(); // BAD (always)
}
public void Ex15(bool b)
@@ -217,7 +217,7 @@ public class E
x = null;
x.ToString(); // BAD (maybe)
if (b)
x.ToString(); // BAD (always) (false negative)
x.ToString(); // BAD (always)
}
public void Ex16(bool b)
@@ -227,17 +227,17 @@ public class E
x = null;
if (b)
x.ToString(); // BAD (always)
x.ToString(); // BAD (maybe) (false negative)
x.ToString(); // BAD (maybe)
}
public int Ex17(int? i)
{
return i.Value; // BAD (maybe) (false negative)
return i.Value; // BAD (maybe)
}
public int Ex18(int? i)
{
return (int)i; // BAD (maybe) (false negative)
return (int)i; // BAD (maybe)
}
public int Ex19(int? i)

View File

@@ -33,7 +33,7 @@ class ForwardingTests
if (IsNotNullWrong(s))
{
Console.WriteLine(s.Length); // BAD (always) (reported as maybe)
Console.WriteLine(s.Length); // BAD (always)
}
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: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: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: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 |
| 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:31:27:31: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:51:27:51: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 |
| 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:18:13:18:13 | access to local variable o | Variable $@ is always null here. | C.cs:10:16:10:16 | o | o |
| 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:57:9:57:10 | access to local variable o2 | Variable $@ is always null here. | C.cs:55:13:55:14 | o2 | o2 |
| 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:197:13:197:13 | access to local variable s | Variable $@ is always null here. | C.cs:186:13:186: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:234:9:234:9 | access to local variable s | Variable $@ is always null here. | C.cs:229:16:229:16 | 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:250:9:250:9 | access to local variable a | Variable $@ is always null here. | C.cs:249:15:249:15 | a | a |
| 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:262:20:262:21 | access to local variable sa | Variable $@ is always null here. | C.cs:259:18:259:19 | sa | sa |
| 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:229:13:229:13 | access to local variable x | Variable $@ is always null here. | E.cs:225:13:225:13 | x | x |
| 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: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: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:50:9:50:14 | access to local variable varRef | Variable '$@' is always null here. | A.cs:48:16:48:21 | varRef | varRef |
| 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:23:27:23: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:47:27:47: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 |
| B.cs:13:13:13:24 | access to local variable eqCallAlways | Variable '$@' is always null here. | B.cs:7:11:7:22 | eqCallAlways | eqCallAlways |
| 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:18:13:18:13 | access to local variable o | Variable '$@' is always null here. | C.cs:10:16:10:16 | o | o |
| 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:57:9:57:10 | access to local variable o2 | Variable '$@' is always null here. | C.cs:55:13:55:14 | o2 | o2 |
| 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:171:13:171:13 | access to local variable s | Variable '$@' is always null here. | C.cs:152:13:152:13 | 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:219:13:219:13 | access to local variable s | Variable '$@' is always null here. | C.cs:211:13:211: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:238:13:238:13 | access to local variable s | Variable '$@' is always null here. | C.cs:229:16:229:16 | 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:261:9:261:10 | access to local variable ia | Variable '$@' is always null here. | C.cs:258:15:258:16 | ia | ia |
| 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: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: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: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 |
@@ -184,7 +185,10 @@
| 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 | 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: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 | true | 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 | null | true |
| 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: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:68:9:68:10 | access to local variable o2 | Variable $@ may be null here. | C.cs:66:13:66:14 | o2 | o2 |
| 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:171:13:171: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. | C.cs:152:13:152:13 | s | s |
| 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:224:9:224:9 | access to local variable s | Variable $@ may be null here. | C.cs:211:13:211:13 | s | s |
| C.cs:243:13:243:13 | access to local variable s | Variable $@ may be null here. | C.cs:229:16:229:16 | s | s |
| 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:62:13:62:14 | access to local variable o5 | Variable $@ may be null here. | D.cs:58:13:58:14 | o5 | o5 |
| 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:78:13:78: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. | D.cs:75:13:75:14 | o8 | o8 |
| 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:84:13:84:14 | access to local variable o8 | Variable $@ may be null here. | D.cs:75:13:75:14 | o8 | o8 |
| 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:94:21:94:22 | access to local variable xs | Variable $@ may be null here. | D.cs:89:15:89:16 | xs | xs |
| 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:105:19:105:20 | access to local variable xs | Variable $@ may be null here. | D.cs:89:15:89:16 | xs | xs |
| 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:245:13:245:13 | access to local variable o | Variable $@ may be null here. | D.cs:228:16:228:16 | o | o |
| 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:253:13:253:14 | access to local variable o2 | Variable $@ may be null here. | D.cs:249:13:249:14 | o2 | o2 |
| 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:291:13:291:13 | access to local variable o | Variable $@ may be null here. | D.cs:258:16:258:16 | o | o |
| 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:300:17:300:20 | access to local variable prev | Variable $@ may be null here. | D.cs:296:16:296:19 | prev | prev |
| 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:324:9:324:9 | access to local variable r | Variable $@ may be null here. | D.cs:316:16:316:16 | r | r |
| 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:363:13:363:16 | access to local variable last | Variable $@ may be null here. | D.cs:360:20:360:23 | last | last |
| D.cs:372:13:372:13 | access to local variable b | Variable $@ may be null here. | D.cs:366:15:366:15 | b | b |
| E.cs:12:38:12:39 | access to local variable a2 | Variable $@ may be null here. | E.cs:9:18:9:19 | a2 | a2 |
| E.cs:14:13:14:14 | access to local variable a3 | Variable $@ may be null here. | E.cs:11:16:11:17 | a3 | a3 |
| E.cs:27:13:27:14 | access to local variable s1 | Variable $@ may be null here. | E.cs:19:13:19:14 | s1 | s1 |
| E.cs:35:9:35:12 | access to local variable last | Variable $@ may be null here. | E.cs:32:16:32:19 | last | last |
| E.cs:43:13:43:16 | access to local variable last | Variable $@ may be null here. | E.cs:32:16:32:19 | last | last |
| E.cs:61:13:61:17 | access to local variable slice | Variable $@ may be null here. | E.cs:51:22:51:26 | slice | slice |
| E.cs:112:13:112:16 | access to local variable arr2 | Variable $@ may be null here. | E.cs:107:15:107:18 | arr2 | arr2 |
| E.cs:125:33:125:35 | access to local variable obj | Variable $@ may be null here. | E.cs:119:13:119:15 | obj | obj |
| E.cs:218:9:218:9 | access to local variable x | Variable $@ may be null here. | E.cs:215:13:215:13 | x | x |
| Forwarding.cs:36:31:36:31 | access to local variable s | Variable $@ may be null here. | Forwarding.cs:7:16:7:16 | s | s |
| Forwarding.cs:40:27:40:27 | access to local variable s | Variable $@ may be null here. | Forwarding.cs:7:16:7:16 | s | s |
| GuardedString.cs:35:31:35:31 | access to local variable s | Variable $@ may be null here. | GuardedString.cs:7:16:7:16 | s | s |
| StringConcatenation.cs:16:17:16:17 | access to local variable s | Variable $@ may be null here. | StringConcatenation.cs:14:16:14:16 | s | s |
| 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 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 because of $@ assignment. | C.cs:95:13:95:13 | o | o | C.cs:95:13:95:45 | Object o = ... | this |
| 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 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 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 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 because of $@ assignment. | C.cs:229:16:229:16 | s | s | C.cs:241:24:241:31 | ... = ... | this |
| 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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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);
}
}
}