C#: Remove some unnecessary TCs

This commit is contained in:
Tom Hvitved
2026-02-19 11:18:58 +01:00
parent 600f585a31
commit aa7a730041
5 changed files with 72 additions and 21 deletions

View File

@@ -142,6 +142,7 @@ private module GuardsInput implements
}
}
pragma[nomagic]
predicate equalityTest(Expr eqtest, Expr left, Expr right, boolean polarity) {
exists(ComparisonTest ct |
ct.getExpr() = eqtest and
@@ -410,6 +411,22 @@ private predicate typePattern(PatternMatch pm, TypePatternExpr tpe, Type t) {
t = pm.getExpr().getType()
}
pragma[nomagic]
private predicate dereferenceableExpr(Expr e, boolean isNullableType) {
exists(Type t | t = e.getType() |
t instanceof NullableType and
isNullableType = true
or
t instanceof RefType and
isNullableType = false
)
or
exists(Expr parent |
dereferenceableExpr(parent, isNullableType) and
e = getNullEquivParent(parent)
)
}
/**
* An expression that evaluates to a value that can be dereferenced. That is,
* an expression that may evaluate to `null`.
@@ -418,21 +435,12 @@ class DereferenceableExpr extends Expr {
private boolean isNullableType;
DereferenceableExpr() {
exists(Expr e, Type t |
// There is currently a bug in the extractor: the type of `x?.Length` is
// incorrectly `int`, while it should have been `int?`. We apply
// `getNullEquivParent()` as a workaround
this = getNullEquivParent*(e) and
t = e.getType() and
not this instanceof SwitchCaseExpr and
not this instanceof PatternExpr
|
t instanceof NullableType and
isNullableType = true
or
t instanceof RefType and
isNullableType = false
)
// There is currently a bug in the extractor: the type of `x?.Length` is
// incorrectly `int`, while it should have been `int?`. We apply
// `getNullEquivParent()` as a workaround
dereferenceableExpr(this, isNullableType) and
not this instanceof SwitchCaseExpr and
not this instanceof PatternExpr
}
/** Holds if this expression has a nullable type `T?`. */

View File

@@ -94,9 +94,19 @@ private Element getAChild(Element p) {
result = p.(AssignOperation).getExpandedAssignment()
}
pragma[nomagic]
private predicate astNode(Element e) {
e = any(@top_level_exprorstmt_parent p | not p instanceof Attribute)
or
exists(Element parent |
astNode(parent) and
e = getAChild(parent)
)
}
/** An AST node. */
class AstNode extends Element, TAstNode {
AstNode() { this = getAChild*(any(@top_level_exprorstmt_parent p | not p instanceof Attribute)) }
AstNode() { astNode(this) }
int getId() { idOf(this, result) }
}

View File

@@ -7,6 +7,7 @@ private import FlowSummaryImpl as FlowSummaryImpl
private import semmle.code.csharp.dataflow.FlowSummary as FlowSummary
private import semmle.code.csharp.dataflow.internal.ExternalFlow
private import semmle.code.csharp.Conversion
private import semmle.code.csharp.exprs.internal.Expr
private import semmle.code.csharp.dataflow.internal.SsaImpl as SsaImpl
private import semmle.code.csharp.ExprOrStmtParent
private import semmle.code.csharp.Unification
@@ -2377,6 +2378,16 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
storeStepDelegateCall(node1, c, node2)
}
pragma[nomagic]
private predicate isAssignExprLValueDescendant(Expr e) {
e = any(AssignExpr ae).getLValue()
or
exists(Expr parent |
isAssignExprLValueDescendant(parent) and
e = parent.getAChildExpr()
)
}
private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration {
ReadStepConfiguration() { this = "ReadStepConfiguration" }
@@ -2432,7 +2443,7 @@ private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration
scope =
any(AssignExpr ae |
ae = defTo.(AssignableDefinitions::TupleAssignmentDefinition).getAssignment() and
e = ae.getLValue().getAChildExpr*().(TupleExpr) and
isAssignExprLValueDescendant(e.(TupleExpr)) and
exactScope = false and
isSuccessor = true
)
@@ -2488,7 +2499,7 @@ private predicate readContentStep(Node node1, Content c, Node node2) {
)
or
// item = variable in node1 = (..., variable, ...) in a case/is var (..., ...)
te = any(PatternExpr pe).getAChildExpr*() and
isPatternExprDescendant(te) and
exists(AssignableDefinitions::LocalVariableDefinition lvd |
node2.(AssignableDefinitionNode).getDefinition() = lvd and
lvd.getDeclaration() = item and

View File

@@ -21,6 +21,7 @@ import semmle.code.csharp.Type
private import semmle.code.csharp.ExprOrStmtParent
private import semmle.code.csharp.frameworks.System
private import semmle.code.csharp.TypeRef
private import internal.Expr
/**
* An expression. Either an access (`Access`), a call (`Call`), an object or
@@ -64,14 +65,24 @@ class Expr extends ControlFlowElement, @expr {
/** Gets the enclosing callable of this expression, if any. */
override Callable getEnclosingCallable() { enclosingCallable(this, result) }
pragma[nomagic]
private predicate isExpandedAssignmentRValueDescendant() {
this =
any(AssignOperation op).getExpandedAssignment().getRValue().getChildExpr(0).getAChildExpr()
or
exists(Expr parent |
parent.isExpandedAssignmentRValueDescendant() and
this = parent.getAChildExpr()
)
}
/**
* Holds if this expression is generated by the compiler and does not appear
* explicitly in the source code.
*/
final predicate isImplicit() {
compiler_generated(this) or
this =
any(AssignOperation op).getExpandedAssignment().getRValue().getChildExpr(0).getAChildExpr+()
this.isExpandedAssignmentRValueDescendant()
}
/**
@@ -1133,7 +1144,7 @@ class TupleExpr extends Expr, @tuple_expr {
/** Holds if this expression is a tuple construction. */
predicate isConstruction() {
not this = getAnAssignOrForeachChild() and
not this = any(PatternExpr pe).getAChildExpr*()
not isPatternExprDescendant(this)
}
override string getAPrimaryQlClass() { result = "TupleExpr" }

View File

@@ -0,0 +1,11 @@
private import csharp
pragma[nomagic]
predicate isPatternExprDescendant(Expr e) {
e instanceof PatternExpr
or
exists(Expr parent |
isPatternExprDescendant(parent) and
e = parent.getAChildExpr()
)
}