diff --git a/csharp/ql/lib/semmle/code/csharp/Caching.qll b/csharp/ql/lib/semmle/code/csharp/Caching.qll index 4e34f5504dc..50a789f7989 100644 --- a/csharp/ql/lib/semmle/code/csharp/Caching.qll +++ b/csharp/ql/lib/semmle/code/csharp/Caching.qll @@ -10,7 +10,6 @@ module Stages { cached module ControlFlowStage { private import semmle.code.csharp.controlflow.internal.Splitting - private import semmle.code.csharp.controlflow.Guards as Guards cached predicate forceCachingInSameStage() { any() } @@ -21,8 +20,6 @@ module Stages { or exists(ControlFlow::Node n) or - Guards::Internal::isCustomNullCheck(_, _, _, _) - or forceCachingInSameStageRev() } } diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll index 5f4c5c75246..654d2700d54 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll @@ -293,9 +293,7 @@ private module LogicInput implements GuardsImpl::LogicInputSig { module Guards = GuardsImpl::Logic; /** An expression whose value may control the execution of another element. */ -class Guard extends Expr { - Guard() { isGuard(this, _) } - +class Guard extends Guards::Guard { /** * Holds if `cfn` is guarded by this expression having value `v`, where `sub` is * a sub expression of this expression that is structurally equal to the expression @@ -323,229 +321,39 @@ class Guard extends Expr { */ predicate controlsBasicBlock(BasicBlock bb, AbstractValue v) { guardControls(this, bb, v) } - /** - * Holds if this guard is an equality test between `e1` and `e2`. If the test is - * negated, that is `!=`, then `polarity` is false, otherwise `polarity` is - * true. - */ - predicate isEquality(Expr e1, Expr e2, boolean polarity) { - exists(BooleanValue v | - this = getAnEqualityCheck(e1, v, e2) and - polarity = v.getValue() - ) - } - /** * Gets a valid value for this guard. For example, if this guard is a test, then * it can have Boolean values `true` and `false`. */ - AbstractValue getAValue() { isGuard(this, result) } + deprecated AbstractValue getAValue() { isGuard(this, result) } } -/** An abstract value. */ -abstract class AbstractValue extends TAbstractValue { - /** Holds if the `s` branch out of `cfe` is taken iff `e` has this value. */ - abstract predicate branch(ControlFlowElement cfe, ConditionalSuccessor s, Expr e); - - /** Gets an abstract value that represents the dual of this value, if any. */ - abstract AbstractValue getDualValue(); - - /** - * Gets an expression that has this abstract value. Two expressions that have the - * same concrete value also have the same abstract value, but not necessarily the - * other way around. - * - * Moreover, `e = this.getAnExpr() implies not e = this.getDualValue().getAnExpr()`. - */ - abstract Expr getAnExpr(); - - /** - * Holds if this is a singleton abstract value. That is, two expressions that have - * this abstract value also have the same concrete value. - */ - abstract predicate isSingleton(); - - /** - * Holds if this value describes a referential property. For example, emptiness - * of a collection is a referential property. - * - * Such values only propagate through adjacent reads, for example, in - * - * ```csharp - * int M() - * { - * var x = new string[]{ "a", "b", "c" }.ToList(); - * x.Clear(); - * return x.Count; - * } - * ``` - * - * the non-emptiness of `new string[]{ "a", "b", "c" }.ToList()` only propagates - * to the read of `x` in `x.Clear()` and not in `x.Count`. - * - * Aliasing is not taken into account in the analyses. - */ - predicate isReferentialProperty() { none() } - - /** Gets a textual representation of this abstract value. */ - abstract string toString(); -} +class AbstractValue = GuardValue; /** Provides different types of `AbstractValues`s. */ module AbstractValues { - /** A Boolean value. */ - class BooleanValue extends AbstractValue, TBooleanValue { - /** Gets the underlying Boolean value. */ - boolean getValue() { this = TBooleanValue(result) } + class BooleanValue extends AbstractValue { + BooleanValue() { exists(this.asBooleanValue()) } - override predicate branch(ControlFlowElement cfe, ConditionalSuccessor s, Expr e) { - s.(BooleanSuccessor).getValue() = this.getValue() and - exists(BooleanCompletion c | s = c.getAMatchingSuccessorType() | - c.isValidFor(cfe) and - e = cfe - ) - } - - override BooleanValue getDualValue() { result.getValue() = this.getValue().booleanNot() } - - override Expr getAnExpr() { - result.getType() instanceof BoolType and - result.getValue() = this.getValue().toString() - } - - override predicate isSingleton() { any() } - - override string toString() { result = this.getValue().toString() } + boolean getValue() { this.asBooleanValue() = result } } - /** An integer value. */ - class IntegerValue extends AbstractValue, TIntegerValue { - /** Gets the underlying integer value. */ - int getValue() { this = TIntegerValue(result) } + class IntegerValue extends AbstractValue { + IntegerValue() { exists(this.asIntValue()) } - override predicate branch(ControlFlowElement cfe, ConditionalSuccessor s, Expr e) { none() } - - override IntegerValue getDualValue() { none() } - - override Expr getAnExpr() { - result.getValue().toInt() = this.getValue() and - ( - result.getType() instanceof Enum - or - result.getType() instanceof IntegralType - ) - } - - override predicate isSingleton() { any() } - - override string toString() { result = this.getValue().toString() } + int getValue() { this.asIntValue() = result } } - /** A value that is either `null` or non-`null`. */ - class NullValue extends AbstractValue, TNullValue { - /** Holds if this value represents `null`. */ - predicate isNull() { this = TNullValue(true) } + class NullValue extends AbstractValue { + NullValue() { this.isNullness(_) } - /** Holds if this value represents non-`null`. */ - predicate isNonNull() { this = TNullValue(false) } + predicate isNull() { this.isNullValue() } - override predicate branch(ControlFlowElement cfe, ConditionalSuccessor s, Expr e) { - this = TNullValue(s.(NullnessSuccessor).getValue()) and - exists(NullnessCompletion c | s = c.getAMatchingSuccessorType() | - c.isValidFor(cfe) and - e = cfe - ) - } + predicate isNonNull() { this.isNonNullValue() } - override NullValue getDualValue() { - if this.isNull() then result.isNonNull() else result.isNull() - } - - override DereferenceableExpr getAnExpr() { + DereferenceableExpr getAnExpr() { if this.isNull() then nullValueImplied(result) else nonNullValueImplied(result) } - - override predicate isSingleton() { this.isNull() } - - override string toString() { if this.isNull() then result = "null" else result = "non-null" } - } - - /** A value that represents match or non-match against a specific pattern. */ - class MatchValue extends AbstractValue, TMatchValue { - /** Gets the case. */ - Case getCase() { this = TMatchValue(result, _) } - - /** Holds if this value represents a match. */ - predicate isMatch() { this = TMatchValue(_, true) } - - override predicate branch(ControlFlowElement cfe, ConditionalSuccessor s, Expr e) { - this = TMatchValue(_, s.(MatchingSuccessor).getValue()) and - exists(MatchingCompletion c, Switch switch, Case case | s = c.getAMatchingSuccessorType() | - c.isValidFor(cfe) and - switchMatching(switch, case, cfe) and - e = switch.getExpr() and - case = this.getCase() - ) - } - - override MatchValue getDualValue() { - result = - any(MatchValue mv | - mv.getCase() = this.getCase() and - if this.isMatch() then not mv.isMatch() else mv.isMatch() - ) - } - - override Expr getAnExpr() { none() } - - override predicate isSingleton() { none() } - - override string toString() { - exists(string s | s = this.getCase().getPattern().toString() | - if this.isMatch() then result = "match " + s else result = "non-match " + s - ) - } - } - - /** A value that represents an empty or non-empty collection. */ - class EmptyCollectionValue extends AbstractValue, TEmptyCollectionValue { - /** Holds if this value represents an empty collection. */ - predicate isEmpty() { this = TEmptyCollectionValue(true) } - - /** Holds if this value represents a non-empty collection. */ - predicate isNonEmpty() { this = TEmptyCollectionValue(false) } - - override predicate branch(ControlFlowElement cfe, ConditionalSuccessor s, Expr e) { - this = TEmptyCollectionValue(s.(EmptinessSuccessor).getValue()) and - exists(EmptinessCompletion c, ForeachStmt fs | s = c.getAMatchingSuccessorType() | - c.isValidFor(cfe) and - foreachEmptiness(fs, cfe) and - e = fs.getIterableExpr() - ) and - // Only when taking the non-empty successor do we know that the original iterator - // expression was non-empty. When taking the empty successor, we may have already - // iterated through the `foreach` loop zero or more times, hence the iterator - // expression can be both empty and non-empty - this.isNonEmpty() - } - - override EmptyCollectionValue getDualValue() { - if this.isEmpty() then result.isNonEmpty() else result.isEmpty() - } - - override Expr getAnExpr() { - this.isEmpty() and - emptyValue(result) - or - this.isNonEmpty() and - nonEmptyValue(result) - } - - override predicate isSingleton() { none() } - - override predicate isReferentialProperty() { any() } - - override string toString() { if this.isEmpty() then result = "empty" else result = "non-empty" } } } @@ -608,133 +416,6 @@ class DereferenceableExpr extends Expr { /** Holds if this expression has a nullable type `T?`. */ predicate hasNullableType() { isNullableType = true } - /** - * Gets an expression that directly tests whether this expression is `null`. - * - * If the returned expression evaluates to `v`, then this expression is - * guaranteed to be `null` if `isNull` is true, and non-`null` if `isNull` is - * false. - * - * For example, if the expression `x != null` evaluates to `true` then the - * expression `x` is guaranteed to be non-`null`. - */ - private Expr getABooleanNullCheck(BooleanValue v, boolean isNull) { - exists(boolean branch | branch = v.getValue() | - // Comparison with `null`, for example `x != null` - exists(ComparisonTest ct, ComparisonKind ck, Expr e | - ct.getExpr() = result and - ct.getAnArgument() = this and - ct.getAnArgument() = e and - e = any(NullValue nv | nv.isNull()).getAnExpr() and - this != e and - ck = ct.getComparisonKind() - | - ck.isEquality() and isNull = branch - or - ck.isInequality() and isNull = branch.booleanNot() - ) - or - // Comparison with a non-`null` value, for example `x?.Length > 0` - exists(ComparisonTest ct, ComparisonKind ck, Expr e | ct.getExpr() = result | - ct.getAnArgument() = this and - ct.getAnArgument() = e and - e = any(NullValue nv | nv.isNonNull()).getAnExpr() and - ck = ct.getComparisonKind() and - this != e and - isNull = false and - if ck.isInequality() then branch = false else branch = true - ) - or - // Call to `string.IsNullOrEmpty()` or `string.IsNullOrWhiteSpace()` - exists(MethodCall mc, string name | result = mc | - mc.getTarget() = any(SystemStringClass c).getAMethod(name) and - name.regexpMatch("IsNullOr(Empty|WhiteSpace)") and - mc.getArgument(0) = this and - branch = false and - isNull = false - ) - or - result = - any(PatternMatch pm | - this = pm.getExpr() and - ( - // E.g. `x is null` - pm.getPattern() instanceof NullLiteral and - isNull = branch - or - // E.g. `x is string` or `x is ""` - branch.booleanNot() = patternMatchesNull(pm.getPattern()) and - isNull = false - or - exists(TypePatternExpr tpe | - // E.g. `x is string` where `x` has type `string` - typePattern(result, tpe, tpe.getCheckedType()) 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) - ) - } - - /** - * Gets an expression that tests via matching whether this expression is `null`. - * - * If the returned expression matches (`v.isMatch()`) or non-matches - * (`not v.isMatch()`), then this expression is guaranteed to be `null` - * if `isNull` is true, and non-`null` if `isNull` is false. - * - * For example, if the case statement `case string s` matches in - * - * ```csharp - * switch (o) - * { - * case string s: - * return s; - * default: - * return ""; - * } - * ``` - * - * then `o` is guaranteed to be non-`null`. - */ - private Expr getAMatchingNullCheck(MatchValue v, boolean isNull) { - exists(Switch s, Case case | - case = v.getCase() and - this = s.getExpr() and - result = this and - case = s.getACase() - | - // E.g. `case string` - case.getPattern() instanceof TypePatternExpr and - v.isMatch() and - isNull = false - or - case.getPattern() = - any(ConstantPatternExpr cpe | - if cpe instanceof NullLiteral - then - // `case null` - if v.isMatch() then isNull = true else isNull = false - else ( - // E.g. `case ""` - v.isMatch() and - isNull = false - ) - ) - ) - } - /** * Gets an expression that tests via nullness whether this expression is `null`. * @@ -759,24 +440,6 @@ class DereferenceableExpr extends Expr { ) } - /** - * Gets an expression that tests whether this expression is `null`. - * - * If the returned expression has abstract value `v`, then this expression is - * guaranteed to be `null` if `isNull` is true, and non-`null` if `isNull` is - * false. - * - * For example, if the expression `x != null` evaluates to `true` then the - * expression `x` is guaranteed to be non-`null`. - */ - Expr getANullCheck(AbstractValue v, boolean isNull) { - result = this.getABooleanNullCheck(v, isNull) - or - result = this.getAMatchingNullCheck(v, isNull) - or - result = this.getANullnessNullCheck(v, isNull) - } - /** Holds if `guard` suggests that this expression may be `null`. */ predicate guardSuggestsMaybeNull(Guards::Guard guard) { guard = this.getANullnessNullCheck(_, true) @@ -955,6 +618,14 @@ private AssignableAccess getATrackedAccess(Ssa::Definition def, ControlFlow::Nod cfn = def.getControlFlowNode() } +private predicate ssaMustHaveValue(Expr e, GuardValue v) { + exists(Ssa::Definition def, BasicBlock bb | + e = def.getARead() and + e.getBasicBlock() = bb and + Guards::ssaControls(def, bb, v) + ) +} + /** * A guarded expression. * @@ -993,11 +664,7 @@ private AssignableAccess getATrackedAccess(Ssa::Definition def, ControlFlow::Nod * definition). */ class GuardedExpr extends AccessOrCallExpr { - private Guard g; - private AccessOrCallExpr sub0; - private AbstractValue v0; - - GuardedExpr() { isGuardedByExpr(this, g, sub0, v0) } + GuardedExpr() { isGuardedByExpr(this, _, _, _) or ssaMustHaveValue(this, _) } /** * Gets an expression that guards this expression. That is, this expression is @@ -1010,18 +677,16 @@ class GuardedExpr extends AccessOrCallExpr { * left-most qualifier, then so must the other (accessing the same SSA * variable). */ - Guard getAGuard(Expr sub, AbstractValue v) { - result = g and - sub = sub0 and - v = v0 - } + Guard getAGuard(Expr sub, AbstractValue v) { isGuardedByExpr(this, result, sub, v) } /** * 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) { g = this.getAGuard(g, v) } + predicate mustHaveValue(AbstractValue v) { + exists(Guard g | g = this.getAGuard(g, v)) or ssaMustHaveValue(this, v) + } /** * Holds if this expression is guarded by expression `cond`, which must @@ -1153,16 +818,6 @@ class NullGuardedDataFlowNode extends GuardedDataFlowNode { /** INTERNAL: Do not use. */ module Internal { - newtype TAbstractValue = - TBooleanValue(boolean b) { b = true or b = false } or - TIntegerValue(int i) { i = any(Expr e).getValue().toInt() } or - TNullValue(boolean b) { b = true or b = false } or - TMatchValue(Case c, boolean b) { - exists(c.getPattern()) and - (b = true or b = false) - } or - TEmptyCollectionValue(boolean b) { b = true or b = false } - /** Holds if expression `e` is a `null` value. */ predicate nullValue(Expr e) { e instanceof NullLiteral @@ -1280,22 +935,6 @@ module Internal { e = any(BinaryArithmeticOperation bao | result = bao.getAnOperand()) } - private Expr stripConditionalExpr(Expr e) { - e = - any(ConditionalExpr ce | - result = stripConditionalExpr(ce.getThen()) - or - result = stripConditionalExpr(ce.getElse()) - ) - or - not e instanceof ConditionalExpr and - result = e - } - - private predicate canReturn(Callable c, Expr ret) { - exists(Expr e | c.canReturn(e) | ret = stripConditionalExpr(e)) - } - // The predicates in this module should be evaluated in the same stage as the CFG // construction stage. This is to avoid recomputation of pre-basic-blocks and // pre-SSA predicates @@ -1303,416 +942,6 @@ module Internal { private import semmle.code.csharp.controlflow.internal.PreBasicBlocks as PreBasicBlocks private import semmle.code.csharp.controlflow.internal.PreSsa - /** - * Holds if pre-basic-block `bb` only is reached when guard `g` has abstract value `v`, - * not taking implications into account. - */ - pragma[nomagic] - private predicate preControlsDirect(Guard g, PreBasicBlocks::PreBasicBlock bb, AbstractValue v) { - exists(PreBasicBlocks::ConditionBlock cb, ConditionalSuccessor s | cb.controls(bb, s) | - v.branch(cb.getLastNode(), s, g) - ) - } - - pragma[nomagic] - private predicate preControlsDefDirect(Guard g, PreSsa::Definition def, AbstractValue v) { - preControlsDirect(g, def.getBasicBlock(), v) - } - - /** Holds if pre-basic-block `bb` only is reached when guard `g` has abstract value `v`. */ - predicate preControls(Guard g, PreBasicBlocks::PreBasicBlock bb, AbstractValue v) { - preControlsDirect(g, bb, v) - or - exists(AbstractValue v0, Guard g0 | - preControls(g0, bb, v0) and - preImpliesStep(g0, v0, g, v) - ) - } - - private class PreSsaImplicitParameterDefinition extends PreSsa::Definition { - private Parameter p; - - PreSsaImplicitParameterDefinition() { - p = this.getDefinition().(AssignableDefinitions::ImplicitParameterDefinition).getParameter() - } - - Parameter getParameter() { result = p } - - /** - * Holds if the callable that this parameter belongs to can return `ret`, but - * only if this parameter is `null` or non-`null`, as specified by `isNull`. - */ - predicate nullGuardedReturn(Expr ret, boolean isNull) { - canReturn(p.getCallable(), ret) and - exists(PreBasicBlocks::PreBasicBlock bb, NullValue nv | - preControls(this.getARead(), bb, nv) - | - ret = bb.getAnElement() and - if nv.isNull() then isNull = true else isNull = false - ) - } - } - - private predicate canReturnBool(Callable c, Expr ret) { - canReturn(c, ret) and - c.getReturnType() instanceof BoolType - } - - private predicate boolReturnImplies(Expr ret, BooleanValue retVal, Guard g, AbstractValue v) { - canReturnBool(_, ret) and - isGuard(ret, retVal) and - g = ret and - v = retVal - or - exists(Guard g0, AbstractValue v0 | - boolReturnImplies(ret, retVal, g0, v0) and - preImpliesStep(g0, v0, g, v) - ) - } - - /** - * Holds if `ret` is an expression returned by the callable to which parameter - * `p` belongs, and `ret` having Boolean value `retVal` allows the conclusion - * that the parameter `p` either is `null` or non-`null`, as specified by `isNull`. - */ - private predicate validReturnInCustomNullCheck( - Expr ret, Parameter p, BooleanValue retVal, boolean isNull - ) { - exists(Callable c | - canReturnBool(c, ret) and - p.getCallable() = c - ) and - exists(PreSsaImplicitParameterDefinition def | p = def.getParameter() | - def.nullGuardedReturn(ret, isNull) - or - exists(NullValue nv | boolReturnImplies(ret, retVal, def.getARead(), nv) | - if nv.isNull() then isNull = true else isNull = false - ) - ) - } - - /** - * Gets a non-overridable callable with a Boolean return value that performs a - * `null`-check on parameter `p`. A return value having Boolean value `retVal` - * allows us to conclude that the argument either is `null` or non-`null`, as - * specified by `isNull`. - */ - private Callable customNullCheck(Parameter p, BooleanValue retVal, boolean isNull) { - result.getReturnType() instanceof BoolType and - not result.(Overridable).isOverridableOrImplementable() and - p.getCallable() = result and - not p.isParams() and - p.getType() = any(Type t | t instanceof RefType or t instanceof NullableType) and - forex(Expr ret | - canReturn(result, ret) and - not ret.(BoolLiteral).getBoolValue() = retVal.getValue().booleanNot() - | - validReturnInCustomNullCheck(ret, p, retVal, isNull) - ) - } - - pragma[nomagic] - private predicate conditionalAssign0( - Guard guard, AbstractValue vGuard, PreSsa::PhiNode phi, Expr e, PreSsa::Definition upd, - PreBasicBlocks::PreBasicBlock bbGuard, PreBasicBlocks::PreBasicBlock bbPhi - ) { - e = upd.getDefinition().getSource() and - upd = phi.getAnInput() and - preControlsDefDirect(guard, upd, vGuard) and - bbGuard.getAnElement() = guard and - bbPhi = phi.getBasicBlock() - } - - pragma[noinline] - private predicate conditionalAssign1( - Guard guard, AbstractValue vGuard, PreSsa::PhiNode phi, Expr e, PreSsa::Definition upd, - PreBasicBlocks::PreBasicBlock bbGuard - ) { - exists(PreBasicBlocks::PreBasicBlock bbPhi | - conditionalAssign0(guard, vGuard, phi, e, upd, bbGuard, bbPhi) and - bbGuard.strictlyDominates(bbPhi) and - not preControlsDefDirect(guard, phi, vGuard) - ) - } - - pragma[noinline] - private predicate conditionalAssign2( - Guard guard, AbstractValue vGuard, PreSsa::PhiNode phi, Expr e, PreSsa::Definition upd, - PreBasicBlocks::PreBasicBlock bbGuard, PreSsa::Definition other - ) { - conditionalAssign1(guard, vGuard, phi, e, upd, bbGuard) and - other != upd and - other = phi.getAnInput() - } - - pragma[noinline] - private predicate conditionalAssign3( - Guard guard, AbstractValue vGuard, PreSsa::Definition def, Expr e, PreSsa::Definition upd, - PreBasicBlocks::PreBasicBlock bbGuard, PreSsa::Definition other - ) { - conditionalAssign2(guard, vGuard, def, e, upd, bbGuard, other) and - preControlsDefDirect(guard, other, vGuard.getDualValue()) - } - - /** Gets the successor block that is reached when guard `g` has abstract value `v`. */ - private PreBasicBlocks::PreBasicBlock getConditionalSuccessor(Guard g, AbstractValue v) { - exists(PreBasicBlocks::ConditionBlock pred, ConditionalSuccessor s | - v.branch(pred.getLastNode(), s, g) - | - result = pred.getASuccessor(s) - ) - } - - pragma[noinline] - private predicate conditionalAssign4( - Guard guard, AbstractValue vGuard, PreSsa::Definition def, Expr e, PreSsa::Definition upd, - PreBasicBlocks::PreBasicBlock bbGuard, PreSsa::Definition other - ) { - conditionalAssign2(guard, vGuard, def, e, upd, bbGuard, other) and - other.getBasicBlock().dominates(bbGuard) and - not other.isLiveAtEndOfBlock(getConditionalSuccessor(guard, vGuard)) - } - - /** - * Holds if the evaluation of `guard` to `vGuard` implies that `def` is assigned - * expression `e`. - */ - private predicate conditionalAssign( - Guard guard, AbstractValue vGuard, PreSsa::Definition def, Expr e - ) { - // For example: - // v = guard ? e : x; - exists(ConditionalExpr c | c = def.getDefinition().getSource() | - guard = c.getCondition() and - vGuard = - any(BooleanValue bv | - bv.getValue() = true and - e = c.getThen() - or - bv.getValue() = false and - e = c.getElse() - ) - ) - or - exists(PreSsa::Definition upd, PreBasicBlocks::PreBasicBlock bbGuard | - conditionalAssign1(guard, vGuard, def, e, upd, bbGuard) - | - forall(PreSsa::Definition other | - conditionalAssign2(guard, vGuard, def, e, upd, bbGuard, other) - | - // For example: - // if (guard) - // upd = a; - // else - // other = b; - // def = phi(upd, other) - conditionalAssign3(guard, vGuard, def, e, upd, bbGuard, other) - or - // For example: - // other = a; - // if (guard) - // upd = b; - // def = phi(other, upd) - conditionalAssign4(guard, vGuard, def, e, upd, bbGuard, other) - ) - ) - } - - /** - * Holds if the evaluation of `guard` to `vGuard` implies that `def` is assigned - * an expression with abstract value `vDef`. - */ - private predicate conditionalAssignVal( - Expr guard, AbstractValue vGuard, PreSsa::Definition def, AbstractValue vDef - ) { - conditionalAssign(guard, vGuard, def, vDef.getAnExpr()) - } - - pragma[noinline] - private predicate relevantEq(PreSsa::Definition def, AbstractValue v, AssignableRead ar) { - conditionalAssignVal(_, _, def, v) and - ar = def.getARead() - } - - /** - * Gets an expression that directly tests whether expression `e1` is equal - * to expression `e2`. - * - * If the returned expression evaluates to `v`, then expression `e1` is - * guaranteed to be equal to `e2`, otherwise it is guaranteed to not be - * equal to `e2`. - * - * For example, if the expression `x != ""` evaluates to `false` then the - * expression `x` is guaranteed to be equal to `""`. - */ - private Expr getABooleanEqualityCheck(Expr e1, BooleanValue v, Expr e2) { - exists(boolean branch | branch = v.getValue() | - exists(ComparisonTest ct, ComparisonKind ck | - ct.getExpr() = result and - ct.getAnArgument() = e1 and - ct.getAnArgument() = e2 and - e2 != e1 and - ck = ct.getComparisonKind() - | - ck.isEquality() and branch = true - or - ck.isInequality() and branch = false - ) - or - result = - any(IsExpr ie | - ie.getExpr() = e1 and - e2 = ie.getPattern().(ConstantPatternExpr) and - branch = true - ) - ) - } - - /** - * Gets an expression that tests via matching whether expression `e1` is equal - * to expression `e2`. - * - * If the returned expression matches (`v.isMatch()`), then expression `e1` is - * guaranteed to be equal to `e2`. If the returned expression non-matches - * (`not v.isMatch()`), then this expression is guaranteed to not be equal to `e2`. - * - * For example, if the case statement `case ""` matches in - * - * ```csharp - * switch (o) - * { - * case "": - * return s; - * default: - * return ""; - * } - * ``` - * - * then `o` is guaranteed to be equal to `""`. - */ - private Expr getAMatchingEqualityCheck(Expr e1, MatchValue v, Expr e2) { - exists(Switch s, Case case | case = v.getCase() | - e1 = s.getExpr() and - result = e1 and - case = s.getACase() and - e2 = case.getPattern().(ConstantPatternExpr) and - v.isMatch() - ) - } - - pragma[nomagic] - private Expr getAnEqualityCheckVal(Expr e, AbstractValue v, AbstractValue vExpr) { - result = getAnEqualityCheck(e, v, vExpr.getAnExpr()) - } - - /** - * Holds if the evaluation of `guard` to `vGuard` implies that `def` does not - * have the value `vDef`. - */ - private predicate guardImpliesNotEqual( - Expr guard, AbstractValue vGuard, PreSsa::Definition def, AbstractValue vDef - ) { - exists(AssignableRead ar | relevantEq(def, vDef, ar) | - // For example: - // if (de == null); vGuard = TBooleanValue(false); vDef = TNullValue(true) - // but not - // if (de == "abc"); vGuard = TBooleanValue(false); vDef = TNullValue(false) - guard = getAnEqualityCheckVal(ar, vGuard.getDualValue(), vDef) and - vDef.isSingleton() - or - // For example: - // if (de != null); vGuard = TBooleanValue(true); vDef = TNullValue(true) - // or - // if (de == null); vGuard = TBooleanValue(true); vDef = TNullValue(false) - exists(NullValue nv | - guard = - ar.(DereferenceableExpr).getANullCheck(vGuard, any(boolean b | nv = TNullValue(b))) - | - vDef = nv.getDualValue() - ) - or - // For example: - // if (de == false); vGuard = TBooleanValue(true); vDef = TBooleanValue(true) - guard = getAnEqualityCheckVal(ar, vGuard, vDef.getDualValue()) - ) - } - - /** - * Holds if `def` can have a value that is not representable as an - * abstract value. - */ - private predicate hasPossibleUnknownValue(PreSsa::Definition def) { - exists(PreSsa::Definition input | input = def.getAnUltimateDefinition() | - not exists(input.getDefinition().getSource()) - or - exists(Expr e | e = stripConditionalExpr(input.getDefinition().getSource()) | - not e = any(AbstractValue v).getAnExpr() - ) - ) - } - - /** - * Gets an ultimate definition of `def` that is not itself a phi node. The - * boolean `fromBackEdge` indicates whether the flow from `result` to `def` - * goes through a back edge. - */ - private PreSsa::Definition getADefinition(PreSsa::Definition def, boolean fromBackEdge) { - result = def and - not def instanceof PreSsa::PhiNode and - fromBackEdge = false - or - exists(PreSsa::Definition input, PreBasicBlocks::PreBasicBlock pred, boolean fbe | - input = def.(PreSsa::PhiNode).getAnInput() - | - pred = def.getBasicBlock().getAPredecessor() and - input.isLiveAtEndOfBlock(pred) and - result = getADefinition(input, fbe) and - (if def.getBasicBlock().dominates(pred) then fromBackEdge = true else fromBackEdge = fbe) - ) - } - - /** - * Holds if `e` has abstract value `v` and may be assigned to `def`. The Boolean - * `fromBackEdge` indicates whether the flow from `e` to `def` goes through a - * back edge. - */ - private predicate possibleValue( - PreSsa::Definition def, boolean fromBackEdge, Expr e, AbstractValue v - ) { - not hasPossibleUnknownValue(def) and - exists(PreSsa::Definition input | input = getADefinition(def, fromBackEdge) | - e = stripConditionalExpr(input.getDefinition().getSource()) and - v.getAnExpr() = e - ) - } - - private predicate nonUniqueValue(PreSsa::Definition def, Expr e, AbstractValue v) { - possibleValue(def, false, e, v) and - possibleValue(def, _, any(Expr other | other != e), v) - } - - /** - * Holds if `e` has abstract value `v` and may be assigned to `def` without going - * through back edges, and all other possible ultimate definitions of `def` do not - * have abstract value `v`. The trivial case where `def` is an explicit update with - * source `e` is excluded. - */ - private predicate uniqueValue(PreSsa::Definition def, Expr e, AbstractValue v) { - possibleValue(def, false, e, v) and - not nonUniqueValue(def, e, v) and - exists(Expr other | possibleValue(def, _, other, _) and other != e) - } - - /** - * Holds if `guard` having abstract value `vGuard` implies that `def` has - * abstract value `vDef`. - */ - private predicate guardImpliesEqual( - Guard guard, AbstractValue vGuard, PreSsa::Definition def, AbstractValue vDef - ) { - guard = getAnEqualityCheck(def.getARead(), vGuard, vDef.getAnExpr()) - } - private predicate nullDef(PreSsa::Definition def) { nullValueImplied(def.getDefinition().getSource()) } @@ -1729,64 +958,25 @@ module Internal { nonEmptyValue(def.getDefinition().getSource()) } + deprecated predicate isGuard(Expr e, AbstractValue val) { + ( + e.getType() instanceof BoolType and + not e instanceof BoolLiteral and + not e instanceof SwitchCaseExpr and + not e instanceof PatternExpr and + exists(val.asBooleanValue()) + or + e instanceof DereferenceableExpr and + val.isNullness(_) + ) and + not e = any(ExprStmt es).getExpr() and + not e = any(LocalVariableDeclStmt s).getAVariableDeclExpr() + } + cached private module CachedWithCfg { private import semmle.code.csharp.Caching - cached - predicate isGuard(Expr e, AbstractValue val) { - Stages::ControlFlowStage::forceCachingInSameStage() and - ( - e.getType() instanceof BoolType and - not e instanceof BoolLiteral and - not e instanceof SwitchCaseExpr and - not e instanceof PatternExpr and - val = TBooleanValue(_) - or - e instanceof DereferenceableExpr and - val = TNullValue(_) - or - val.branch(_, _, e) - or - e instanceof EnumerableCollectionExpr and - val = TEmptyCollectionValue(_) - ) and - not e = any(ExprStmt es).getExpr() and - not e = any(LocalVariableDeclStmt s).getAVariableDeclExpr() - } - - /** - * Gets an expression that tests whether expression `e1` is equal to - * expression `e2`. - * - * If the returned expression has abstract value `v`, then expression `e1` is - * guaranteed to be equal to `e2`, and if the returned expression has abstract - * value `v.getDualValue()`, then this expression is guaranteed to be - * non-equal to `e`. - * - * For example, if the expression `x != ""` evaluates to `false` then the - * expression `x` is guaranteed to be equal to `""`. - */ - cached - Expr getAnEqualityCheck(Expr e1, AbstractValue v, Expr e2) { - result = getABooleanEqualityCheck(e1, v, e2) - or - result = getABooleanEqualityCheck(e2, v, e1) - or - result = getAMatchingEqualityCheck(e1, v, e2) - or - result = getAMatchingEqualityCheck(e2, v, e1) - } - - cached - predicate isCustomNullCheck(Call call, Expr arg, BooleanValue v, boolean isNull) { - exists(Callable callable, Parameter p | - arg = call.getArgumentForParameter(any(Parameter p0 | p0.getUnboundDeclaration() = p)) and - call.getTarget().getUnboundDeclaration() = callable and - callable = customNullCheck(p, v, isNull) - ) - } - private predicate firstReadSameVarUniquePredecessor( PreSsa::Definition def, AssignableRead read ) { @@ -1798,156 +988,6 @@ module Internal { ) } - /** - * Holds if the assumption that `g1` has abstract value `v1` implies that - * `g2` has abstract value `v2`, using one step of reasoning. That is, the - * evaluation of `g2` to `v2` dominates the evaluation of `g1` to `v1`. - * - * This predicate does not rely on the control flow graph. - */ - cached - predicate preImpliesStep(Guard g1, AbstractValue v1, Guard g2, AbstractValue v2) { - g1 = - any(BinaryOperation bo | - ( - bo instanceof BitwiseAndExpr or - bo instanceof LogicalAndExpr - ) and - g2 = bo.getAnOperand() and - v1 = TBooleanValue(true) and - v2 = v1 - ) - or - g1 = - any(BinaryOperation bo | - ( - bo instanceof BitwiseOrExpr or - bo instanceof LogicalOrExpr - ) and - g2 = bo.getAnOperand() and - v1 = TBooleanValue(false) and - v2 = v1 - ) - or - g2 = g1.(LogicalNotExpr).getOperand() and - v2 = TBooleanValue(v1.(BooleanValue).getValue().booleanNot()) - or - exists(ComparisonTest ct, boolean polarity, BoolLiteral boolLit, boolean b | - ct.getAnArgument() = boolLit and - b = boolLit.getBoolValue() and - g2 = ct.getAnArgument() and - g1 = ct.getExpr() and - v2 = TBooleanValue(v1.(BooleanValue).getValue().booleanXor(polarity).booleanXor(b)) - | - ct.getComparisonKind().isEquality() and - polarity = true - or - ct.getComparisonKind().isInequality() and - polarity = false - ) - or - exists(ConditionalExpr cond, boolean branch, Expr e, AbstractValue v | - e = v.getAnExpr() and - ( - cond.getThen() = e and branch = true - or - cond.getElse() = e and branch = false - ) - | - g1 = cond and - v1 = v.getDualValue() and - ( - // g1 === g2 ? e : ...; - g2 = cond.getCondition() and - v2 = TBooleanValue(branch.booleanNot()) - or - // g1 === ... ? g2 : e - g2 = cond.getThen() and - branch = false and - v2 = v1 - or - // g1 === g2 ? ... : e - g2 = cond.getElse() and - branch = true and - v2 = v1 - ) - ) - or - isGuard(g1, v1) and - v1 = - any(MatchValue mv | - mv.isMatch() and - g2 = g1 and - v2.getAnExpr() = mv.getCase().getPattern().(ConstantPatternExpr) and - v1 != v2 - ) - or - exists(boolean isNull | g1 = g2.(DereferenceableExpr).getANullCheck(v1, isNull) | - v2 = any(NullValue nv | if nv.isNull() then isNull = true else isNull = false) and - (g1 != g2 or v1 != v2) - ) - or - exists(boolean isEmpty | - g1 = g2.(EnumerableCollectionExpr).getAnEmptinessCheck(v1, isEmpty) - | - v2 = - any(EmptyCollectionValue ecv | if ecv.isEmpty() then isEmpty = true else isEmpty = false) and - g1 != g2 - ) - or - g1 instanceof DereferenceableExpr and - g1 = getNullEquivParent(g2) and - v1 instanceof NullValue and - v2 = v1 - or - g1 instanceof DereferenceableExpr and - g2 = getANullImplyingChild(g1) and - v1.(NullValue).isNonNull() and - v2 = v1 - or - g2 = g1.(AssignExpr).getRValue() and - isGuard(g1, v1) and - v2 = v1 - or - g2 = g1.(Assignment).getLValue() and - isGuard(g1, v1) and - v2 = v1 - or - g2 = g1.(CastExpr).getExpr() and - isGuard(g1, v1) and - v2 = v1.(NullValue) - or - exists(PreSsa::Definition def | - def.getDefinition().getSource() = g2 and - g1 = def.getARead() and - isGuard(g1, v1) and - v2 = v1 and - if v1.isReferentialProperty() then firstReadSameVarUniquePredecessor(def, g1) else any() - ) - or - exists(PreSsa::Definition def, AbstractValue v | - // If for example `def = g2 ? v : ...`, then a guard `g1` proving `def != v` - // ensures that `g2` evaluates to `false`. - conditionalAssignVal(g2, v2.getDualValue(), def, v) and - guardImpliesNotEqual(g1, v1, def, v) - ) - or - exists(PreSsa::Definition def, Expr e, AbstractValue v | - // If for example `def = g2 ? v : ...` and all other assignments to `def` are - // different from `v`, then a guard proving `def == v` ensures that `g2` - // evaluates to `true`. - uniqueValue(def, e, v) and - guardImpliesEqual(g1, v1, def, v) and - preControlsDirect(g2, any(PreBasicBlocks::PreBasicBlock bb | e = bb.getAnElement()), v2) and - not preControlsDirect(g2, any(PreBasicBlocks::PreBasicBlock bb | g1 = bb.getAnElement()), - v2) - ) - or - g2 = g1.(NullCoalescingExpr).getAnOperand() and - v1.(NullValue).isNull() and - v2 = v1 - } - cached predicate nullValueImplied(Expr e) { nullValue(e) @@ -2153,14 +1193,7 @@ module Internal { */ cached predicate guardControls(Guard g, BasicBlock bb, AbstractValue v) { - exists(ControlFlowElement cfe, ConditionalSuccessor cs | - v.branch(cfe, cs, g) and cfe.controlsBlock(bb, cs, _) - ) - or - exists(AbstractValue v0, Guard g0 | - guardControls(g0, bb, v0) and - impliesStep(g0, v0, g, v) - ) + g.(Guards::Guard).valueControls(bb, v) } pragma[nomagic] @@ -2214,21 +1247,6 @@ module Internal { ) } - private predicate adjacentReadPairSameVarUniquePredecessor( - Ssa::Definition def, ControlFlow::Node cfn1, ControlFlow::Node cfn2 - ) { - SsaImpl::adjacentReadPairSameVar(def, cfn1, cfn2) and - ( - cfn1 = cfn2 and - cfn1 = unique(ControlFlow::Node other | SsaImpl::adjacentReadPairSameVar(def, other, cfn2)) - or - cfn1 = - unique(ControlFlow::Node other | - SsaImpl::adjacentReadPairSameVar(def, other, cfn2) and other != cfn2 - ) - ) - } - pragma[noinline] private predicate isGuardedByExpr0( AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, AbstractValue v @@ -2246,12 +1264,7 @@ module Internal { forall(ControlFlow::Node subCfn, Ssa::Definition def | nodeIsGuardedBySameSubExprSsaDef(_, guarded, g, subCfn, sub, v, def) | - exists(ControlFlow::Node guardedCfn | - def = guarded.getAnSsaQualifier(guardedCfn) and - if v.isReferentialProperty() - then adjacentReadPairSameVarUniquePredecessor(def, subCfn, guardedCfn) - else any() - ) + def = guarded.getAnSsaQualifier(_) ) } @@ -2267,38 +1280,7 @@ module Internal { guarded .getAstNode() .(AccessOrCallExpr) - .getAnSsaQualifier(guarded.getBasicBlock().getANode()) and - if v.isReferentialProperty() - then adjacentReadPairSameVarUniquePredecessor(def, subCfn, guarded) - else any() - ) - } - - private predicate firstReadUniquePredecessor(Ssa::ExplicitDefinition def, ControlFlow::Node cfn) { - exists(def.getAFirstReadAtNode(cfn)) and - not exists(ControlFlow::Node other | - SsaImpl::adjacentReadPairSameVar(def, other, cfn) and - other != cfn - ) - } - - /** - * Holds if the assumption that `g1` has abstract value `v1` implies that - * `g2` has abstract value `v2`, using one step of reasoning. That is, the - * evaluation of `g2` to `v2` dominates the evaluation of `g1` to `v1`. - * - * This predicate relies on the control flow graph. - */ - private predicate impliesStep(Guard g1, AbstractValue v1, Guard g2, AbstractValue v2) { - preImpliesStep(g1, v1, g2, v2) - or - forex(ControlFlow::Node cfn1 | cfn1 = g1.getAControlFlowNode() | - exists(Ssa::ExplicitDefinition def | def.getADefinition().getSource() = g2 | - g1 = def.getAReadAtNode(cfn1) and - isGuard(g1, v1) and - v2 = v1 and - if v1.isReferentialProperty() then firstReadUniquePredecessor(def, cfn1) else any() - ) + .getAnSsaQualifier(guarded.getBasicBlock().getANode()) ) } } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll index 62ac89faef8..f4a76b2f577 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -2906,7 +2906,8 @@ class DataFlowExpr = Expr; /** Holds if `e` is an expression that always has the same Boolean value `val`. */ private predicate constantBooleanExpr(Expr e, boolean val) { - e = any(AbstractValues::BooleanValue bv | val = bv.getValue()).getAnExpr() + e.getType() instanceof BoolType and + e.getValue() = val.toString() or exists(Ssa::ExplicitDefinition def, Expr src | e = def.getARead() and diff --git a/csharp/ql/test/library-tests/controlflow/guards/AbstractValue.expected b/csharp/ql/test/library-tests/controlflow/guards/AbstractValue.expected index b6059d48772..74e9febe662 100644 --- a/csharp/ql/test/library-tests/controlflow/guards/AbstractValue.expected +++ b/csharp/ql/test/library-tests/controlflow/guards/AbstractValue.expected @@ -1,4 +1,22 @@ -abstractValue +| | Assert.cs:9:31:9:32 | "" | +| | Assert.cs:16:31:16:32 | "" | +| | Assert.cs:23:31:23:32 | "" | +| | Assert.cs:30:31:30:32 | "" | +| | Assert.cs:37:31:37:32 | "" | +| | Assert.cs:44:31:44:32 | "" | +| | Assert.cs:51:31:51:32 | "" | +| | Assert.cs:58:31:58:32 | "" | +| | Assert.cs:65:31:65:32 | "" | +| | Assert.cs:72:31:72:32 | "" | +| | Assert.cs:79:31:79:32 | "" | +| | Collections.cs:73:40:73:41 | "" | +| | Guards.cs:96:18:96:19 | "" | +| | Guards.cs:97:31:97:31 | access to parameter s | +| | Guards.cs:157:18:157:19 | "" | +| | Guards.cs:158:24:158:24 | access to parameter o | +| | Guards.cs:282:13:282:14 | "" | +| | Guards.cs:283:17:283:17 | access to parameter o | +| | Guards.cs:341:31:341:32 | "" | | 0 | Collections.cs:11:32:11:32 | 0 | | 0 | Collections.cs:13:28:13:28 | 0 | | 0 | Collections.cs:15:27:15:27 | 0 | @@ -21,9 +39,13 @@ abstractValue | 0 | Collections.cs:86:17:86:32 | 0 | | 0 | Guards.cs:12:24:12:24 | 0 | | 0 | Guards.cs:78:26:78:26 | 0 | +| 0 | Guards.cs:78:26:78:26 | (...) ... | | 0 | Guards.cs:80:25:80:25 | 0 | +| 0 | Guards.cs:80:25:80:25 | (...) ... | | 0 | Guards.cs:82:26:82:26 | 0 | +| 0 | Guards.cs:82:26:82:26 | (...) ... | | 0 | Guards.cs:92:30:92:30 | 0 | +| 0 | Guards.cs:92:30:92:30 | (...) ... | | 0 | Guards.cs:241:17:241:17 | 0 | | 0 | Guards.cs:255:17:255:19 | access to constant A | | 0 | Guards.cs:298:21:298:21 | 0 | @@ -46,340 +68,823 @@ abstractValue | 1 | Collections.cs:81:36:81:36 | 1 | | 1 | Collections.cs:88:13:88:32 | 1 | | 1 | Guards.cs:92:25:92:25 | 1 | +| 1 | Guards.cs:92:25:92:25 | (...) ... | +| 1 | Guards.cs:243:13:243:17 | ... = ... | | 1 | Guards.cs:243:17:243:17 | 1 | | 1 | Guards.cs:246:18:246:18 | 1 | +| 1 | Guards.cs:257:13:257:19 | ... = ... | | 1 | Guards.cs:257:17:257:19 | access to constant B | | 1 | Guards.cs:260:18:260:20 | access to constant B | | 1 | Guards.cs:299:18:299:18 | 1 | | 1 | Guards.cs:311:18:311:18 | 1 | +| 1 | Guards.cs:319:13:319:17 | ... = ... | | 1 | Guards.cs:319:17:319:17 | 1 | | 1 | Guards.cs:322:13:322:13 | 1 | | 1 | Guards.cs:323:18:323:18 | 1 | +| 1 | Guards.cs:331:13:331:19 | ... = ... | | 1 | Guards.cs:331:17:331:19 | access to constant B | | 1 | Guards.cs:334:13:334:15 | access to constant B | | 1 | Guards.cs:335:18:335:18 | 1 | | 3 | Collections.cs:54:13:54:42 | 3 | | 3 | Collections.cs:62:17:62:46 | 3 | | 10 | Guards.cs:84:25:84:26 | 10 | +| 10 | Guards.cs:84:25:84:26 | (...) ... | | 10 | Guards.cs:86:26:86:27 | 10 | -| empty | Collections.cs:53:13:53:16 | access to parameter args | -| empty | Collections.cs:56:9:56:25 | ... = ... | -| empty | Collections.cs:56:13:56:25 | array creation of type String[] | -| empty | Collections.cs:57:9:57:13 | ... = ... | -| empty | Collections.cs:57:13:57:13 | access to local variable x | -| empty | Collections.cs:64:13:64:13 | access to local variable x | -| empty | Collections.cs:86:17:86:32 | array creation of type String[] | -| empty | Collections.cs:86:30:86:32 | { ..., ... } | -| empty | Collections.cs:87:22:87:24 | { ..., ... } | +| 10 | Guards.cs:86:26:86:27 | (...) ... | +| | Guards.cs:18:31:18:46 | "" | +| a | Collections.cs:54:28:54:30 | "a" | +| a | Collections.cs:62:32:62:34 | "a" | +| a | Collections.cs:66:19:66:21 | "a" | +| a | Collections.cs:88:28:88:30 | "a" | +| a | Collections.cs:89:24:89:26 | "a" | +| b | Collections.cs:54:33:54:35 | "b" | +| b | Collections.cs:62:37:62:39 | "b" | +| b | Collections.cs:67:19:67:21 | "b" | +| c | Collections.cs:54:38:54:40 | "c" | +| c | Collections.cs:62:42:62:44 | "c" | | false | Assert.cs:85:61:85:65 | false | | false | Guards.cs:178:16:178:20 | false | | false | Guards.cs:181:53:181:57 | false | +| false | Guards.cs:217:13:217:22 | ... = ... | | false | Guards.cs:217:18:217:22 | false | | false | Guards.cs:228:18:228:22 | false | +| false | Guards.cs:295:13:295:22 | ... = ... | | false | Guards.cs:295:18:295:22 | false | | false | Guards.cs:305:18:305:22 | false | -| non-empty | Collections.cs:54:9:54:42 | ... = ... | -| non-empty | Collections.cs:54:13:54:42 | array creation of type String[] | -| non-empty | Collections.cs:54:26:54:42 | { ..., ... } | -| non-empty | Collections.cs:55:9:55:13 | ... = ... | -| non-empty | Collections.cs:55:13:55:13 | access to local variable x | -| non-empty | Collections.cs:62:17:62:46 | array creation of type String[] | -| non-empty | Collections.cs:62:30:62:46 | { ..., ... } | -| non-empty | Collections.cs:67:13:67:13 | access to local variable x | -| non-empty | Collections.cs:88:9:88:32 | ... = ... | -| non-empty | Collections.cs:88:13:88:32 | array creation of type String[] | -| non-empty | Collections.cs:88:26:88:32 | { ..., ... } | -| non-empty | Collections.cs:89:22:89:28 | { ..., ... } | -| non-null | Assert.cs:9:31:9:32 | "" | -| non-null | Assert.cs:10:9:10:13 | access to type Debug | -| non-null | Assert.cs:11:9:11:15 | access to type Console | -| non-null | Assert.cs:11:27:11:27 | access to local variable s | -| non-null | Assert.cs:16:31:16:32 | "" | -| non-null | Assert.cs:17:9:17:14 | access to type Assert | -| non-null | Assert.cs:18:9:18:15 | access to type Console | -| non-null | Assert.cs:23:31:23:32 | "" | -| non-null | Assert.cs:24:9:24:14 | access to type Assert | -| non-null | Assert.cs:25:9:25:15 | access to type Console | -| non-null | Assert.cs:30:31:30:32 | "" | -| non-null | Assert.cs:31:9:31:14 | access to type Assert | -| non-null | Assert.cs:32:9:32:15 | access to type Console | -| non-null | Assert.cs:37:31:37:32 | "" | -| non-null | Assert.cs:38:9:38:14 | access to type Assert | -| non-null | Assert.cs:39:9:39:15 | access to type Console | -| non-null | Assert.cs:44:31:44:32 | "" | -| non-null | Assert.cs:45:9:45:14 | access to type Assert | -| non-null | Assert.cs:46:9:46:15 | access to type Console | -| non-null | Assert.cs:51:31:51:32 | "" | -| non-null | Assert.cs:52:9:52:14 | access to type Assert | -| non-null | Assert.cs:53:9:53:15 | access to type Console | -| non-null | Assert.cs:58:31:58:32 | "" | -| non-null | Assert.cs:59:9:59:14 | access to type Assert | -| non-null | Assert.cs:60:9:60:15 | access to type Console | -| non-null | Assert.cs:65:31:65:32 | "" | -| non-null | Assert.cs:66:9:66:14 | access to type Assert | -| non-null | Assert.cs:67:9:67:15 | access to type Console | -| non-null | Assert.cs:72:31:72:32 | "" | -| non-null | Assert.cs:73:9:73:14 | access to type Assert | -| non-null | Assert.cs:74:9:74:15 | access to type Console | -| non-null | Assert.cs:79:31:79:32 | "" | -| non-null | Assert.cs:80:9:80:14 | access to type Assert | -| non-null | Assert.cs:81:9:81:15 | access to type Console | -| non-null | Assert.cs:93:9:93:35 | this access | -| non-null | Collections.cs:11:17:11:20 | access to parameter args | -| non-null | Collections.cs:12:13:12:16 | access to parameter args | -| non-null | Collections.cs:13:13:13:16 | access to parameter args | -| non-null | Collections.cs:14:13:14:16 | access to parameter args | -| non-null | Collections.cs:15:13:15:16 | access to parameter args | -| non-null | Collections.cs:16:13:16:16 | access to parameter args | -| non-null | Collections.cs:17:13:17:16 | access to parameter args | -| non-null | Collections.cs:22:17:22:20 | access to parameter args | -| non-null | Collections.cs:23:13:23:16 | access to parameter args | -| non-null | Collections.cs:24:13:24:16 | access to parameter args | -| non-null | Collections.cs:25:13:25:16 | access to parameter args | -| non-null | Collections.cs:26:13:26:16 | access to parameter args | -| non-null | Collections.cs:27:13:27:16 | access to parameter args | -| non-null | Collections.cs:28:13:28:16 | access to parameter args | -| non-null | Collections.cs:33:17:33:20 | access to parameter args | -| non-null | Collections.cs:34:13:34:16 | access to parameter args | -| non-null | Collections.cs:35:13:35:16 | access to parameter args | -| non-null | Collections.cs:36:13:36:16 | access to parameter args | -| non-null | Collections.cs:37:13:37:16 | access to parameter args | -| non-null | Collections.cs:38:13:38:16 | access to parameter args | -| non-null | Collections.cs:39:13:39:16 | access to parameter args | -| non-null | Collections.cs:44:17:44:20 | access to parameter args | -| non-null | Collections.cs:49:13:49:16 | access to parameter args | -| non-null | Collections.cs:51:17:51:20 | access to parameter args | -| non-null | Collections.cs:51:17:51:30 | call to method ToArray | -| non-null | Collections.cs:52:9:52:12 | access to parameter args | -| non-null | Collections.cs:53:9:53:9 | access to local variable x | -| non-null | Collections.cs:53:9:53:26 | ... = ... | -| non-null | Collections.cs:53:13:53:16 | access to parameter args | -| non-null | Collections.cs:53:13:53:26 | call to method ToArray | -| non-null | Collections.cs:54:9:54:9 | access to local variable x | -| non-null | Collections.cs:54:9:54:42 | ... = ... | -| non-null | Collections.cs:54:13:54:42 | array creation of type String[] | -| non-null | Collections.cs:54:28:54:30 | "a" | -| non-null | Collections.cs:54:33:54:35 | "b" | -| non-null | Collections.cs:54:38:54:40 | "c" | -| non-null | Collections.cs:55:9:55:9 | access to local variable x | -| non-null | Collections.cs:55:9:55:13 | ... = ... | -| non-null | Collections.cs:55:13:55:13 | access to local variable x | -| non-null | Collections.cs:56:9:56:9 | access to local variable x | -| non-null | Collections.cs:56:9:56:25 | ... = ... | -| non-null | Collections.cs:56:13:56:25 | array creation of type String[] | -| non-null | Collections.cs:57:9:57:9 | access to local variable x | -| non-null | Collections.cs:57:9:57:13 | ... = ... | -| non-null | Collections.cs:57:13:57:13 | access to local variable x | -| non-null | Collections.cs:62:17:62:46 | array creation of type String[] | -| non-null | Collections.cs:62:17:62:55 | call to method ToList | -| non-null | Collections.cs:62:32:62:34 | "a" | -| non-null | Collections.cs:62:37:62:39 | "b" | -| non-null | Collections.cs:62:42:62:44 | "c" | -| non-null | Collections.cs:63:9:63:9 | access to local variable x | -| non-null | Collections.cs:64:13:64:13 | access to local variable x | -| non-null | Collections.cs:66:13:66:13 | access to local variable x | -| non-null | Collections.cs:66:19:66:21 | "a" | -| non-null | Collections.cs:67:13:67:13 | access to local variable x | -| non-null | Collections.cs:67:19:67:21 | "b" | -| non-null | Collections.cs:73:35:73:35 | access to parameter s | -| non-null | Collections.cs:73:40:73:41 | "" | -| non-null | Collections.cs:74:17:74:20 | access to parameter args | -| non-null | Collections.cs:74:26:74:32 | access to local function IsEmpty | -| non-null | Collections.cs:74:26:74:32 | delegate creation of type Func | -| non-null | Collections.cs:74:26:74:32 | this access | -| non-null | Collections.cs:75:13:75:16 | access to parameter args | -| non-null | Collections.cs:75:24:75:30 | access to local function IsEmpty | -| non-null | Collections.cs:75:24:75:30 | delegate creation of type Func | -| non-null | Collections.cs:75:24:75:30 | this access | -| non-null | Collections.cs:76:13:76:16 | access to parameter args | -| non-null | Collections.cs:76:24:76:30 | access to local function IsEmpty | -| non-null | Collections.cs:76:24:76:30 | delegate creation of type Func | -| non-null | Collections.cs:76:24:76:30 | this access | -| non-null | Collections.cs:77:13:77:16 | access to parameter args | -| non-null | Collections.cs:77:24:77:30 | access to local function IsEmpty | -| non-null | Collections.cs:77:24:77:30 | delegate creation of type Func | -| non-null | Collections.cs:77:24:77:30 | this access | -| non-null | Collections.cs:78:13:78:16 | access to parameter args | -| non-null | Collections.cs:78:24:78:30 | access to local function IsEmpty | -| non-null | Collections.cs:78:24:78:30 | delegate creation of type Func | -| non-null | Collections.cs:78:24:78:30 | this access | -| non-null | Collections.cs:79:13:79:16 | access to parameter args | -| non-null | Collections.cs:79:24:79:30 | access to local function IsEmpty | -| non-null | Collections.cs:79:24:79:30 | delegate creation of type Func | -| non-null | Collections.cs:79:24:79:30 | this access | -| non-null | Collections.cs:80:13:80:16 | access to parameter args | -| non-null | Collections.cs:80:24:80:30 | access to local function IsEmpty | -| non-null | Collections.cs:80:24:80:30 | delegate creation of type Func | -| non-null | Collections.cs:80:24:80:30 | this access | -| non-null | Collections.cs:81:13:81:16 | access to parameter args | -| non-null | Collections.cs:81:24:81:30 | access to local function IsEmpty | -| non-null | Collections.cs:81:24:81:30 | delegate creation of type Func | -| non-null | Collections.cs:81:24:81:30 | this access | -| non-null | Collections.cs:86:17:86:32 | array creation of type String[] | -| non-null | Collections.cs:87:22:87:24 | array creation of type String[] | -| non-null | Collections.cs:88:9:88:9 | access to local variable x | -| non-null | Collections.cs:88:9:88:32 | ... = ... | -| non-null | Collections.cs:88:13:88:32 | array creation of type String[] | -| non-null | Collections.cs:88:28:88:30 | "a" | -| non-null | Collections.cs:89:22:89:28 | array creation of type String[] | -| non-null | Collections.cs:89:24:89:26 | "a" | -| non-null | Collections.cs:94:29:94:32 | access to parameter args | -| non-null | Collections.cs:95:13:95:19 | access to type Console | -| non-null | Collections.cs:95:31:95:34 | access to parameter args | -| non-null | Collections.cs:100:29:100:32 | access to parameter args | -| non-null | Collections.cs:102:9:102:15 | access to type Console | -| non-null | Collections.cs:102:27:102:30 | access to parameter args | -| non-null | Guards.cs:12:13:12:13 | access to parameter s | -| non-null | Guards.cs:14:13:14:19 | access to type Console | -| non-null | Guards.cs:14:31:14:31 | access to parameter s | -| non-null | Guards.cs:18:13:18:19 | access to type Console | -| non-null | Guards.cs:18:31:18:46 | "" | -| non-null | Guards.cs:26:13:26:19 | access to type Console | -| non-null | Guards.cs:26:31:26:31 | access to parameter s | -| non-null | Guards.cs:32:14:32:19 | access to type String | -| non-null | Guards.cs:33:13:33:19 | access to type Console | -| non-null | Guards.cs:33:31:33:35 | ... + ... | -| non-null | Guards.cs:36:14:36:20 | access to type Console | -| 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:39:13:39:19 | access to type Console | -| 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:42:14:42:20 | access to type Console | -| 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:17 | this access | -| non-null | Guards.cs:45:13:45:19 | access to type Console | -| non-null | Guards.cs:45:31:45:42 | object creation of type Guards | -| non-null | Guards.cs:47:13:47:17 | this access | -| non-null | Guards.cs:48:13:48:19 | access to type Console | -| non-null | Guards.cs:48:31:48:34 | this access | -| non-null | Guards.cs:48:31:48:40 | access to field Field | -| non-null | Guards.cs:53:13:53:13 | access to parameter g | -| non-null | Guards.cs:55:9:55:15 | access to type Console | -| non-null | Guards.cs:55:27:55:27 | access to parameter g | -| non-null | Guards.cs:55:27:55:33 | access to field Field | -| non-null | Guards.cs:60:13:60:13 | access to parameter g | -| non-null | Guards.cs:61:19:61:33 | object creation of type Exception | -| non-null | Guards.cs:62:9:62:15 | access to type Console | -| non-null | Guards.cs:62:27:62:27 | access to parameter g | -| non-null | Guards.cs:62:27:62:36 | access to property Property | -| non-null | Guards.cs:62:27:62:45 | access to property Property | -| non-null | Guards.cs:62:27:62:51 | access to field Field | -| non-null | Guards.cs:63:9:63:15 | access to type Console | -| non-null | Guards.cs:63:27:63:27 | access to parameter g | -| non-null | Guards.cs:63:27:63:36 | access to property Property | -| non-null | Guards.cs:70:13:70:19 | access to type Console | -| non-null | Guards.cs:70:31:70:31 | access to parameter s | -| non-null | Guards.cs:71:13:71:13 | access to parameter s | -| non-null | Guards.cs:72:13:72:19 | access to type Console | -| non-null | Guards.cs:78:26:78:26 | (...) ... | -| non-null | Guards.cs:79:13:79:19 | access to type Console | -| non-null | Guards.cs:79:31:79:31 | access to parameter s | -| non-null | Guards.cs:80:25:80:25 | (...) ... | -| non-null | Guards.cs:81:13:81:19 | access to type Console | -| non-null | Guards.cs:81:31:81:31 | access to parameter s | -| non-null | Guards.cs:82:26:82:26 | (...) ... | -| non-null | Guards.cs:83:13:83:19 | access to type Console | -| non-null | Guards.cs:83:31:83:31 | access to parameter s | -| non-null | Guards.cs:84:25:84:26 | (...) ... | -| non-null | Guards.cs:85:13:85:19 | access to type Console | -| non-null | Guards.cs:85:31:85:31 | access to parameter s | -| non-null | Guards.cs:86:26:86:27 | (...) ... | -| non-null | Guards.cs:87:13:87:19 | access to type Console | -| non-null | Guards.cs:87:31:87:31 | access to parameter s | -| non-null | Guards.cs:89:13:89:19 | access to type Console | -| non-null | Guards.cs:89:31:89:31 | access to parameter s | -| non-null | Guards.cs:91:13:91:19 | access to type Console | -| non-null | Guards.cs:92:25:92:25 | (...) ... | -| non-null | Guards.cs:92:30:92:30 | (...) ... | -| non-null | Guards.cs:93:13:93:19 | access to type Console | -| non-null | Guards.cs:95:13:95:19 | access to type Console | -| non-null | Guards.cs:96:18:96:19 | "" | -| non-null | Guards.cs:97:13:97:19 | access to type Console | -| non-null | Guards.cs:97:31:97:31 | access to parameter s | -| non-null | Guards.cs:99:13:99:19 | access to type Console | -| non-null | Guards.cs:104:13:104:13 | access to parameter g | -| non-null | Guards.cs:105:19:105:33 | object creation of type Exception | -| non-null | Guards.cs:106:9:106:9 | access to parameter g | -| non-null | Guards.cs:106:9:106:18 | access to property Property | -| non-null | Guards.cs:107:9:107:15 | access to type Console | -| non-null | Guards.cs:107:27:107:27 | access to parameter g | -| non-null | Guards.cs:108:9:108:15 | access to type Console | -| non-null | Guards.cs:108:27:108:27 | access to parameter g | -| non-null | Guards.cs:108:27:108:36 | access to property Property | -| non-null | Guards.cs:113:21:113:21 | access to parameter g | -| non-null | Guards.cs:114:14:114:14 | access to parameter g | -| non-null | Guards.cs:114:14:114:23 | access to property Property | -| non-null | Guards.cs:114:14:114:32 | access to property Property | -| non-null | Guards.cs:115:9:115:55 | ... = ... | -| non-null | Guards.cs:115:17:115:17 | access to parameter g | -| non-null | Guards.cs:115:17:115:26 | access to property Property | -| non-null | Guards.cs:115:17:115:35 | access to property Property | -| non-null | Guards.cs:115:17:115:55 | ... ?? ... | -| non-null | Guards.cs:115:46:115:55 | throw ... | -| non-null | Guards.cs:116:9:116:15 | access to type Console | -| non-null | Guards.cs:116:27:116:27 | access to parameter g | -| non-null | Guards.cs:116:27:116:36 | access to property Property | -| non-null | Guards.cs:116:27:116:45 | access to property Property | -| non-null | Guards.cs:116:27:116:51 | access to field Field | -| non-null | Guards.cs:117:9:117:9 | access to parameter g | -| non-null | Guards.cs:117:9:117:18 | access to property Property | -| non-null | Guards.cs:118:9:118:15 | access to type Console | -| non-null | Guards.cs:118:27:118:27 | access to parameter g | -| non-null | Guards.cs:119:9:119:15 | access to type Console | -| non-null | Guards.cs:119:27:119:27 | access to parameter g | -| non-null | Guards.cs:119:27:119:36 | access to property Property | -| non-null | Guards.cs:125:18:125:19 | access to parameter s1 | -| non-null | Guards.cs:125:29:125:30 | access to parameter s1 | -| non-null | Guards.cs:132:16:132:16 | access to parameter s | -| non-null | Guards.cs:138:20:138:20 | access to parameter s | -| non-null | Guards.cs:145:20:145:20 | access to local variable s | -| non-null | Guards.cs:154:24:154:24 | access to parameter o | -| non-null | Guards.cs:156:24:156:24 | access to local variable a | -| non-null | Guards.cs:158:24:158:24 | access to parameter o | -| non-null | Guards.cs:162:24:162:24 | access to parameter o | -| non-null | Guards.cs:168:14:168:19 | access to type String | -| non-null | Guards.cs:169:13:169:19 | access to type Console | -| non-null | Guards.cs:169:31:169:31 | access to parameter x | -| non-null | Guards.cs:183:38:183:49 | this access | -| non-null | Guards.cs:189:14:189:25 | this access | -| non-null | Guards.cs:190:13:190:19 | access to type Console | -| non-null | Guards.cs:191:14:191:25 | this access | -| non-null | Guards.cs:192:13:192:19 | access to type Console | -| non-null | Guards.cs:193:14:193:25 | this access | -| non-null | Guards.cs:194:13:194:19 | access to type Console | -| non-null | Guards.cs:195:13:195:27 | this access | -| non-null | Guards.cs:196:13:196:19 | access to type Console | -| non-null | Guards.cs:197:14:197:29 | this access | -| non-null | Guards.cs:198:13:198:19 | access to type Console | -| non-null | Guards.cs:205:13:205:13 | access to parameter o | -| non-null | Guards.cs:206:33:206:36 | access to parameter args | -| non-null | Guards.cs:208:17:208:17 | access to parameter o | -| non-null | Guards.cs:268:30:268:41 | call to method GetType | -| non-null | Guards.cs:269:13:269:14 | access to parameter o1 | -| non-null | Guards.cs:270:30:270:31 | access to parameter o2 | -| non-null | Guards.cs:279:17:279:17 | access to parameter o | -| non-null | Guards.cs:281:17:281:17 | access to local variable a | -| non-null | Guards.cs:283:17:283:17 | access to parameter o | -| non-null | Guards.cs:287:17:287:17 | access to parameter o | -| non-null | Guards.cs:341:31:341:32 | "" | -| non-null | Guards.cs:343:13:343:19 | access to type Console | -| non-null | Guards.cs:343:31:343:31 | access to local variable s | -| non-null | Guards.cs:349:13:349:13 | access to parameter o | +| not | Guards.cs:99:31:99:31 | access to parameter s | +| not | Guards.cs:160:24:160:24 | access to parameter o | +| not | Guards.cs:162:24:162:24 | access to parameter o | +| not | Guards.cs:285:17:285:17 | access to parameter o | +| not | Guards.cs:287:17:287:17 | access to parameter o | +| not null | Assert.cs:9:20:9:20 | access to parameter b | +| not null | Assert.cs:9:31:9:32 | "" | +| not null | Assert.cs:10:9:10:13 | access to type Debug | +| not null | Assert.cs:10:9:10:31 | call to method Assert | +| not null | Assert.cs:10:22:10:30 | ... != ... | +| not null | Assert.cs:11:9:11:15 | access to type Console | +| not null | Assert.cs:11:9:11:35 | call to method WriteLine | +| not null | Assert.cs:11:27:11:27 | access to local variable s | +| not null | Assert.cs:11:27:11:34 | access to property Length | +| not null | Assert.cs:16:20:16:20 | access to parameter b | +| not null | Assert.cs:16:31:16:32 | "" | +| not null | Assert.cs:17:9:17:14 | access to type Assert | +| not null | Assert.cs:17:9:17:24 | call to method IsNull | +| not null | Assert.cs:18:9:18:15 | access to type Console | +| not null | Assert.cs:18:9:18:35 | call to method WriteLine | +| not null | Assert.cs:18:27:18:34 | access to property Length | +| not null | Assert.cs:23:20:23:20 | access to parameter b | +| not null | Assert.cs:23:31:23:32 | "" | +| not null | Assert.cs:24:9:24:14 | access to type Assert | +| not null | Assert.cs:24:9:24:27 | call to method IsNotNull | +| not null | Assert.cs:25:9:25:15 | access to type Console | +| not null | Assert.cs:25:9:25:35 | call to method WriteLine | +| not null | Assert.cs:25:27:25:34 | access to property Length | +| not null | Assert.cs:30:20:30:20 | access to parameter b | +| not null | Assert.cs:30:31:30:32 | "" | +| not null | Assert.cs:31:9:31:14 | access to type Assert | +| not null | Assert.cs:31:9:31:32 | call to method IsTrue | +| not null | Assert.cs:31:23:31:31 | ... == ... | +| not null | Assert.cs:32:9:32:15 | access to type Console | +| not null | Assert.cs:32:9:32:35 | call to method WriteLine | +| not null | Assert.cs:32:27:32:34 | access to property Length | +| not null | Assert.cs:37:20:37:20 | access to parameter b | +| not null | Assert.cs:37:31:37:32 | "" | +| not null | Assert.cs:38:9:38:14 | access to type Assert | +| not null | Assert.cs:38:9:38:32 | call to method IsTrue | +| not null | Assert.cs:38:23:38:31 | ... != ... | +| not null | Assert.cs:39:9:39:15 | access to type Console | +| not null | Assert.cs:39:9:39:35 | call to method WriteLine | +| not null | Assert.cs:39:27:39:27 | access to local variable s | +| not null | Assert.cs:39:27:39:34 | access to property Length | +| not null | Assert.cs:44:20:44:20 | access to parameter b | +| not null | Assert.cs:44:31:44:32 | "" | +| not null | Assert.cs:45:9:45:14 | access to type Assert | +| not null | Assert.cs:45:9:45:33 | call to method IsFalse | +| not null | Assert.cs:45:24:45:32 | ... != ... | +| not null | Assert.cs:46:9:46:15 | access to type Console | +| not null | Assert.cs:46:9:46:35 | call to method WriteLine | +| not null | Assert.cs:46:27:46:34 | access to property Length | +| not null | Assert.cs:51:20:51:20 | access to parameter b | +| not null | Assert.cs:51:31:51:32 | "" | +| not null | Assert.cs:52:9:52:14 | access to type Assert | +| not null | Assert.cs:52:9:52:33 | call to method IsFalse | +| not null | Assert.cs:52:24:52:32 | ... == ... | +| not null | Assert.cs:53:9:53:15 | access to type Console | +| not null | Assert.cs:53:9:53:35 | call to method WriteLine | +| not null | Assert.cs:53:27:53:27 | access to local variable s | +| not null | Assert.cs:53:27:53:34 | access to property Length | +| not null | Assert.cs:58:20:58:20 | access to parameter b | +| not null | Assert.cs:58:31:58:32 | "" | +| not null | Assert.cs:59:9:59:14 | access to type Assert | +| not null | Assert.cs:59:9:59:37 | call to method IsTrue | +| not null | Assert.cs:59:23:59:31 | ... != ... | +| not null | Assert.cs:59:23:59:36 | ... && ... | +| not null | Assert.cs:59:36:59:36 | access to parameter b | +| not null | Assert.cs:60:9:60:15 | access to type Console | +| not null | Assert.cs:60:9:60:35 | call to method WriteLine | +| not null | Assert.cs:60:27:60:27 | access to local variable s | +| not null | Assert.cs:60:27:60:34 | access to property Length | +| not null | Assert.cs:65:20:65:20 | access to parameter b | +| not null | Assert.cs:65:31:65:32 | "" | +| not null | Assert.cs:66:9:66:14 | access to type Assert | +| not null | Assert.cs:66:9:66:38 | call to method IsFalse | +| not null | Assert.cs:66:24:66:32 | ... == ... | +| not null | Assert.cs:66:24:66:37 | ... \|\| ... | +| not null | Assert.cs:66:37:66:37 | access to parameter b | +| not null | Assert.cs:67:9:67:15 | access to type Console | +| not null | Assert.cs:67:9:67:35 | call to method WriteLine | +| not null | Assert.cs:67:27:67:27 | access to local variable s | +| not null | Assert.cs:67:27:67:34 | access to property Length | +| not null | Assert.cs:72:20:72:20 | access to parameter b | +| not null | Assert.cs:72:31:72:32 | "" | +| not null | Assert.cs:73:9:73:14 | access to type Assert | +| not null | Assert.cs:73:9:73:37 | call to method IsTrue | +| not null | Assert.cs:73:23:73:31 | ... == ... | +| not null | Assert.cs:73:23:73:36 | ... && ... | +| not null | Assert.cs:73:36:73:36 | access to parameter b | +| not null | Assert.cs:74:9:74:15 | access to type Console | +| not null | Assert.cs:74:9:74:35 | call to method WriteLine | +| not null | Assert.cs:74:27:74:34 | access to property Length | +| not null | Assert.cs:79:20:79:20 | access to parameter b | +| not null | Assert.cs:79:31:79:32 | "" | +| not null | Assert.cs:80:9:80:14 | access to type Assert | +| not null | Assert.cs:80:9:80:38 | call to method IsFalse | +| not null | Assert.cs:80:24:80:32 | ... != ... | +| not null | Assert.cs:80:24:80:37 | ... \|\| ... | +| not null | Assert.cs:80:37:80:37 | access to parameter b | +| not null | Assert.cs:81:9:81:15 | access to type Console | +| not null | Assert.cs:81:9:81:35 | call to method WriteLine | +| not null | Assert.cs:81:27:81:34 | access to property Length | +| not null | Assert.cs:85:61:85:65 | false | +| not null | Assert.cs:86:61:86:64 | true | +| not null | Assert.cs:93:9:93:35 | call to method AssertTrueFalse | +| not null | Assert.cs:93:9:93:35 | this access | +| not null | Assert.cs:93:25:93:26 | access to parameter b1 | +| not null | Assert.cs:93:29:93:30 | access to parameter b2 | +| not null | Assert.cs:93:33:93:34 | access to parameter b2 | +| not null | Assert.cs:94:16:94:17 | access to parameter b1 | +| not null | Assert.cs:94:16:94:24 | ... && ... | +| not null | Assert.cs:94:22:94:24 | !... | +| not null | Assert.cs:94:23:94:24 | access to parameter b2 | +| not null | Collections.cs:11:13:11:13 | access to local variable b | +| not null | Collections.cs:11:13:11:32 | Boolean b = ... | +| not null | Collections.cs:11:17:11:20 | access to parameter args | +| not null | Collections.cs:11:17:11:27 | access to property Length | +| not null | Collections.cs:11:17:11:32 | ... == ... | +| not null | Collections.cs:11:32:11:32 | 0 | +| not null | Collections.cs:12:9:12:9 | access to local variable b | +| not null | Collections.cs:12:9:12:28 | ... = ... | +| not null | Collections.cs:12:13:12:16 | access to parameter args | +| not null | Collections.cs:12:13:12:23 | access to property Length | +| not null | Collections.cs:12:13:12:28 | ... == ... | +| not null | Collections.cs:12:28:12:28 | 1 | +| not null | Collections.cs:13:9:13:9 | access to local variable b | +| not null | Collections.cs:13:9:13:28 | ... = ... | +| not null | Collections.cs:13:13:13:16 | access to parameter args | +| not null | Collections.cs:13:13:13:23 | access to property Length | +| not null | Collections.cs:13:13:13:28 | ... != ... | +| not null | Collections.cs:13:28:13:28 | 0 | +| not null | Collections.cs:14:9:14:9 | access to local variable b | +| not null | Collections.cs:14:9:14:28 | ... = ... | +| not null | Collections.cs:14:13:14:16 | access to parameter args | +| not null | Collections.cs:14:13:14:23 | access to property Length | +| not null | Collections.cs:14:13:14:28 | ... != ... | +| not null | Collections.cs:14:28:14:28 | 1 | +| not null | Collections.cs:15:9:15:9 | access to local variable b | +| not null | Collections.cs:15:9:15:27 | ... = ... | +| not null | Collections.cs:15:13:15:16 | access to parameter args | +| not null | Collections.cs:15:13:15:23 | access to property Length | +| not null | Collections.cs:15:13:15:27 | ... > ... | +| not null | Collections.cs:15:27:15:27 | 0 | +| not null | Collections.cs:16:9:16:9 | access to local variable b | +| not null | Collections.cs:16:9:16:28 | ... = ... | +| not null | Collections.cs:16:13:16:16 | access to parameter args | +| not null | Collections.cs:16:13:16:23 | access to property Length | +| not null | Collections.cs:16:13:16:28 | ... >= ... | +| not null | Collections.cs:16:28:16:28 | 0 | +| not null | Collections.cs:17:9:17:9 | access to local variable b | +| not null | Collections.cs:17:9:17:28 | ... = ... | +| not null | Collections.cs:17:13:17:16 | access to parameter args | +| not null | Collections.cs:17:13:17:23 | access to property Length | +| not null | Collections.cs:17:13:17:28 | ... >= ... | +| not null | Collections.cs:17:28:17:28 | 1 | +| not null | Collections.cs:22:13:22:13 | access to local variable b | +| not null | Collections.cs:22:13:22:31 | Boolean b = ... | +| not null | Collections.cs:22:17:22:20 | access to parameter args | +| not null | Collections.cs:22:17:22:26 | access to property Count | +| not null | Collections.cs:22:17:22:31 | ... == ... | +| not null | Collections.cs:22:31:22:31 | 0 | +| not null | Collections.cs:23:9:23:9 | access to local variable b | +| not null | Collections.cs:23:9:23:27 | ... = ... | +| not null | Collections.cs:23:13:23:16 | access to parameter args | +| not null | Collections.cs:23:13:23:22 | access to property Count | +| not null | Collections.cs:23:13:23:27 | ... == ... | +| not null | Collections.cs:23:27:23:27 | 1 | +| not null | Collections.cs:24:9:24:9 | access to local variable b | +| not null | Collections.cs:24:9:24:27 | ... = ... | +| not null | Collections.cs:24:13:24:16 | access to parameter args | +| not null | Collections.cs:24:13:24:22 | access to property Count | +| not null | Collections.cs:24:13:24:27 | ... != ... | +| not null | Collections.cs:24:27:24:27 | 0 | +| not null | Collections.cs:25:9:25:9 | access to local variable b | +| not null | Collections.cs:25:9:25:27 | ... = ... | +| not null | Collections.cs:25:13:25:16 | access to parameter args | +| not null | Collections.cs:25:13:25:22 | access to property Count | +| not null | Collections.cs:25:13:25:27 | ... != ... | +| not null | Collections.cs:25:27:25:27 | 1 | +| not null | Collections.cs:26:9:26:9 | access to local variable b | +| not null | Collections.cs:26:9:26:26 | ... = ... | +| not null | Collections.cs:26:13:26:16 | access to parameter args | +| not null | Collections.cs:26:13:26:22 | access to property Count | +| not null | Collections.cs:26:13:26:26 | ... > ... | +| not null | Collections.cs:26:26:26:26 | 0 | +| not null | Collections.cs:27:9:27:9 | access to local variable b | +| not null | Collections.cs:27:9:27:27 | ... = ... | +| not null | Collections.cs:27:13:27:16 | access to parameter args | +| not null | Collections.cs:27:13:27:22 | access to property Count | +| not null | Collections.cs:27:13:27:27 | ... >= ... | +| not null | Collections.cs:27:27:27:27 | 0 | +| not null | Collections.cs:28:9:28:9 | access to local variable b | +| not null | Collections.cs:28:9:28:27 | ... = ... | +| not null | Collections.cs:28:13:28:16 | access to parameter args | +| not null | Collections.cs:28:13:28:22 | access to property Count | +| not null | Collections.cs:28:13:28:27 | ... >= ... | +| not null | Collections.cs:28:27:28:27 | 1 | +| not null | Collections.cs:33:13:33:13 | access to local variable b | +| not null | Collections.cs:33:13:33:33 | Boolean b = ... | +| not null | Collections.cs:33:17:33:20 | access to parameter args | +| not null | Collections.cs:33:17:33:28 | call to method Count | +| not null | Collections.cs:33:17:33:33 | ... == ... | +| not null | Collections.cs:33:33:33:33 | 0 | +| not null | Collections.cs:34:9:34:9 | access to local variable b | +| not null | Collections.cs:34:9:34:29 | ... = ... | +| not null | Collections.cs:34:13:34:16 | access to parameter args | +| not null | Collections.cs:34:13:34:24 | call to method Count | +| not null | Collections.cs:34:13:34:29 | ... == ... | +| not null | Collections.cs:34:29:34:29 | 1 | +| not null | Collections.cs:35:9:35:9 | access to local variable b | +| not null | Collections.cs:35:9:35:29 | ... = ... | +| not null | Collections.cs:35:13:35:16 | access to parameter args | +| not null | Collections.cs:35:13:35:24 | call to method Count | +| not null | Collections.cs:35:13:35:29 | ... != ... | +| not null | Collections.cs:35:29:35:29 | 0 | +| not null | Collections.cs:36:9:36:9 | access to local variable b | +| not null | Collections.cs:36:9:36:29 | ... = ... | +| not null | Collections.cs:36:13:36:16 | access to parameter args | +| not null | Collections.cs:36:13:36:24 | call to method Count | +| not null | Collections.cs:36:13:36:29 | ... != ... | +| not null | Collections.cs:36:29:36:29 | 1 | +| not null | Collections.cs:37:9:37:9 | access to local variable b | +| not null | Collections.cs:37:9:37:28 | ... = ... | +| not null | Collections.cs:37:13:37:16 | access to parameter args | +| not null | Collections.cs:37:13:37:24 | call to method Count | +| not null | Collections.cs:37:13:37:28 | ... > ... | +| not null | Collections.cs:37:28:37:28 | 0 | +| not null | Collections.cs:38:9:38:9 | access to local variable b | +| not null | Collections.cs:38:9:38:29 | ... = ... | +| not null | Collections.cs:38:13:38:16 | access to parameter args | +| not null | Collections.cs:38:13:38:24 | call to method Count | +| not null | Collections.cs:38:13:38:29 | ... >= ... | +| not null | Collections.cs:38:29:38:29 | 0 | +| not null | Collections.cs:39:9:39:9 | access to local variable b | +| not null | Collections.cs:39:9:39:29 | ... = ... | +| not null | Collections.cs:39:13:39:16 | access to parameter args | +| not null | Collections.cs:39:13:39:24 | call to method Count | +| not null | Collections.cs:39:13:39:29 | ... >= ... | +| not null | Collections.cs:39:29:39:29 | 1 | +| not null | Collections.cs:44:13:44:13 | access to local variable b | +| not null | Collections.cs:44:13:44:26 | Boolean b = ... | +| not null | Collections.cs:44:17:44:20 | access to parameter args | +| not null | Collections.cs:44:17:44:26 | call to method Any | +| not null | Collections.cs:49:13:49:16 | access to parameter args | +| not null | Collections.cs:49:13:49:22 | access to property Count | +| not null | Collections.cs:49:13:49:27 | ... == ... | +| not null | Collections.cs:49:27:49:27 | 0 | +| not null | Collections.cs:51:17:51:20 | access to parameter args | +| not null | Collections.cs:51:17:51:30 | call to method ToArray | +| not null | Collections.cs:52:9:52:12 | access to parameter args | +| not null | Collections.cs:52:9:52:20 | call to method Clear | +| not null | Collections.cs:53:9:53:9 | access to local variable x | +| not null | Collections.cs:53:9:53:26 | ... = ... | +| not null | Collections.cs:53:13:53:16 | access to parameter args | +| not null | Collections.cs:53:13:53:26 | call to method ToArray | +| not null | Collections.cs:54:9:54:9 | access to local variable x | +| not null | Collections.cs:54:9:54:42 | ... = ... | +| not null | Collections.cs:54:13:54:42 | 3 | +| not null | Collections.cs:54:13:54:42 | array creation of type String[] | +| not null | Collections.cs:54:28:54:30 | "a" | +| not null | Collections.cs:54:33:54:35 | "b" | +| not null | Collections.cs:54:38:54:40 | "c" | +| not null | Collections.cs:55:9:55:9 | access to local variable x | +| not null | Collections.cs:55:9:55:13 | ... = ... | +| not null | Collections.cs:55:13:55:13 | access to local variable x | +| not null | Collections.cs:56:9:56:9 | access to local variable x | +| not null | Collections.cs:56:9:56:25 | ... = ... | +| not null | Collections.cs:56:13:56:25 | array creation of type String[] | +| not null | Collections.cs:56:24:56:24 | 0 | +| not null | Collections.cs:57:9:57:9 | access to local variable x | +| not null | Collections.cs:57:9:57:13 | ... = ... | +| not null | Collections.cs:57:13:57:13 | access to local variable x | +| not null | Collections.cs:62:17:62:46 | 3 | +| not null | Collections.cs:62:17:62:46 | array creation of type String[] | +| not null | Collections.cs:62:17:62:55 | call to method ToList | +| not null | Collections.cs:62:32:62:34 | "a" | +| not null | Collections.cs:62:37:62:39 | "b" | +| not null | Collections.cs:62:42:62:44 | "c" | +| not null | Collections.cs:63:9:63:9 | access to local variable x | +| not null | Collections.cs:63:9:63:17 | call to method Clear | +| not null | Collections.cs:64:13:64:13 | access to local variable x | +| not null | Collections.cs:64:13:64:19 | access to property Count | +| not null | Collections.cs:64:13:64:24 | ... == ... | +| not null | Collections.cs:64:24:64:24 | 0 | +| not null | Collections.cs:66:13:66:13 | access to local variable x | +| not null | Collections.cs:66:13:66:22 | call to method Add | +| not null | Collections.cs:66:19:66:21 | "a" | +| not null | Collections.cs:67:13:67:13 | access to local variable x | +| not null | Collections.cs:67:13:67:22 | call to method Add | +| not null | Collections.cs:67:19:67:21 | "b" | +| not null | Collections.cs:73:35:73:35 | access to parameter s | +| not null | Collections.cs:73:35:73:41 | ... == ... | +| not null | Collections.cs:73:40:73:41 | "" | +| not null | Collections.cs:74:13:74:13 | access to local variable b | +| not null | Collections.cs:74:13:74:33 | Boolean b = ... | +| not null | Collections.cs:74:17:74:20 | access to parameter args | +| not null | Collections.cs:74:17:74:33 | call to method Any | +| not null | Collections.cs:74:26:74:32 | access to local function IsEmpty | +| not null | Collections.cs:74:26:74:32 | delegate creation of type Func | +| not null | Collections.cs:74:26:74:32 | this access | +| not null | Collections.cs:75:9:75:9 | access to local variable b | +| not null | Collections.cs:75:9:75:36 | ... = ... | +| not null | Collections.cs:75:13:75:16 | access to parameter args | +| not null | Collections.cs:75:13:75:31 | call to method Count | +| not null | Collections.cs:75:13:75:36 | ... == ... | +| not null | Collections.cs:75:24:75:30 | access to local function IsEmpty | +| not null | Collections.cs:75:24:75:30 | delegate creation of type Func | +| not null | Collections.cs:75:24:75:30 | this access | +| not null | Collections.cs:75:36:75:36 | 0 | +| not null | Collections.cs:76:9:76:9 | access to local variable b | +| not null | Collections.cs:76:9:76:36 | ... = ... | +| not null | Collections.cs:76:13:76:16 | access to parameter args | +| not null | Collections.cs:76:13:76:31 | call to method Count | +| not null | Collections.cs:76:13:76:36 | ... == ... | +| not null | Collections.cs:76:24:76:30 | access to local function IsEmpty | +| not null | Collections.cs:76:24:76:30 | delegate creation of type Func | +| not null | Collections.cs:76:24:76:30 | this access | +| not null | Collections.cs:76:36:76:36 | 1 | +| not null | Collections.cs:77:9:77:9 | access to local variable b | +| not null | Collections.cs:77:9:77:36 | ... = ... | +| not null | Collections.cs:77:13:77:16 | access to parameter args | +| not null | Collections.cs:77:13:77:31 | call to method Count | +| not null | Collections.cs:77:13:77:36 | ... != ... | +| not null | Collections.cs:77:24:77:30 | access to local function IsEmpty | +| not null | Collections.cs:77:24:77:30 | delegate creation of type Func | +| not null | Collections.cs:77:24:77:30 | this access | +| not null | Collections.cs:77:36:77:36 | 0 | +| not null | Collections.cs:78:9:78:9 | access to local variable b | +| not null | Collections.cs:78:9:78:36 | ... = ... | +| not null | Collections.cs:78:13:78:16 | access to parameter args | +| not null | Collections.cs:78:13:78:31 | call to method Count | +| not null | Collections.cs:78:13:78:36 | ... != ... | +| not null | Collections.cs:78:24:78:30 | access to local function IsEmpty | +| not null | Collections.cs:78:24:78:30 | delegate creation of type Func | +| not null | Collections.cs:78:24:78:30 | this access | +| not null | Collections.cs:78:36:78:36 | 1 | +| not null | Collections.cs:79:9:79:9 | access to local variable b | +| not null | Collections.cs:79:9:79:35 | ... = ... | +| not null | Collections.cs:79:13:79:16 | access to parameter args | +| not null | Collections.cs:79:13:79:31 | call to method Count | +| not null | Collections.cs:79:13:79:35 | ... > ... | +| not null | Collections.cs:79:24:79:30 | access to local function IsEmpty | +| not null | Collections.cs:79:24:79:30 | delegate creation of type Func | +| not null | Collections.cs:79:24:79:30 | this access | +| not null | Collections.cs:79:35:79:35 | 0 | +| not null | Collections.cs:80:9:80:9 | access to local variable b | +| not null | Collections.cs:80:9:80:36 | ... = ... | +| not null | Collections.cs:80:13:80:16 | access to parameter args | +| not null | Collections.cs:80:13:80:31 | call to method Count | +| not null | Collections.cs:80:13:80:36 | ... >= ... | +| not null | Collections.cs:80:24:80:30 | access to local function IsEmpty | +| not null | Collections.cs:80:24:80:30 | delegate creation of type Func | +| not null | Collections.cs:80:24:80:30 | this access | +| not null | Collections.cs:80:36:80:36 | 0 | +| not null | Collections.cs:81:9:81:9 | access to local variable b | +| not null | Collections.cs:81:9:81:36 | ... = ... | +| not null | Collections.cs:81:13:81:16 | access to parameter args | +| not null | Collections.cs:81:13:81:31 | call to method Count | +| not null | Collections.cs:81:13:81:36 | ... >= ... | +| not null | Collections.cs:81:24:81:30 | access to local function IsEmpty | +| not null | Collections.cs:81:24:81:30 | delegate creation of type Func | +| not null | Collections.cs:81:24:81:30 | this access | +| not null | Collections.cs:81:36:81:36 | 1 | +| not null | Collections.cs:86:17:86:32 | 0 | +| not null | Collections.cs:86:17:86:32 | array creation of type String[] | +| not null | Collections.cs:87:22:87:24 | array creation of type String[] | +| not null | Collections.cs:88:9:88:9 | access to local variable x | +| not null | Collections.cs:88:9:88:32 | ... = ... | +| not null | Collections.cs:88:13:88:32 | 1 | +| not null | Collections.cs:88:13:88:32 | array creation of type String[] | +| not null | Collections.cs:88:28:88:30 | "a" | +| not null | Collections.cs:89:22:89:28 | array creation of type String[] | +| not null | Collections.cs:89:24:89:26 | "a" | +| not null | Collections.cs:94:29:94:32 | access to parameter args | +| not null | Collections.cs:95:13:95:19 | access to type Console | +| not null | Collections.cs:95:13:95:35 | call to method WriteLine | +| not null | Collections.cs:95:31:95:34 | access to parameter args | +| not null | Collections.cs:100:29:100:32 | access to parameter args | +| not null | Collections.cs:102:9:102:15 | access to type Console | +| not null | Collections.cs:102:9:102:31 | call to method WriteLine | +| not null | Collections.cs:102:27:102:30 | access to parameter args | +| not null | Guards.cs:10:13:10:25 | !... | +| not null | Guards.cs:10:14:10:25 | !... | +| not null | Guards.cs:10:16:10:24 | ... == ... | +| not null | Guards.cs:12:13:12:13 | access to parameter s | +| not null | Guards.cs:12:13:12:20 | access to property Length | +| not null | Guards.cs:12:13:12:24 | ... > ... | +| not null | Guards.cs:12:24:12:24 | 0 | +| not null | Guards.cs:14:13:14:19 | access to type Console | +| not null | Guards.cs:14:13:14:32 | call to method WriteLine | +| not null | Guards.cs:14:31:14:31 | access to parameter s | +| not null | Guards.cs:18:13:18:19 | access to type Console | +| not null | Guards.cs:18:13:18:47 | call to method WriteLine | +| not null | Guards.cs:18:31:18:46 | "" | +| not null | Guards.cs:24:13:24:21 | ... != ... | +| not null | Guards.cs:26:13:26:19 | access to type Console | +| not null | Guards.cs:26:13:26:32 | call to method WriteLine | +| not null | Guards.cs:26:31:26:31 | access to parameter s | +| not null | Guards.cs:32:13:32:36 | !... | +| not null | Guards.cs:32:13:32:51 | ... & ... | +| not null | Guards.cs:32:14:32:19 | access to type String | +| not null | Guards.cs:32:14:32:36 | call to method IsNullOrEmpty | +| not null | Guards.cs:32:40:32:51 | !... | +| not null | Guards.cs:32:42:32:50 | ... == ... | +| not null | Guards.cs:33:13:33:19 | access to type Console | +| not null | Guards.cs:33:13:33:36 | call to method WriteLine | +| not null | Guards.cs:33:31:33:31 | access to parameter x | +| not null | Guards.cs:33:31:33:35 | ... + ... | +| not null | Guards.cs:33:35:33:35 | access to parameter y | +| not null | Guards.cs:35:13:35:21 | ... == ... | +| not null | Guards.cs:35:13:35:34 | ... \|\| ... | +| not null | Guards.cs:35:26:35:34 | ... == ... | +| not null | Guards.cs:36:14:36:20 | access to type Console | +| not null | Guards.cs:36:14:36:37 | call to method WriteLine | +| not null | Guards.cs:36:32:36:32 | access to parameter x | +| not null | Guards.cs:36:32:36:36 | ... + ... | +| not null | Guards.cs:36:36:36:36 | access to parameter y | +| not null | Guards.cs:38:13:38:37 | !... | +| not null | Guards.cs:38:15:38:23 | ... == ... | +| not null | Guards.cs:38:15:38:36 | ... \|\| ... | +| not null | Guards.cs:38:28:38:36 | ... == ... | +| not null | Guards.cs:39:13:39:19 | access to type Console | +| not null | Guards.cs:39:13:39:36 | call to method WriteLine | +| not null | Guards.cs:39:31:39:31 | access to parameter x | +| not null | Guards.cs:39:31:39:35 | ... + ... | +| not null | Guards.cs:39:35:39:35 | access to parameter y | +| not null | Guards.cs:41:13:41:39 | !... | +| not null | Guards.cs:41:14:41:39 | !... | +| not null | Guards.cs:41:15:41:39 | !... | +| not null | Guards.cs:41:17:41:25 | ... != ... | +| not null | Guards.cs:41:17:41:38 | ... && ... | +| not null | Guards.cs:41:30:41:38 | ... != ... | +| not null | Guards.cs:42:14:42:20 | access to type Console | +| not null | Guards.cs:42:14:42:37 | call to method WriteLine | +| not null | Guards.cs:42:32:42:32 | access to parameter x | +| not null | Guards.cs:42:32:42:36 | ... + ... | +| not null | Guards.cs:42:36:42:36 | access to parameter y | +| not null | Guards.cs:44:13:44:17 | this access | +| not null | Guards.cs:44:13:44:25 | ... != ... | +| not null | Guards.cs:45:13:45:19 | access to type Console | +| not null | Guards.cs:45:13:45:49 | call to method WriteLine | +| not null | Guards.cs:45:31:45:42 | object creation of type Guards | +| not null | Guards.cs:47:13:47:17 | this access | +| not null | Guards.cs:47:13:47:25 | ... != ... | +| not null | Guards.cs:48:13:48:19 | access to type Console | +| not null | Guards.cs:48:13:48:41 | call to method WriteLine | +| not null | Guards.cs:48:31:48:34 | this access | +| not null | Guards.cs:48:31:48:40 | access to field Field | +| not null | Guards.cs:53:13:53:13 | access to parameter g | +| not null | Guards.cs:53:13:53:27 | ... == ... | +| not null | Guards.cs:55:9:55:15 | access to type Console | +| not null | Guards.cs:55:9:55:34 | call to method WriteLine | +| not null | Guards.cs:55:27:55:27 | access to parameter g | +| not null | Guards.cs:55:27:55:33 | access to field Field | +| not null | Guards.cs:60:13:60:13 | access to parameter g | +| not null | Guards.cs:60:13:60:45 | ... == ... | +| not null | Guards.cs:61:19:61:33 | object creation of type Exception | +| not null | Guards.cs:62:9:62:15 | access to type Console | +| not null | Guards.cs:62:9:62:52 | call to method WriteLine | +| not null | Guards.cs:62:27:62:27 | access to parameter g | +| not null | Guards.cs:62:27:62:36 | access to property Property | +| not null | Guards.cs:62:27:62:45 | access to property Property | +| not null | Guards.cs:62:27:62:51 | access to field Field | +| not null | Guards.cs:63:9:63:15 | access to type Console | +| not null | Guards.cs:63:9:63:43 | call to method WriteLine | +| not null | Guards.cs:63:27:63:27 | access to parameter g | +| not null | Guards.cs:63:27:63:36 | access to property Property | +| not null | Guards.cs:68:16:68:24 | ... != ... | +| not null | Guards.cs:70:13:70:19 | access to type Console | +| not null | Guards.cs:70:13:70:32 | call to method WriteLine | +| not null | Guards.cs:70:31:70:31 | access to parameter s | +| not null | Guards.cs:71:13:71:13 | access to parameter s | +| not null | Guards.cs:72:13:72:19 | access to type Console | +| not null | Guards.cs:72:13:72:32 | call to method WriteLine | +| not null | Guards.cs:78:13:78:26 | ... == ... | +| not null | Guards.cs:78:26:78:26 | 0 | +| not null | Guards.cs:78:26:78:26 | (...) ... | +| not null | Guards.cs:79:13:79:19 | access to type Console | +| not null | Guards.cs:79:13:79:32 | call to method WriteLine | +| not null | Guards.cs:79:31:79:31 | access to parameter s | +| not null | Guards.cs:80:13:80:25 | ... > ... | +| not null | Guards.cs:80:25:80:25 | 0 | +| not null | Guards.cs:80:25:80:25 | (...) ... | +| not null | Guards.cs:81:13:81:19 | access to type Console | +| not null | Guards.cs:81:13:81:32 | call to method WriteLine | +| not null | Guards.cs:81:31:81:31 | access to parameter s | +| not null | Guards.cs:82:13:82:26 | ... >= ... | +| not null | Guards.cs:82:26:82:26 | 0 | +| not null | Guards.cs:82:26:82:26 | (...) ... | +| not null | Guards.cs:83:13:83:19 | access to type Console | +| not null | Guards.cs:83:13:83:32 | call to method WriteLine | +| not null | Guards.cs:83:31:83:31 | access to parameter s | +| not null | Guards.cs:84:13:84:26 | ... < ... | +| not null | Guards.cs:84:25:84:26 | 10 | +| not null | Guards.cs:84:25:84:26 | (...) ... | +| not null | Guards.cs:85:13:85:19 | access to type Console | +| not null | Guards.cs:85:13:85:32 | call to method WriteLine | +| not null | Guards.cs:85:31:85:31 | access to parameter s | +| not null | Guards.cs:86:13:86:27 | ... <= ... | +| not null | Guards.cs:86:26:86:27 | 10 | +| not null | Guards.cs:86:26:86:27 | (...) ... | +| not null | Guards.cs:87:13:87:19 | access to type Console | +| not null | Guards.cs:87:13:87:32 | call to method WriteLine | +| not null | Guards.cs:87:31:87:31 | access to parameter s | +| not null | Guards.cs:88:13:88:29 | ... != ... | +| not null | Guards.cs:89:13:89:19 | access to type Console | +| not null | Guards.cs:89:13:89:32 | call to method WriteLine | +| not null | Guards.cs:89:31:89:31 | access to parameter s | +| not null | Guards.cs:91:13:91:19 | access to type Console | +| not null | Guards.cs:91:13:91:32 | call to method WriteLine | +| not null | Guards.cs:92:13:92:30 | ... != ... | +| not null | Guards.cs:92:25:92:25 | 1 | +| not null | Guards.cs:92:25:92:25 | (...) ... | +| not null | Guards.cs:92:30:92:30 | 0 | +| not null | Guards.cs:92:30:92:30 | (...) ... | +| not null | Guards.cs:93:13:93:19 | access to type Console | +| not null | Guards.cs:93:13:93:32 | call to method WriteLine | +| not null | Guards.cs:95:13:95:19 | access to type Console | +| not null | Guards.cs:95:13:95:32 | call to method WriteLine | +| not null | Guards.cs:96:13:96:19 | ... == ... | +| not null | Guards.cs:96:18:96:19 | "" | +| not null | Guards.cs:97:13:97:19 | access to type Console | +| not null | Guards.cs:97:13:97:32 | call to method WriteLine | +| not null | Guards.cs:97:31:97:31 | access to parameter s | +| not null | Guards.cs:99:13:99:19 | access to type Console | +| not null | Guards.cs:99:13:99:32 | call to method WriteLine | +| not null | Guards.cs:104:13:104:13 | access to parameter g | +| not null | Guards.cs:104:13:104:45 | ... == ... | +| not null | Guards.cs:105:19:105:33 | object creation of type Exception | +| not null | Guards.cs:106:9:106:9 | access to parameter g | +| not null | Guards.cs:106:9:106:18 | access to property Property | +| not null | Guards.cs:107:9:107:15 | access to type Console | +| not null | Guards.cs:107:9:107:52 | call to method WriteLine | +| not null | Guards.cs:107:27:107:27 | access to parameter g | +| not null | Guards.cs:108:9:108:15 | access to type Console | +| not null | Guards.cs:108:9:108:43 | call to method WriteLine | +| not null | Guards.cs:108:27:108:27 | access to parameter g | +| not null | Guards.cs:108:27:108:36 | access to property Property | +| not null | Guards.cs:113:21:113:21 | access to parameter g | +| not null | Guards.cs:114:14:114:14 | access to parameter g | +| not null | Guards.cs:114:14:114:23 | access to property Property | +| not null | Guards.cs:114:14:114:32 | access to property Property | +| not null | Guards.cs:115:9:115:55 | ... = ... | +| not null | Guards.cs:115:17:115:17 | access to parameter g | +| not null | Guards.cs:115:17:115:26 | access to property Property | +| not null | Guards.cs:115:17:115:35 | access to property Property | +| not null | Guards.cs:115:17:115:55 | ... ?? ... | +| not null | Guards.cs:115:46:115:55 | throw ... | +| not null | Guards.cs:116:9:116:15 | access to type Console | +| not null | Guards.cs:116:9:116:52 | call to method WriteLine | +| not null | Guards.cs:116:27:116:27 | access to parameter g | +| not null | Guards.cs:116:27:116:36 | access to property Property | +| not null | Guards.cs:116:27:116:45 | access to property Property | +| not null | Guards.cs:116:27:116:51 | access to field Field | +| not null | Guards.cs:117:9:117:9 | access to parameter g | +| not null | Guards.cs:117:9:117:18 | access to property Property | +| not null | Guards.cs:118:9:118:15 | access to type Console | +| not null | Guards.cs:118:9:118:52 | call to method WriteLine | +| not null | Guards.cs:118:27:118:27 | access to parameter g | +| not null | Guards.cs:119:9:119:15 | access to type Console | +| not null | Guards.cs:119:9:119:43 | call to method WriteLine | +| not null | Guards.cs:119:27:119:27 | access to parameter g | +| not null | Guards.cs:119:27:119:36 | access to property Property | +| not null | Guards.cs:124:13:124:14 | access to local variable b1 | +| not null | Guards.cs:124:13:124:30 | Boolean b1 = ... | +| not null | Guards.cs:124:18:124:30 | call to method Equals | +| not null | Guards.cs:125:18:125:19 | access to parameter s1 | +| not null | Guards.cs:125:29:125:30 | access to parameter s1 | +| not null | Guards.cs:130:13:130:21 | ... is ... | +| not null | Guards.cs:131:20:131:27 | access to property Length | +| not null | Guards.cs:132:16:132:16 | access to parameter s | +| not null | Guards.cs:132:16:132:23 | access to property Length | +| not null | Guards.cs:137:13:137:25 | ... is ... | +| not null | Guards.cs:137:18:137:23 | access to type String | +| not null | Guards.cs:138:20:138:20 | access to parameter s | +| not null | Guards.cs:138:20:138:27 | access to property Length | +| not null | Guards.cs:139:16:139:23 | access to property Length | +| not null | Guards.cs:144:13:144:25 | ... is ... | +| not null | Guards.cs:145:20:145:20 | access to local variable s | +| not null | Guards.cs:153:18:153:31 | access to type Action | +| not null | Guards.cs:154:24:154:24 | access to parameter o | +| not null | Guards.cs:156:24:156:24 | access to local variable a | +| not null | Guards.cs:157:18:157:19 | "" | +| not null | Guards.cs:158:24:158:24 | access to parameter o | +| not null | Guards.cs:159:18:159:21 | null | +| not null | Guards.cs:162:24:162:24 | access to parameter o | +| not null | Guards.cs:168:13:168:41 | !... | +| not null | Guards.cs:168:14:168:19 | access to type String | +| not null | Guards.cs:168:14:168:41 | call to method IsNullOrWhiteSpace | +| not null | Guards.cs:169:13:169:19 | access to type Console | +| not null | Guards.cs:169:13:169:32 | call to method WriteLine | +| not null | Guards.cs:169:31:169:31 | access to parameter x | +| not null | Guards.cs:172:34:172:42 | ... == ... | +| not null | Guards.cs:176:13:176:21 | ... is ... | +| not null | Guards.cs:177:20:177:23 | true | +| not null | Guards.cs:178:16:178:20 | false | +| not null | Guards.cs:181:34:181:42 | ... == ... | +| not null | Guards.cs:181:34:181:57 | ... ? ... : ... | +| not null | Guards.cs:181:46:181:49 | true | +| not null | Guards.cs:181:53:181:57 | false | +| not null | Guards.cs:183:37:183:49 | !... | +| not null | Guards.cs:183:38:183:49 | call to method NullTest3 | +| not null | Guards.cs:183:38:183:49 | this access | +| not null | Guards.cs:185:38:185:46 | ... == ... | +| not null | Guards.cs:185:38:185:60 | ... ? ... : ... | +| not null | Guards.cs:185:50:185:53 | true | +| not null | Guards.cs:185:57:185:60 | true | +| not null | Guards.cs:189:13:189:25 | !... | +| not null | Guards.cs:189:14:189:25 | call to method NullTest1 | +| not null | Guards.cs:189:14:189:25 | this access | +| not null | Guards.cs:190:13:190:19 | access to type Console | +| not null | Guards.cs:190:13:190:32 | call to method WriteLine | +| not null | Guards.cs:191:13:191:25 | !... | +| not null | Guards.cs:191:14:191:25 | call to method NullTest2 | +| not null | Guards.cs:191:14:191:25 | this access | +| not null | Guards.cs:192:13:192:19 | access to type Console | +| not null | Guards.cs:192:13:192:32 | call to method WriteLine | +| not null | Guards.cs:193:13:193:25 | !... | +| not null | Guards.cs:193:14:193:25 | call to method NullTest3 | +| not null | Guards.cs:193:14:193:25 | this access | +| not null | Guards.cs:194:13:194:19 | access to type Console | +| not null | Guards.cs:194:13:194:32 | call to method WriteLine | +| not null | Guards.cs:195:13:195:27 | call to method NotNullTest4 | +| not null | Guards.cs:195:13:195:27 | this access | +| not null | Guards.cs:196:13:196:19 | access to type Console | +| not null | Guards.cs:196:13:196:32 | call to method WriteLine | +| not null | Guards.cs:197:13:197:29 | !... | +| not null | Guards.cs:197:14:197:29 | call to method NullTestWrong | +| not null | Guards.cs:197:14:197:29 | this access | +| not null | Guards.cs:198:13:198:19 | access to type Console | +| not null | Guards.cs:198:13:198:32 | call to method WriteLine | +| not null | Guards.cs:203:13:203:21 | ... != ... | +| not null | Guards.cs:205:13:205:13 | access to parameter o | +| not null | Guards.cs:206:33:206:36 | access to parameter args | +| not null | Guards.cs:208:17:208:17 | access to parameter o | +| not null | Guards.cs:215:13:215:14 | access to local variable b2 | +| not null | Guards.cs:215:13:215:21 | Boolean b2 = ... | +| not null | Guards.cs:215:18:215:21 | true | +| not null | Guards.cs:216:13:216:14 | access to parameter b1 | +| not null | Guards.cs:217:13:217:14 | access to local variable b2 | +| not null | Guards.cs:217:13:217:22 | ... = ... | +| not null | Guards.cs:217:18:217:22 | false | +| not null | Guards.cs:218:17:218:18 | access to local variable b2 | +| not null | Guards.cs:220:18:220:21 | true | +| not null | Guards.cs:228:13:228:14 | access to local variable b2 | +| not null | Guards.cs:228:13:228:22 | Boolean b2 = ... | +| not null | Guards.cs:228:18:228:22 | false | +| not null | Guards.cs:229:13:229:14 | access to parameter b1 | +| not null | Guards.cs:230:13:230:14 | access to local variable b2 | +| not null | Guards.cs:230:13:230:21 | ... = ... | +| not null | Guards.cs:230:18:230:21 | true | +| not null | Guards.cs:231:17:231:18 | access to local variable b2 | +| not null | Guards.cs:233:18:233:21 | true | +| not null | Guards.cs:241:13:241:13 | access to local variable i | +| not null | Guards.cs:241:13:241:17 | Int32 i = ... | +| not null | Guards.cs:241:17:241:17 | 0 | +| not null | Guards.cs:242:13:242:13 | access to parameter b | +| not null | Guards.cs:243:13:243:13 | access to local variable i | +| not null | Guards.cs:243:13:243:17 | ... = ... | +| not null | Guards.cs:243:17:243:17 | 1 | +| not null | Guards.cs:244:17:244:17 | access to local variable i | +| not null | Guards.cs:246:18:246:18 | 1 | +| not null | Guards.cs:255:13:255:13 | access to local variable e | +| not null | Guards.cs:255:13:255:19 | E e = ... | +| not null | Guards.cs:255:17:255:17 | access to type E | +| not null | Guards.cs:255:17:255:19 | access to constant A | +| not null | Guards.cs:256:13:256:13 | access to parameter b | +| not null | Guards.cs:257:13:257:13 | access to local variable e | +| not null | Guards.cs:257:13:257:19 | ... = ... | +| not null | Guards.cs:257:17:257:17 | access to type E | +| not null | Guards.cs:257:17:257:19 | access to constant B | +| not null | Guards.cs:258:17:258:17 | access to local variable e | +| not null | Guards.cs:260:18:260:18 | access to type E | +| not null | Guards.cs:260:18:260:20 | access to constant B | +| not null | Guards.cs:268:13:268:41 | call to operator == | +| not null | Guards.cs:268:30:268:41 | call to method GetType | +| not null | Guards.cs:269:13:269:14 | access to parameter o1 | +| not null | Guards.cs:270:13:270:42 | call to operator == | +| not null | Guards.cs:270:30:270:31 | access to parameter o2 | +| not null | Guards.cs:278:13:278:26 | access to type Action | +| not null | Guards.cs:279:17:279:17 | access to parameter o | +| not null | Guards.cs:281:17:281:17 | access to local variable a | +| not null | Guards.cs:282:13:282:14 | "" | +| not null | Guards.cs:283:17:283:17 | access to parameter o | +| not null | Guards.cs:287:17:287:17 | access to parameter o | +| not null | Guards.cs:293:13:293:14 | access to local variable b2 | +| not null | Guards.cs:293:13:293:21 | Boolean b2 = ... | +| not null | Guards.cs:293:18:293:21 | true | +| not null | Guards.cs:294:13:294:14 | access to parameter b1 | +| not null | Guards.cs:295:13:295:14 | access to local variable b2 | +| not null | Guards.cs:295:13:295:22 | ... = ... | +| not null | Guards.cs:295:18:295:22 | false | +| not null | Guards.cs:296:16:296:17 | access to local variable b2 | +| not null | Guards.cs:296:16:300:9 | ... switch { ... } | +| not null | Guards.cs:298:13:298:16 | true | +| not null | Guards.cs:298:13:298:21 | ... => ... | +| not null | Guards.cs:298:21:298:21 | 0 | +| not null | Guards.cs:299:13:299:13 | _ | +| not null | Guards.cs:299:13:299:18 | ... => ... | +| not null | Guards.cs:299:18:299:18 | 1 | +| not null | Guards.cs:305:13:305:14 | access to local variable b2 | +| not null | Guards.cs:305:13:305:22 | Boolean b2 = ... | +| not null | Guards.cs:305:18:305:22 | false | +| not null | Guards.cs:306:13:306:14 | access to parameter b1 | +| not null | Guards.cs:307:13:307:14 | access to local variable b2 | +| not null | Guards.cs:307:13:307:21 | ... = ... | +| not null | Guards.cs:307:18:307:21 | true | +| not null | Guards.cs:308:16:308:17 | access to local variable b2 | +| not null | Guards.cs:308:16:312:9 | ... switch { ... } | +| not null | Guards.cs:310:13:310:16 | true | +| not null | Guards.cs:310:13:310:21 | ... => ... | +| not null | Guards.cs:310:21:310:21 | 0 | +| not null | Guards.cs:311:13:311:13 | _ | +| not null | Guards.cs:311:13:311:18 | ... => ... | +| not null | Guards.cs:311:18:311:18 | 1 | +| not null | Guards.cs:317:13:317:13 | access to local variable i | +| not null | Guards.cs:317:13:317:17 | Int32 i = ... | +| not null | Guards.cs:317:17:317:17 | 0 | +| not null | Guards.cs:318:13:318:13 | access to parameter b | +| not null | Guards.cs:319:13:319:13 | access to local variable i | +| not null | Guards.cs:319:13:319:17 | ... = ... | +| not null | Guards.cs:319:17:319:17 | 1 | +| not null | Guards.cs:320:16:320:16 | access to local variable i | +| not null | Guards.cs:320:16:324:9 | ... switch { ... } | +| not null | Guards.cs:322:13:322:13 | 1 | +| not null | Guards.cs:322:13:322:18 | ... => ... | +| not null | Guards.cs:322:18:322:18 | 0 | +| not null | Guards.cs:323:13:323:13 | _ | +| not null | Guards.cs:323:13:323:18 | ... => ... | +| not null | Guards.cs:323:18:323:18 | 1 | +| not null | Guards.cs:329:13:329:13 | access to local variable e | +| not null | Guards.cs:329:13:329:19 | E e = ... | +| not null | Guards.cs:329:17:329:17 | access to type E | +| not null | Guards.cs:329:17:329:19 | access to constant A | +| not null | Guards.cs:330:13:330:13 | access to parameter b | +| not null | Guards.cs:331:13:331:13 | access to local variable e | +| not null | Guards.cs:331:13:331:19 | ... = ... | +| not null | Guards.cs:331:17:331:17 | access to type E | +| not null | Guards.cs:331:17:331:19 | access to constant B | +| not null | Guards.cs:332:16:332:16 | access to local variable e | +| not null | Guards.cs:332:16:336:9 | ... switch { ... } | +| not null | Guards.cs:334:13:334:13 | access to type E | +| not null | Guards.cs:334:13:334:15 | access to constant B | +| not null | Guards.cs:334:13:334:20 | ... => ... | +| not null | Guards.cs:334:20:334:20 | 0 | +| not null | Guards.cs:335:13:335:13 | _ | +| not null | Guards.cs:335:13:335:18 | ... => ... | +| not null | Guards.cs:335:18:335:18 | 1 | +| not null | Guards.cs:341:20:341:20 | access to parameter b | +| not null | Guards.cs:341:31:341:32 | "" | +| not null | Guards.cs:342:13:342:21 | ... != ... | +| not null | Guards.cs:342:13:342:27 | ... && ... | +| not null | Guards.cs:342:26:342:27 | !... | +| not null | Guards.cs:342:27:342:27 | access to parameter b | +| not null | Guards.cs:343:13:343:19 | access to type Console | +| not null | Guards.cs:343:13:343:39 | call to method WriteLine | +| not null | Guards.cs:343:31:343:31 | access to local variable s | +| not null | Guards.cs:343:31:343:38 | access to property Length | +| not null | Guards.cs:348:13:348:25 | ... is ... | +| not null | Guards.cs:349:13:349:13 | access to parameter o | | null | Assert.cs:9:24:9:27 | null | | null | Assert.cs:10:27:10:30 | null | | null | Assert.cs:16:24:16:27 | null | | null | Assert.cs:23:24:23:27 | null | | null | Assert.cs:30:24:30:27 | null | | null | Assert.cs:31:28:31:31 | null | +| null | Assert.cs:32:27:32:27 | access to local variable s | | null | Assert.cs:37:24:37:27 | null | | null | Assert.cs:38:28:38:31 | null | | null | Assert.cs:44:24:44:27 | null | | null | Assert.cs:45:29:45:32 | null | +| null | Assert.cs:46:27:46:27 | access to local variable s | | null | Assert.cs:51:24:51:27 | null | | null | Assert.cs:52:29:52:32 | null | | null | Assert.cs:58:24:58:27 | null | @@ -388,8 +893,10 @@ abstractValue | null | Assert.cs:66:29:66:32 | null | | null | Assert.cs:72:24:72:27 | null | | null | Assert.cs:73:28:73:31 | null | +| null | Assert.cs:74:27:74:27 | access to local variable s | | null | Assert.cs:79:24:79:27 | null | | null | Assert.cs:80:29:80:32 | null | +| null | Assert.cs:81:27:81:27 | access to local variable s | | null | Guards.cs:10:21:10:24 | null | | null | Guards.cs:24:18:24:21 | null | | null | Guards.cs:32:47:32:50 | null | @@ -411,82 +918,43 @@ abstractValue | null | Guards.cs:104:42:104:45 | null | | null | Guards.cs:106:9:106:25 | ... = ... | | null | Guards.cs:106:22:106:25 | null | +| null | Guards.cs:107:27:107:36 | access to property Property | +| null | Guards.cs:108:27:108:36 | access to property Property | | null | Guards.cs:115:52:115:55 | null | | null | Guards.cs:117:9:117:25 | ... = ... | | null | Guards.cs:117:22:117:25 | null | +| null | Guards.cs:118:27:118:36 | access to property Property | +| null | Guards.cs:119:27:119:36 | access to property Property | +| null | Guards.cs:130:18:130:21 | null | +| null | Guards.cs:131:20:131:20 | access to parameter s | +| null | Guards.cs:139:16:139:16 | access to parameter s | +| null | Guards.cs:159:18:159:21 | null | +| null | Guards.cs:160:24:160:24 | access to parameter o | | null | Guards.cs:172:39:172:42 | null | +| null | Guards.cs:176:18:176:21 | null | | null | Guards.cs:181:39:181:42 | null | | null | Guards.cs:185:43:185:46 | null | | null | Guards.cs:203:18:203:21 | null | +| null | Guards.cs:284:13:284:16 | null | +| null | Guards.cs:285:17:285:17 | access to parameter o | | null | Guards.cs:341:24:341:27 | null | | null | Guards.cs:342:18:342:21 | null | +| null | Guards.cs:348:22:348:25 | null | +| true | Assert.cs:73:36:73:36 | access to parameter b | +| true | Assert.cs:80:37:80:37 | access to parameter b | | true | Assert.cs:86:61:86:64 | true | | true | Guards.cs:177:20:177:23 | true | | true | Guards.cs:181:46:181:49 | true | +| true | Guards.cs:185:38:185:60 | ... ? ... : ... | | true | Guards.cs:185:50:185:53 | true | | true | Guards.cs:185:57:185:60 | true | | true | Guards.cs:215:18:215:21 | true | | true | Guards.cs:220:18:220:21 | true | +| true | Guards.cs:230:13:230:21 | ... = ... | | true | Guards.cs:230:18:230:21 | true | | true | Guards.cs:233:18:233:21 | true | | true | Guards.cs:293:18:293:21 | true | | true | Guards.cs:298:13:298:16 | true | +| true | Guards.cs:307:13:307:21 | ... = ... | | true | Guards.cs:307:18:307:21 | true | | true | Guards.cs:310:13:310:16 | true | -dualValue -| empty | non-empty | -| false | true | -| match 1 | non-match 1 | -| match 1 | non-match 1 | -| match "" | non-match "" | -| match "" | non-match "" | -| match Action a | non-match Action a | -| match Action a | non-match Action a | -| match _ | non-match _ | -| match _ | non-match _ | -| match _ | non-match _ | -| match _ | non-match _ | -| match _ | non-match _ | -| match access to constant B | non-match access to constant B | -| match access to constant B | non-match access to constant B | -| match access to type Action | non-match access to type Action | -| match access to type Action | non-match access to type Action | -| match null | non-match null | -| match null | non-match null | -| match true | non-match true | -| match true | non-match true | -| match true | non-match true | -| match true | non-match true | -| non-empty | empty | -| non-match 1 | match 1 | -| non-match 1 | match 1 | -| non-match "" | match "" | -| non-match "" | match "" | -| non-match Action a | match Action a | -| non-match Action a | match Action a | -| non-match _ | match _ | -| non-match _ | match _ | -| non-match _ | match _ | -| non-match _ | match _ | -| non-match _ | match _ | -| non-match access to constant B | match access to constant B | -| non-match access to constant B | match access to constant B | -| non-match access to type Action | match access to type Action | -| non-match access to type Action | match access to type Action | -| non-match null | match null | -| non-match null | match null | -| non-match true | match true | -| non-match true | match true | -| non-match true | match true | -| non-match true | match true | -| non-null | null | -| null | non-null | -| true | false | -singletonValue -| 0 | -| 1 | -| 3 | -| 10 | -| false | -| null | -| true | diff --git a/csharp/ql/test/library-tests/controlflow/guards/AbstractValue.ql b/csharp/ql/test/library-tests/controlflow/guards/AbstractValue.ql index e433004e3d5..a28da604ff6 100644 --- a/csharp/ql/test/library-tests/controlflow/guards/AbstractValue.ql +++ b/csharp/ql/test/library-tests/controlflow/guards/AbstractValue.ql @@ -2,11 +2,5 @@ import csharp private import semmle.code.csharp.controlflow.Guards query predicate abstractValue(AbstractValue value, Expr e) { - e = value.getAnExpr() and e.fromSource() -} - -query predicate dualValue(AbstractValue value, AbstractValue dual) { dual = value.getDualValue() } - -query predicate singletonValue(AbstractValue value) { - value.isSingleton() and value.getAnExpr().fromSource() + Guards::InternalUtil::exprHasValue(e, value) and e.fromSource() } diff --git a/csharp/ql/test/query-tests/Nullness/EqualityCheck.expected b/csharp/ql/test/query-tests/Nullness/EqualityCheck.expected index 7bb568f695a..5ba1fdf8765 100644 --- a/csharp/ql/test/query-tests/Nullness/EqualityCheck.expected +++ b/csharp/ql/test/query-tests/Nullness/EqualityCheck.expected @@ -190,12 +190,12 @@ | E.cs:83:13:83:24 | ... != ... | false | E.cs:83:21:83:24 | null | E.cs:83:13:83:16 | access to parameter vals | | E.cs:85:18:85:29 | ... != ... | false | E.cs:85:18:85:21 | access to parameter vals | E.cs:85:26:85:29 | null | | E.cs:85:18:85:29 | ... != ... | false | E.cs:85:26:85:29 | null | E.cs:85:18:85:21 | access to parameter vals | -| E.cs:90:17:90:27 | access to local variable switchguard | match access to constant MY_CONST_A | E.cs:90:17:90:27 | access to local variable switchguard | E.cs:92:18:92:27 | access to constant MY_CONST_A | -| E.cs:90:17:90:27 | access to local variable switchguard | match access to constant MY_CONST_A | E.cs:92:18:92:27 | access to constant MY_CONST_A | E.cs:90:17:90:27 | access to local variable switchguard | -| E.cs:90:17:90:27 | access to local variable switchguard | match access to constant MY_CONST_B | E.cs:90:17:90:27 | access to local variable switchguard | E.cs:97:18:97:27 | access to constant MY_CONST_B | -| E.cs:90:17:90:27 | access to local variable switchguard | match access to constant MY_CONST_B | E.cs:97:18:97:27 | access to constant MY_CONST_B | E.cs:90:17:90:27 | access to local variable switchguard | -| E.cs:90:17:90:27 | access to local variable switchguard | match access to constant MY_CONST_C | E.cs:90:17:90:27 | access to local variable switchguard | E.cs:95:18:95:27 | access to constant MY_CONST_C | -| E.cs:90:17:90:27 | access to local variable switchguard | match access to constant MY_CONST_C | E.cs:95:18:95:27 | access to constant MY_CONST_C | E.cs:90:17:90:27 | access to local variable switchguard | +| E.cs:92:13:92:28 | case ...: | true | E.cs:90:17:90:27 | access to local variable switchguard | E.cs:92:18:92:27 | access to constant MY_CONST_A | +| E.cs:92:13:92:28 | case ...: | true | E.cs:92:18:92:27 | access to constant MY_CONST_A | E.cs:90:17:90:27 | access to local variable switchguard | +| E.cs:95:13:95:28 | case ...: | true | E.cs:90:17:90:27 | access to local variable switchguard | E.cs:95:18:95:27 | access to constant MY_CONST_C | +| E.cs:95:13:95:28 | case ...: | true | E.cs:95:18:95:27 | access to constant MY_CONST_C | E.cs:90:17:90:27 | access to local variable switchguard | +| E.cs:97:13:97:28 | case ...: | true | E.cs:90:17:90:27 | access to local variable switchguard | E.cs:97:18:97:27 | access to constant MY_CONST_B | +| E.cs:97:13:97:28 | case ...: | true | E.cs:97:18:97:27 | access to constant MY_CONST_B | E.cs:90:17:90:27 | access to local variable switchguard | | E.cs:126:21:126:29 | ... == ... | true | E.cs:126:21:126:24 | access to local variable step | E.cs:126:29:126:29 | 0 | | E.cs:126:21:126:29 | ... == ... | true | E.cs:126:29:126:29 | 0 | E.cs:126:21:126:24 | access to local variable step | | E.cs:153:13:153:24 | ... != ... | false | E.cs:153:13:153:16 | access to local variable obj2 | E.cs:153:21:153:24 | null | @@ -252,6 +252,8 @@ | E.cs:442:13:442:18 | ... == ... | true | E.cs:442:18:442:18 | 1 | E.cs:442:13:442:13 | access to parameter i | | E.cs:447:13:447:18 | ... == ... | true | E.cs:447:13:447:13 | access to parameter i | E.cs:447:18:447:18 | 2 | | E.cs:447:13:447:18 | ... == ... | true | E.cs:447:18:447:18 | 2 | E.cs:447:13:447:13 | access to parameter i | +| E.cs:447:23:447:35 | ... is ... | false | E.cs:447:23:447:23 | access to parameter s | E.cs:447:32:447:35 | null | +| E.cs:447:23:447:35 | ... is ... | false | E.cs:447:32:447:35 | null | E.cs:447:23:447:23 | access to parameter s | | E.cs:452:13:452:18 | ... == ... | true | E.cs:452:13:452:13 | access to parameter i | E.cs:452:18:452:18 | 3 | | E.cs:452:13:452:18 | ... == ... | true | E.cs:452:18:452:18 | 3 | E.cs:452:13:452:13 | access to parameter i | | Forwarding.cs:59:13:59:21 | ... == ... | true | Forwarding.cs:59:13:59:13 | access to parameter o | Forwarding.cs:59:18:59:21 | null | diff --git a/csharp/ql/test/query-tests/Nullness/EqualityCheck.ql b/csharp/ql/test/query-tests/Nullness/EqualityCheck.ql index 3036911b5a6..1140d4e9a9d 100644 --- a/csharp/ql/test/query-tests/Nullness/EqualityCheck.ql +++ b/csharp/ql/test/query-tests/Nullness/EqualityCheck.ql @@ -1,5 +1,6 @@ import csharp import semmle.code.csharp.controlflow.Guards -from Expr e1, AbstractValue v, Expr e2 -select Internal::getAnEqualityCheck(e1, v, e2), v, e1, e2 +from Guard guard, Expr e1, Expr e2, boolean eqval +where guard.isEquality(e1, e2, eqval) +select guard, eqval, e1, e2 diff --git a/csharp/ql/test/query-tests/Nullness/Forwarding.cs b/csharp/ql/test/query-tests/Nullness/Forwarding.cs index 122c5036567..fc7b7eb2e8f 100644 --- a/csharp/ql/test/query-tests/Nullness/Forwarding.cs +++ b/csharp/ql/test/query-tests/Nullness/Forwarding.cs @@ -28,7 +28,7 @@ class ForwardingTests if (IsNotNull(s)) { - Console.WriteLine(s.Length); // GOOD + Console.WriteLine(s.Length); // $ SPURIOUS (false positive): Alert[cs/dereferenced-value-is-always-null] } if (IsNotNullWrong(s)) diff --git a/csharp/ql/test/query-tests/Nullness/NullAlways.expected b/csharp/ql/test/query-tests/Nullness/NullAlways.expected index f432be31d5a..a633c4a1506 100644 --- a/csharp/ql/test/query-tests/Nullness/NullAlways.expected +++ b/csharp/ql/test/query-tests/Nullness/NullAlways.expected @@ -37,6 +37,7 @@ | E.cs:405:16:405:16 | access to local variable i | Variable $@ is always null at this dereference. | E.cs:403:14:403:14 | i | i | | E.cs:439:13:439:13 | access to parameter s | Variable $@ is always null at this dereference. | E.cs:435:29:435:29 | s | s | | F.cs:8:9:8:9 | access to local variable o | Variable $@ is always null at this dereference. | F.cs:7:16:7:16 | o | o | +| Forwarding.cs:31:31:31:31 | access to local variable s | Variable $@ is always null at this dereference. | Forwarding.cs:7:16:7:16 | s | s | | Forwarding.cs:36:31:36:31 | access to local variable s | Variable $@ is always null at this dereference. | Forwarding.cs:7:16:7:16 | s | s | | Forwarding.cs:40:27:40:27 | access to local variable s | Variable $@ is always null at this dereference. | Forwarding.cs:7:16:7:16 | s | s | | NullAlwaysBad.cs:9:30:9:30 | access to parameter s | Variable $@ is always null at this dereference. | NullAlwaysBad.cs:7:29:7:29 | s | s |