diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Assignment.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Assignment.cs
index f3e2e510cd64..67e49b2919c2 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Assignment.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Assignment.cs
@@ -22,26 +22,12 @@ public static Assignment Create(ExpressionNodeInfo info)
protected override void PopulateExpression(TextWriter trapFile)
{
- var operatorKind = OperatorKind;
- if (operatorKind.HasValue)
- {
- // Convert assignment such as `a += b` into `a = a + b`.
- var simpleAssignExpr = new Expression(new ExpressionInfo(Context, Type, Location, ExprKind.SIMPLE_ASSIGN, this, 2, isCompilerGenerated: true, null));
- Create(Context, Syntax.Left, simpleAssignExpr, 1);
- var opexpr = new Expression(new ExpressionInfo(Context, Type, Location, operatorKind.Value, simpleAssignExpr, 0, isCompilerGenerated: true, null));
- Create(Context, Syntax.Left, opexpr, 0, isCompilerGenerated: true);
- Create(Context, Syntax.Right, opexpr, 1);
- opexpr.OperatorCall(trapFile, Syntax);
- }
- else
- {
- Create(Context, Syntax.Left, this, 1);
- Create(Context, Syntax.Right, this, 0);
+ Create(Context, Syntax.Left, this, 0);
+ Create(Context, Syntax.Right, this, 1);
- if (Kind == ExprKind.ADD_EVENT || Kind == ExprKind.REMOVE_EVENT)
- {
- OperatorCall(trapFile, Syntax);
- }
+ if (Kind != ExprKind.SIMPLE_ASSIGN && Kind != ExprKind.ASSIGN_COALESCE)
+ {
+ OperatorCall(trapFile, Syntax);
}
}
@@ -108,56 +94,5 @@ private static ExprKind GetKind(Context cx, AssignmentExpressionSyntax syntax)
return kind;
}
-
- ///
- /// Gets the kind of this assignment operator (null if the
- /// assignment is not an assignment operator). For example, the operator
- /// kind of `*=` is `*`.
- ///
- private ExprKind? OperatorKind
- {
- get
- {
- var kind = Kind;
- if (kind == ExprKind.REMOVE_EVENT || kind == ExprKind.ADD_EVENT || kind == ExprKind.SIMPLE_ASSIGN)
- return null;
-
- if (CallType.AdjustKind(kind) == ExprKind.OPERATOR_INVOCATION)
- return ExprKind.OPERATOR_INVOCATION;
-
- switch (kind)
- {
- case ExprKind.ASSIGN_ADD:
- return ExprKind.ADD;
- case ExprKind.ASSIGN_AND:
- return ExprKind.BIT_AND;
- case ExprKind.ASSIGN_DIV:
- return ExprKind.DIV;
- case ExprKind.ASSIGN_LSHIFT:
- return ExprKind.LSHIFT;
- case ExprKind.ASSIGN_MUL:
- return ExprKind.MUL;
- case ExprKind.ASSIGN_OR:
- return ExprKind.BIT_OR;
- case ExprKind.ASSIGN_REM:
- return ExprKind.REM;
- case ExprKind.ASSIGN_RSHIFT:
- return ExprKind.RSHIFT;
- case ExprKind.ASSIGN_URSHIFT:
- return ExprKind.URSHIFT;
- case ExprKind.ASSIGN_SUB:
- return ExprKind.SUB;
- case ExprKind.ASSIGN_XOR:
- return ExprKind.BIT_XOR;
- case ExprKind.ASSIGN_COALESCE:
- return ExprKind.NULL_COALESCING;
- default:
- Context.ModelError(Syntax, $"Couldn't unfold assignment of type {kind}");
- return ExprKind.UNKNOWN;
- }
- }
- }
-
- public new CallType CallType => GetCallType(Context, Syntax);
}
}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Initializer.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Initializer.cs
index 92e2b910f992..63024cd47fcb 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Initializer.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Initializer.cs
@@ -83,30 +83,31 @@ protected override void PopulateExpression(TextWriter trapFile)
{
var assignmentInfo = new ExpressionNodeInfo(Context, init, this, child++).SetKind(ExprKind.SIMPLE_ASSIGN);
var assignmentEntity = new Expression(assignmentInfo);
- var typeInfoRight = Context.GetTypeInfo(assignment.Right);
- if (typeInfoRight.Type is null)
- // The type may be null for nested initializers such as
- // ```csharp
- // new ClassWithArrayField() { As = { [0] = a } }
- // ```
- // In this case we take the type from the assignment
- // `As = { [0] = a }` instead
- typeInfoRight = assignmentInfo.TypeInfo;
- CreateFromNode(new ExpressionNodeInfo(Context, assignment.Right, assignmentEntity, 0, typeInfoRight));
-
var target = Context.GetSymbolInfo(assignment.Left);
// If the target is null, then assume that this is an array initializer (of the form `[...] = ...`)
-
var access = target.Symbol is null ?
- new Expression(new ExpressionNodeInfo(Context, assignment.Left, assignmentEntity, 1).SetKind(ExprKind.ARRAY_ACCESS)) :
- Access.Create(new ExpressionNodeInfo(Context, assignment.Left, assignmentEntity, 1), target.Symbol, false, Context.CreateEntity(target.Symbol));
+ new Expression(new ExpressionNodeInfo(Context, assignment.Left, assignmentEntity, 0).SetKind(ExprKind.ARRAY_ACCESS)) :
+ Access.Create(new ExpressionNodeInfo(Context, assignment.Left, assignmentEntity, 0), target.Symbol, false, Context.CreateEntity(target.Symbol));
if (assignment.Left is ImplicitElementAccessSyntax iea)
{
// An array/indexer initializer of the form `[...] = ...`
access.PopulateArguments(trapFile, iea.ArgumentList.Arguments, 0);
}
+
+ var typeInfoRight = Context.GetTypeInfo(assignment.Right);
+ if (typeInfoRight.Type is null)
+ {
+ // The type may be null for nested initializers such as
+ // ```csharp
+ // new ClassWithArrayField() { As = { [0] = a } }
+ // ```
+ // In this case we take the type from the assignment
+ // `As = { [0] = a }` instead
+ typeInfoRight = assignmentInfo.TypeInfo;
+ }
+ CreateFromNode(new ExpressionNodeInfo(Context, assignment.Right, assignmentEntity, 1, typeInfoRight));
}
else
{
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ObjectCreation/AnonymousObjectCreation.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ObjectCreation/AnonymousObjectCreation.cs
index a6f94f533387..1fdf03171b9f 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ObjectCreation/AnonymousObjectCreation.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ObjectCreation/AnonymousObjectCreation.cs
@@ -41,11 +41,11 @@ protected override void PopulateExpression(TextWriter trapFile)
var loc = Context.CreateLocation(init.GetLocation());
var assignment = new Expression(new ExpressionInfo(Context, type, loc, ExprKind.SIMPLE_ASSIGN, objectInitializer, child++, isCompilerGenerated: false, null));
- Create(Context, init.Expression, assignment, 0);
Property.Create(Context, property);
-
- var access = new Expression(new ExpressionInfo(Context, type, loc, ExprKind.PROPERTY_ACCESS, assignment, 1, isCompilerGenerated: false, null));
+ var access = new Expression(new ExpressionInfo(Context, type, loc, ExprKind.PROPERTY_ACCESS, assignment, 0, isCompilerGenerated: false, null));
trapFile.expr_access(access, propEntity);
+
+ Create(Context, init.Expression, assignment, 1);
}
}
}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Query.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Query.cs
index 85a1ceda47ca..aadf06f2dee6 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Query.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Query.cs
@@ -94,12 +94,12 @@ protected Expression DeclareRangeVariable(Context cx, IExpressionParentEntity pa
child
);
- Expression.Create(cx, Expr, decl, 0);
-
var nameLoc = cx.CreateLocation(name.GetLocation());
- var access = new Expression(new ExpressionInfo(cx, type, nameLoc, ExprKind.LOCAL_VARIABLE_ACCESS, decl, 1, isCompilerGenerated: false, null));
+ var access = new Expression(new ExpressionInfo(cx, type, nameLoc, ExprKind.LOCAL_VARIABLE_ACCESS, decl, 0, isCompilerGenerated: false, null));
cx.TrapWriter.Writer.expr_access(access, LocalVariable.Create(cx, variableSymbol));
+ Expression.Create(cx, Expr, decl, 1);
+
return decl;
}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/VariableDeclaration.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/VariableDeclaration.cs
index c44f9e2b9468..47ecee3e037e 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/VariableDeclaration.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/VariableDeclaration.cs
@@ -176,11 +176,11 @@ public static VariableDeclaration CreateDeclarator(Context cx, VariableDeclarato
if (d.Initializer is not null)
{
- Create(cx, d.Initializer.Value, ret, 0);
-
// Create an access
- var access = new Expression(new ExpressionInfo(cx, type, localVar.Location, ExprKind.LOCAL_VARIABLE_ACCESS, ret, 1, isCompilerGenerated: false, null));
+ var access = new Expression(new ExpressionInfo(cx, type, localVar.Location, ExprKind.LOCAL_VARIABLE_ACCESS, ret, 0, isCompilerGenerated: false, null));
cx.TrapWriter.Writer.expr_access(access, localVar);
+
+ Create(cx, d.Initializer.Value, ret, 1);
}
if (d.Parent is VariableDeclarationSyntax decl)
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs
index 329115f11c7a..708c00d2f736 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Field.cs
@@ -116,9 +116,9 @@ private Expression AddInitializerAssignment(TextWriter trapFile, ExpressionSynta
{
var type = Symbol.GetAnnotatedType();
var simpleAssignExpr = new Expression(new ExpressionInfo(Context, type, loc, ExprKind.SIMPLE_ASSIGN, this, child++, isCompilerGenerated: true, constValue));
- Expression.CreateFromNode(new ExpressionNodeInfo(Context, initializer, simpleAssignExpr, 0));
- var access = new Expression(new ExpressionInfo(Context, type, Location, ExprKind.FIELD_ACCESS, simpleAssignExpr, 1, isCompilerGenerated: true, constValue));
+ var access = new Expression(new ExpressionInfo(Context, type, Location, ExprKind.FIELD_ACCESS, simpleAssignExpr, 0, isCompilerGenerated: true, constValue));
trapFile.expr_access(access, this);
+ Expression.CreateFromNode(new ExpressionNodeInfo(Context, initializer, simpleAssignExpr, 1));
return access;
}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs
index 57eb5efc0070..988ca843927e 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Property.cs
@@ -94,9 +94,9 @@ public override void Populate(TextWriter trapFile)
var loc = Context.CreateLocation(initializer!.GetLocation());
var annotatedType = AnnotatedTypeSymbol.CreateNotAnnotated(Symbol.Type);
var simpleAssignExpr = new Expression(new ExpressionInfo(Context, annotatedType, loc, ExprKind.SIMPLE_ASSIGN, this, child++, isCompilerGenerated: true, null));
- Expression.CreateFromNode(new ExpressionNodeInfo(Context, initializer.Value, simpleAssignExpr, 0));
- var access = new Expression(new ExpressionInfo(Context, annotatedType, Location, ExprKind.PROPERTY_ACCESS, simpleAssignExpr, 1, isCompilerGenerated: true, null));
+ var access = new Expression(new ExpressionInfo(Context, annotatedType, Location, ExprKind.PROPERTY_ACCESS, simpleAssignExpr, 0, isCompilerGenerated: true, null));
trapFile.expr_access(access, this);
+ Expression.CreateFromNode(new ExpressionNodeInfo(Context, initializer.Value, simpleAssignExpr, 1));
if (!Symbol.IsStatic)
{
This.CreateImplicit(Context, Symbol.ContainingType, Location, access, -1);
diff --git a/csharp/ql/campaigns/Solorigate/src/ModifiedFnvFunctionDetection.ql b/csharp/ql/campaigns/Solorigate/src/ModifiedFnvFunctionDetection.ql
index e09d00807e6d..ed350932bd86 100644
--- a/csharp/ql/campaigns/Solorigate/src/ModifiedFnvFunctionDetection.ql
+++ b/csharp/ql/campaigns/Solorigate/src/ModifiedFnvFunctionDetection.ql
@@ -13,19 +13,14 @@ import csharp
import Solorigate
import experimental.code.csharp.Cryptography.NonCryptographicHashes
+ControlFlowNode loopExitNode(LoopStmt loop) { result.isAfter(loop) }
+
from Variable v, Literal l, LoopStmt loop, Expr additional_xor
where
maybeUsedInFnvFunction(v, _, _, loop) and
- (
- exists(BitwiseXorExpr xor2 | xor2.getAnOperand() = l and additional_xor = xor2 |
- loop.getAControlFlowExitNode().getASuccessor*() = xor2.getAControlFlowNode() and
- xor2.getAnOperand() = v.getAnAccess()
- )
- or
- exists(AssignXorExpr xor2 | xor2.getAnOperand() = l and additional_xor = xor2 |
- loop.getAControlFlowExitNode().getASuccessor*() = xor2.getAControlFlowNode() and
- xor2.getAnOperand() = v.getAnAccess()
- )
+ exists(BitwiseXorOperation xor2 | xor2.getAnOperand() = l and additional_xor = xor2 |
+ loopExitNode(loop).getASuccessor*() = xor2.getAControlFlowNode() and
+ xor2.getAnOperand() = v.getAnAccess()
)
select l, "This literal is used in an $@ after an FNV-like hash calculation with variable $@.",
additional_xor, "additional xor", v, v.toString()
diff --git a/csharp/ql/consistency-queries/CfgConsistency.ql b/csharp/ql/consistency-queries/CfgConsistency.ql
index 4b0d15f8a819..883014fdcf08 100644
--- a/csharp/ql/consistency-queries/CfgConsistency.ql
+++ b/csharp/ql/consistency-queries/CfgConsistency.ql
@@ -1,5 +1,2 @@
import csharp
-import semmle.code.csharp.controlflow.internal.Completion
-import ControlFlow
-import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl::Consistency
-import semmle.code.csharp.controlflow.internal.Splitting
+import ControlFlow::Consistency
diff --git a/csharp/ql/consistency-queries/DataFlowConsistency.ql b/csharp/ql/consistency-queries/DataFlowConsistency.ql
index 03e0f3f1b740..983206aada2c 100644
--- a/csharp/ql/consistency-queries/DataFlowConsistency.ql
+++ b/csharp/ql/consistency-queries/DataFlowConsistency.ql
@@ -1,5 +1,4 @@
import csharp
-private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl as ControlFlowGraphImpl
private import semmle.code.csharp.dataflow.internal.DataFlowImplSpecific
private import semmle.code.csharp.dataflow.internal.TaintTrackingImplSpecific
private import codeql.dataflow.internal.DataFlowImplConsistency
@@ -7,20 +6,6 @@ private import codeql.dataflow.internal.DataFlowImplConsistency
private module Input implements InputSig {
private import CsharpDataFlow
- private predicate isStaticAssignable(Assignable a) { a.(Modifiable).isStatic() }
-
- predicate uniqueEnclosingCallableExclude(Node node) {
- // TODO: Remove once static initializers are folded into the
- // static constructors
- isStaticAssignable(ControlFlowGraphImpl::getNodeCfgScope(node.getControlFlowNode()))
- }
-
- predicate uniqueCallEnclosingCallableExclude(DataFlowCall call) {
- // TODO: Remove once static initializers are folded into the
- // static constructors
- isStaticAssignable(ControlFlowGraphImpl::getNodeCfgScope(call.getControlFlowNode()))
- }
-
predicate uniqueNodeLocationExclude(Node n) {
// Methods with multiple implementations
n instanceof ParameterNode
@@ -70,16 +55,6 @@ private module Input implements InputSig {
init.getInitializer().getNumberOfChildren() > 1
)
or
- exists(ControlFlow::Nodes::ElementNode cfn, ControlFlow::Nodes::Split split |
- exists(arg.asExprAtNode(cfn))
- |
- split = cfn.getASplit() and
- not split = call.getControlFlowNode().getASplit()
- or
- split = call.getControlFlowNode().getASplit() and
- not split = cfn.getASplit()
- )
- or
call.(NonDelegateDataFlowCall).getDispatchCall().isReflection()
)
}
diff --git a/csharp/ql/consistency-queries/VariableCaptureConsistency.ql b/csharp/ql/consistency-queries/VariableCaptureConsistency.ql
index 927741f07bfe..869be5aea9fe 100644
--- a/csharp/ql/consistency-queries/VariableCaptureConsistency.ql
+++ b/csharp/ql/consistency-queries/VariableCaptureConsistency.ql
@@ -1,13 +1,6 @@
import csharp
import semmle.code.csharp.dataflow.internal.DataFlowPrivate::VariableCapture::Flow::ConsistencyChecks
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate::VariableCapture::Flow::ConsistencyChecks as ConsistencyChecks
-private import semmle.code.csharp.controlflow.BasicBlocks
-private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl
-
-query predicate uniqueEnclosingCallable(BasicBlock bb, string msg) {
- ConsistencyChecks::uniqueEnclosingCallable(bb, msg) and
- getNodeCfgScope(bb.getFirstNode()) instanceof Callable
-}
query predicate consistencyOverview(string msg, int n) { none() }
diff --git a/csharp/ql/lib/experimental/code/csharp/Cryptography/NonCryptographicHashes.qll b/csharp/ql/lib/experimental/code/csharp/Cryptography/NonCryptographicHashes.qll
index 49dd011658d4..b09251cf6e42 100644
--- a/csharp/ql/lib/experimental/code/csharp/Cryptography/NonCryptographicHashes.qll
+++ b/csharp/ql/lib/experimental/code/csharp/Cryptography/NonCryptographicHashes.qll
@@ -48,7 +48,7 @@ private predicate maybeUsedInElfHashFunction(Variable v, Operation xor, Operatio
Expr e1, Expr e2, AssignExpr addAssign, AssignExpr xorAssign, Operation notOp,
AssignExpr notAssign
|
- (add instanceof AddExpr or add instanceof AssignAddExpr) and
+ add instanceof AddOperation and
e1.getAChild*() = add.getAnOperand() and
e1 instanceof BinaryBitwiseOperation and
e2 = e1.(BinaryBitwiseOperation).getLeftOperand() and
diff --git a/csharp/ql/lib/printCfg.ql b/csharp/ql/lib/printCfg.ql
index aa92b1192042..c418446b2164 100644
--- a/csharp/ql/lib/printCfg.ql
+++ b/csharp/ql/lib/printCfg.ql
@@ -7,7 +7,7 @@
* @tags ide-contextual-queries/print-cfg
*/
-private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl
+import csharp
external string selectedSourceFile();
@@ -21,7 +21,7 @@ external int selectedSourceColumn();
private predicate selectedSourceColumnAlias = selectedSourceColumn/0;
-module ViewCfgQueryInput implements ViewCfgQueryInputSig {
+module ViewCfgQueryInput implements ControlFlow::ViewCfgQueryInputSig {
predicate selectedSourceFile = selectedSourceFileAlias/0;
predicate selectedSourceLine = selectedSourceLineAlias/0;
@@ -29,7 +29,7 @@ module ViewCfgQueryInput implements ViewCfgQueryInputSig {
predicate selectedSourceColumn = selectedSourceColumnAlias/0;
predicate cfgScopeSpan(
- CfgScope scope, File file, int startLine, int startColumn, int endLine, int endColumn
+ Callable scope, File file, int startLine, int startColumn, int endLine, int endColumn
) {
file = scope.getFile() and
scope.getLocation().getStartLine() = startLine and
@@ -40,11 +40,20 @@ module ViewCfgQueryInput implements ViewCfgQueryInputSig {
|
loc = scope.(Callable).getBody().getLocation()
or
- loc = scope.(Field).getInitializer().getLocation()
+ loc = any(AssignExpr init | scope.(ObjectInitMethod).initializes(init)).getLocation()
or
- loc = scope.(Property).getInitializer().getLocation()
+ exists(AssignableMember a, Constructor ctor |
+ scope = ctor and
+ ctor.isStatic() and
+ a.isStatic() and
+ a.getDeclaringType() = ctor.getDeclaringType()
+ |
+ loc = a.(Field).getInitializer().getLocation()
+ or
+ loc = a.(Property).getInitializer().getLocation()
+ )
)
}
}
-import ViewCfgQuery
+import ControlFlow::ViewCfgQuery
diff --git a/csharp/ql/lib/semmle/code/csharp/Assignable.qll b/csharp/ql/lib/semmle/code/csharp/Assignable.qll
index 3c7170a6f846..3e6b9bd57dfc 100644
--- a/csharp/ql/lib/semmle/code/csharp/Assignable.qll
+++ b/csharp/ql/lib/semmle/code/csharp/Assignable.qll
@@ -78,12 +78,14 @@ class AssignableRead extends AssignableAccess {
this.isRefArgument()
or
this = any(AssignableDefinitions::AddressOfDefinition def).getTargetAccess()
+ or
+ this = any(AssignableDefinitions::AssignOperationDefinition def).getTargetAccess()
) and
not nameOfChild(_, this)
}
pragma[noinline]
- private ControlFlow::Node getAnAdjacentReadSameVar() {
+ private ControlFlowNode getAnAdjacentReadSameVar() {
SsaImpl::adjacentReadPairSameVar(_, this.getAControlFlowNode(), result)
}
@@ -113,7 +115,7 @@ class AssignableRead extends AssignableAccess {
*/
pragma[nomagic]
AssignableRead getANextRead() {
- forex(ControlFlow::Node cfn | cfn = result.getAControlFlowNode() |
+ forex(ControlFlowNode cfn | cfn = result.getAControlFlowNode() |
cfn = this.getAnAdjacentReadSameVar()
)
}
@@ -271,6 +273,8 @@ module AssignableInternal {
def = TAddressOfDefinition(result)
or
def = TPatternDefinition(result)
+ or
+ def = TAssignOperationDefinition(result)
}
/** A local variable declaration at the top-level of a pattern. */
@@ -286,7 +290,11 @@ module AssignableInternal {
private module Cached {
cached
newtype TAssignableDefinition =
- TAssignmentDefinition(Assignment a) { not a.getLValue() instanceof TupleExpr } or
+ TAssignmentDefinition(Assignment a) {
+ not a.getLValue() instanceof TupleExpr and
+ not a instanceof AssignCallOperation and
+ not a instanceof AssignCoalesceExpr
+ } or
TTupleAssignmentDefinition(AssignExpr ae, Expr leaf) { tupleAssignmentDefinition(ae, leaf) } or
TOutRefDefinition(AssignableAccess aa) {
aa.isOutArgument()
@@ -309,7 +317,11 @@ module AssignableInternal {
)
} or
TAddressOfDefinition(AddressOfExpr aoe) or
- TPatternDefinition(TopLevelPatternDecl tlpd)
+ TPatternDefinition(TopLevelPatternDecl tlpd) or
+ TAssignOperationDefinition(AssignOperation ao) {
+ ao instanceof AssignCallOperation or
+ ao instanceof AssignCoalesceExpr
+ }
/**
* Gets the source expression assigned in tuple definition `def`, if any.
@@ -355,6 +367,8 @@ module AssignableInternal {
def = TMutationDefinition(any(MutatorOperation mo | mo.getOperand() = result))
or
def = TAddressOfDefinition(any(AddressOfExpr aoe | aoe.getOperand() = result))
+ or
+ def = TAssignOperationDefinition(any(AssignOperation ao | ao.getLeftOperand() = result))
}
/**
@@ -369,8 +383,10 @@ module AssignableInternal {
or
exists(Assignment ass | ac = ass.getLValue() |
result = ass.getRValue() and
- not ass.(AssignOperation).hasExpandedAssignment()
+ not ass instanceof AssignOperation
)
+ or
+ exists(AssignOperation ao | ac = ao.getLeftOperand() | result = ao)
}
}
@@ -388,8 +404,9 @@ private import AssignableInternal
* a mutation update (`AssignableDefinitions::MutationDefinition`), a local variable
* declaration without an initializer (`AssignableDefinitions::LocalVariableDefinition`),
* an implicit parameter definition (`AssignableDefinitions::ImplicitParameterDefinition`),
- * an address-of definition (`AssignableDefinitions::AddressOfDefinition`), or a pattern
- * definition (`AssignableDefinitions::PatternDefinition`).
+ * an address-of definition (`AssignableDefinitions::AddressOfDefinition`), a pattern
+ * definition (`AssignableDefinitions::PatternDefinition`), or a compound assignment
+ * operation definition (`AssignableDefinitions::AssignOperationDefinition`)
*/
class AssignableDefinition extends TAssignableDefinition {
/**
@@ -402,9 +419,7 @@ class AssignableDefinition extends TAssignableDefinition {
* the definitions of `x` and `y` in `M(out x, out y)` and `(x, y) = (0, 1)`
* relate to the same call to `M` and assignment node, respectively.
*/
- deprecated ControlFlow::Node getAControlFlowNode() {
- result = this.getExpr().getAControlFlowNode()
- }
+ deprecated ControlFlowNode getAControlFlowNode() { result = this.getExpr().getAControlFlowNode() }
/**
* Gets the underlying expression that updates the targeted assignable when
@@ -477,7 +492,7 @@ class AssignableDefinition extends TAssignableDefinition {
*/
pragma[nomagic]
AssignableRead getAFirstRead() {
- forex(ControlFlow::Node cfn | cfn = result.getAControlFlowNode() |
+ forex(ControlFlowNode cfn | cfn = result.getAControlFlowNode() |
exists(Ssa::ExplicitDefinition def | result = def.getAFirstReadAtNode(cfn) |
this = def.getADefinition()
)
@@ -511,7 +526,7 @@ module AssignableDefinitions {
override Expr getSource() {
result = a.getRValue() and
- not a instanceof AssignOperation
+ not a instanceof AddOrRemoveEventExpr
}
override string toString() { result = a.toString() }
@@ -555,9 +570,7 @@ module AssignableDefinitions {
}
/** Holds if a node in basic block `bb` assigns to `ref` parameter `p` via definition `def`. */
- private predicate basicBlockRefParamDef(
- ControlFlow::BasicBlock bb, Parameter p, AssignableDefinition def
- ) {
+ private predicate basicBlockRefParamDef(BasicBlock bb, Parameter p, AssignableDefinition def) {
def = any(RefArg arg).getAnAnalyzableRefDef(p) and
bb.getANode() = def.getExpr().getAControlFlowNode()
}
@@ -568,7 +581,7 @@ module AssignableDefinitions {
* any assignments to `p`.
*/
pragma[nomagic]
- private predicate parameterReachesWithoutDef(Parameter p, ControlFlow::BasicBlock bb) {
+ private predicate parameterReachesWithoutDef(Parameter p, BasicBlock bb) {
forall(AssignableDefinition def | basicBlockRefParamDef(bb, p, def) |
isUncertainRefCall(def.getTargetAccess())
) and
@@ -576,9 +589,7 @@ module AssignableDefinitions {
any(RefArg arg).isAnalyzable(p) and
p.getCallable().getEntryPoint() = bb.getFirstNode()
or
- exists(ControlFlow::BasicBlock mid | parameterReachesWithoutDef(p, mid) |
- bb = mid.getASuccessor()
- )
+ exists(BasicBlock mid | parameterReachesWithoutDef(p, mid) | bb = mid.getASuccessor())
)
}
@@ -590,7 +601,7 @@ module AssignableDefinitions {
cached
predicate isUncertainRefCall(RefArg arg) {
arg.isPotentialAssignment() and
- exists(ControlFlow::BasicBlock bb, Parameter p | arg.isAnalyzable(p) |
+ exists(BasicBlock bb, Parameter p | arg.isAnalyzable(p) |
parameterReachesWithoutDef(p, bb) and
bb.getLastNode() = p.getCallable().getExitPoint()
)
@@ -671,7 +682,7 @@ module AssignableDefinitions {
/** Gets the underlying parameter. */
Parameter getParameter() { result = p }
- deprecated override ControlFlow::Node getAControlFlowNode() {
+ deprecated override ControlFlowNode getAControlFlowNode() {
result = p.getCallable().getEntryPoint()
}
@@ -735,4 +746,17 @@ module AssignableDefinitions {
/** Gets the assignable (field or property) being initialized. */
Assignable getAssignable() { result = fieldOrProp }
}
+
+ /**
+ * A definition by a compound assignment operation, for example `x += y`.
+ */
+ class AssignOperationDefinition extends AssignableDefinition, TAssignOperationDefinition {
+ AssignOperation ao;
+
+ AssignOperationDefinition() { this = TAssignOperationDefinition(ao) }
+
+ override Expr getSource() { result = ao }
+
+ override string toString() { result = ao.toString() }
+ }
}
diff --git a/csharp/ql/lib/semmle/code/csharp/Caching.qll b/csharp/ql/lib/semmle/code/csharp/Caching.qll
index bbe310fe69e5..134332ee75de 100644
--- a/csharp/ql/lib/semmle/code/csharp/Caching.qll
+++ b/csharp/ql/lib/semmle/code/csharp/Caching.qll
@@ -7,23 +7,6 @@ private import csharp
* in the same stage across different files.
*/
module Stages {
- cached
- module ControlFlowStage {
- private import semmle.code.csharp.controlflow.internal.Splitting
-
- cached
- predicate forceCachingInSameStage() { any() }
-
- cached
- private predicate forceCachingInSameStageRev() {
- exists(Split s)
- or
- exists(ControlFlow::Node n)
- or
- forceCachingInSameStageRev()
- }
- }
-
cached
module GuardsStage {
private import semmle.code.csharp.controlflow.Guards
diff --git a/csharp/ql/lib/semmle/code/csharp/Callable.qll b/csharp/ql/lib/semmle/code/csharp/Callable.qll
index 611b578b859a..7859c7ea1d0e 100644
--- a/csharp/ql/lib/semmle/code/csharp/Callable.qll
+++ b/csharp/ql/lib/semmle/code/csharp/Callable.qll
@@ -22,7 +22,7 @@ private import TypeRef
* an anonymous function (`AnonymousFunctionExpr`), or a local function
* (`LocalFunction`).
*/
-class Callable extends Parameterizable, ExprOrStmtParent, @callable {
+class Callable extends Parameterizable, ControlFlowElementOrCallable, @callable {
/** Gets the return type of this callable. */
Type getReturnType() { none() }
@@ -157,10 +157,10 @@ class Callable extends Parameterizable, ExprOrStmtParent, @callable {
final predicate hasExpressionBody() { exists(this.getExpressionBody()) }
/** Gets the entry point in the control graph for this callable. */
- ControlFlow::Nodes::EntryNode getEntryPoint() { result.getCallable() = this }
+ ControlFlow::EntryNode getEntryPoint() { result.getEnclosingCallable() = this }
/** Gets the exit point in the control graph for this callable. */
- ControlFlow::Nodes::ExitNode getExitPoint() { result.getCallable() = this }
+ ControlFlow::ExitNode getExitPoint() { result.getEnclosingCallable() = this }
/**
* Gets the enclosing callable of this callable, if any.
diff --git a/csharp/ql/lib/semmle/code/csharp/ExprOrStmtParent.qll b/csharp/ql/lib/semmle/code/csharp/ExprOrStmtParent.qll
index 5afacf608a8c..1aa558194e3a 100644
--- a/csharp/ql/lib/semmle/code/csharp/ExprOrStmtParent.qll
+++ b/csharp/ql/lib/semmle/code/csharp/ExprOrStmtParent.qll
@@ -20,7 +20,7 @@ class ExprOrStmtParent extends Element, @exprorstmt_parent {
/** Gets the `i`th child expression of this element (zero-based). */
final Expr getChildExpr(int i) {
- expr_parent_adjusted(result, i, this) or
+ expr_parent(result, i, this) or
expr_parent_top_level_adjusted(result, i, this)
}
@@ -118,67 +118,9 @@ private module Cached {
i = 0
}
- /**
- * The `expr_parent()` relation adjusted for expandable assignments. For example,
- * the assignment `x += y` is extracted as
- *
- * ```
- * +=
- * |
- * 2
- * |
- * =
- * / \
- * 1 0
- * / \
- * x +
- * / \
- * 1 0
- * / \
- * x y
- * ```
- *
- * in order to be able to retrieve the expanded assignment `x = x + y` as the 2nd
- * child. This predicate changes the diagram above into
- *
- * ```
- * +=
- * / \
- * 1 0
- * / \
- * x y
- * ```
- */
- cached
- predicate expr_parent_adjusted(Expr child, int i, ControlFlowElement parent) {
- if parent instanceof AssignOperation
- then
- parent =
- any(AssignOperation ao |
- exists(AssignExpr ae | ae = ao.getExpandedAssignment() |
- i = 0 and
- exists(Expr right |
- // right = `x + y`
- expr_parent(right, 0, ae)
- |
- expr_parent(child, 1, right)
- )
- or
- i = 1 and
- expr_parent(child, 1, ae)
- )
- or
- not ao.hasExpandedAssignment() and
- expr_parent(child, i, parent)
- )
- else expr_parent(child, i, parent)
- }
-
private Expr getAChildExpr(ExprOrStmtParent parent) {
result = parent.getAChildExpr() and
not result = parent.(DeclarationWithGetSetAccessors).getExpressionBody()
- or
- result = parent.(AssignOperation).getExpandedAssignment()
}
private ControlFlowElement getAChild(ExprOrStmtParent parent) {
@@ -187,13 +129,6 @@ private module Cached {
result = parent.getAChildStmt()
}
- pragma[inline]
- private ControlFlowElement enclosingStart(ControlFlowElement cfe) {
- result = cfe
- or
- getAChild(result).(AnonymousFunctionExpr) = cfe
- }
-
private predicate parent(ControlFlowElement child, ExprOrStmtParent parent) {
child = getAChild(parent) and
not child = getBody(_)
@@ -203,7 +138,7 @@ private module Cached {
cached
predicate enclosingBody(ControlFlowElement cfe, ControlFlowElement body) {
body = getBody(_) and
- parent*(enclosingStart(cfe), body)
+ parent*(cfe, body)
}
/** Holds if the enclosing callable of `cfe` is `c`. */
@@ -211,7 +146,7 @@ private module Cached {
predicate enclosingCallable(ControlFlowElement cfe, Callable c) {
enclosingBody(cfe, getBody(c))
or
- parent*(enclosingStart(cfe), c.(Constructor).getInitializer())
+ parent*(cfe, c.(Constructor).getInitializer())
or
parent*(cfe, c.(Constructor).getObjectInitializerCall())
or
diff --git a/csharp/ql/lib/semmle/code/csharp/Property.qll b/csharp/ql/lib/semmle/code/csharp/Property.qll
index 88665280d5b9..bbd4fdd9d8ec 100644
--- a/csharp/ql/lib/semmle/code/csharp/Property.qll
+++ b/csharp/ql/lib/semmle/code/csharp/Property.qll
@@ -226,7 +226,7 @@ class Property extends DeclarationWithGetSetAccessors, @property {
* }
* ```
*/
- Expr getInitializer() { result = this.getChildExpr(1).getChildExpr(0) }
+ Expr getInitializer() { result = this.getChildExpr(1).getChildExpr(1) }
/**
* Holds if this property has an initial value. For example, the initial
diff --git a/csharp/ql/lib/semmle/code/csharp/Variable.qll b/csharp/ql/lib/semmle/code/csharp/Variable.qll
index 746ea6acd2f6..6d59816373d2 100644
--- a/csharp/ql/lib/semmle/code/csharp/Variable.qll
+++ b/csharp/ql/lib/semmle/code/csharp/Variable.qll
@@ -408,7 +408,7 @@ class Field extends Variable, AssignableMember, Attributable, TopLevelExprParent
* }
* ```
*/
- final override Expr getInitializer() { result = this.getChildExpr(0).getChildExpr(0) }
+ final override Expr getInitializer() { result = this.getChildExpr(0).getChildExpr(1) }
/**
* Holds if this field has an initial value. For example, the initial
diff --git a/csharp/ql/lib/semmle/code/csharp/commons/Constants.qll b/csharp/ql/lib/semmle/code/csharp/commons/Constants.qll
index ab2d9e0eef74..2fba9288be45 100644
--- a/csharp/ql/lib/semmle/code/csharp/commons/Constants.qll
+++ b/csharp/ql/lib/semmle/code/csharp/commons/Constants.qll
@@ -5,16 +5,16 @@ private import semmle.code.csharp.commons.ComparisonTest
private import semmle.code.csharp.commons.StructuralComparison as StructuralComparison
pragma[noinline]
-private predicate isConstantCondition0(ControlFlow::Node cfn, boolean b) {
- exists(cfn.getASuccessorByType(any(ControlFlow::BooleanSuccessor t | t.getValue() = b))) and
- strictcount(ControlFlow::SuccessorType t | exists(cfn.getASuccessorByType(t))) = 1
+private predicate isConstantCondition0(ControlFlowNode cfn, boolean b) {
+ exists(cfn.getASuccessor(any(ControlFlow::BooleanSuccessor t | t.getValue() = b))) and
+ strictcount(ControlFlow::SuccessorType t | exists(cfn.getASuccessor(t))) = 1
}
/**
* Holds if `e` is a condition that always evaluates to Boolean value `b`.
*/
predicate isConstantCondition(Expr e, boolean b) {
- forex(ControlFlow::Node cfn | cfn = e.getAControlFlowNode() | isConstantCondition0(cfn, b))
+ forex(ControlFlowNode cfn | cfn = e.getAControlFlowNode() | isConstantCondition0(cfn, b))
}
/**
diff --git a/csharp/ql/lib/semmle/code/csharp/commons/Strings.qll b/csharp/ql/lib/semmle/code/csharp/commons/Strings.qll
index bdf9e5585394..678a1f928163 100644
--- a/csharp/ql/lib/semmle/code/csharp/commons/Strings.qll
+++ b/csharp/ql/lib/semmle/code/csharp/commons/Strings.qll
@@ -49,6 +49,11 @@ class ImplicitToStringExpr extends Expr {
this = add.getOtherOperand(o).stripImplicit()
)
or
+ exists(AssignAddExpr add, Expr o | o = add.getLeftOperand() |
+ o.stripImplicit().getType() instanceof StringType and
+ this = add.getRightOperand().stripImplicit()
+ )
+ or
this = any(InterpolatedStringExpr ise).getAnInsert().stripImplicit()
}
}
diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll
deleted file mode 100644
index bf6a97728574..000000000000
--- a/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll
+++ /dev/null
@@ -1,356 +0,0 @@
-/**
- * Provides classes representing basic blocks.
- */
-
-import csharp
-private import ControlFlow
-private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl as CfgImpl
-private import CfgImpl::BasicBlocks as BasicBlocksImpl
-private import codeql.controlflow.BasicBlock as BB
-
-/**
- * A basic block, that is, a maximal straight-line sequence of control flow nodes
- * without branches or joins.
- */
-final class BasicBlock extends BasicBlocksImpl::BasicBlock {
- /** Gets an immediate successor of this basic block of a given type, if any. */
- BasicBlock getASuccessor(ControlFlow::SuccessorType t) { result = super.getASuccessor(t) }
-
- /** DEPRECATED: Use `getASuccessor` instead. */
- deprecated BasicBlock getASuccessorByType(ControlFlow::SuccessorType t) {
- result = this.getASuccessor(t)
- }
-
- /** Gets an immediate predecessor of this basic block of a given type, if any. */
- BasicBlock getAPredecessorByType(ControlFlow::SuccessorType t) {
- result = this.getAPredecessor(t)
- }
-
- /**
- * Gets an immediate `true` successor, if any.
- *
- * An immediate `true` successor is a successor that is reached when
- * the condition that ends this basic block evaluates to `true`.
- *
- * Example:
- *
- * ```csharp
- * if (x < 0)
- * x = -x;
- * ```
- *
- * The basic block on line 2 is an immediate `true` successor of the
- * basic block on line 1.
- */
- BasicBlock getATrueSuccessor() { result.getFirstNode() = this.getLastNode().getATrueSuccessor() }
-
- /**
- * Gets an immediate `false` successor, if any.
- *
- * An immediate `false` successor is a successor that is reached when
- * the condition that ends this basic block evaluates to `false`.
- *
- * Example:
- *
- * ```csharp
- * if (!(x >= 0))
- * x = -x;
- * ```
- *
- * The basic block on line 2 is an immediate `false` successor of the
- * basic block on line 1.
- */
- BasicBlock getAFalseSuccessor() {
- result.getFirstNode() = this.getLastNode().getAFalseSuccessor()
- }
-
- BasicBlock getASuccessor() { result = super.getASuccessor() }
-
- /** Gets the control flow node at a specific (zero-indexed) position in this basic block. */
- ControlFlow::Node getNode(int pos) { result = super.getNode(pos) }
-
- /** Gets a control flow node in this basic block. */
- ControlFlow::Node getANode() { result = super.getANode() }
-
- /** Gets the first control flow node in this basic block. */
- ControlFlow::Node getFirstNode() { result = super.getFirstNode() }
-
- /** Gets the last control flow node in this basic block. */
- ControlFlow::Node getLastNode() { result = super.getLastNode() }
-
- /** Gets the callable that this basic block belongs to. */
- final Callable getCallable() { result = this.getFirstNode().getEnclosingCallable() }
-
- /**
- * Holds if this basic block immediately dominates basic block `bb`.
- *
- * That is, this basic block is the unique basic block satisfying:
- * 1. This basic block strictly dominates `bb`
- * 2. There exists no other basic block that is strictly dominated by this
- * basic block and which strictly dominates `bb`.
- *
- * All basic blocks, except entry basic blocks, have a unique immediate
- * dominator.
- *
- * Example:
- *
- * ```csharp
- * int M(string s) {
- * if (s == null)
- * throw new ArgumentNullException(nameof(s));
- * return s.Length;
- * }
- * ```
- *
- * The basic block starting on line 2 strictly dominates the
- * basic block on line 4 (all paths from the entry point of `M`
- * to `return s.Length;` must go through the null check).
- */
- predicate immediatelyDominates(BasicBlock bb) { super.immediatelyDominates(bb) }
-
- /**
- * Holds if this basic block strictly dominates basic block `bb`.
- *
- * That is, all paths reaching basic block `bb` from some entry point
- * basic block must go through this basic block (which must be different
- * from `bb`).
- *
- * Example:
- *
- * ```csharp
- * int M(string s) {
- * if (s == null)
- * throw new ArgumentNullException(nameof(s));
- * return s.Length;
- * }
- * ```
- *
- * The basic block starting on line 2 strictly dominates the
- * basic block on line 4 (all paths from the entry point of `M`
- * to `return s.Length;` must go through the null check).
- */
- predicate strictlyDominates(BasicBlock bb) { super.strictlyDominates(bb) }
-
- /**
- * Holds if this basic block dominates basic block `bb`.
- *
- * That is, all paths reaching basic block `bb` from some entry point
- * basic block must go through this basic block.
- *
- * Example:
- *
- * ```csharp
- * int M(string s) {
- * if (s == null)
- * throw new ArgumentNullException(nameof(s));
- * return s.Length;
- * }
- * ```
- *
- * The basic block starting on line 2 dominates the basic
- * block on line 4 (all paths from the entry point of `M` to
- * `return s.Length;` must go through the null check).
- *
- * This predicate is *reflexive*, so for example `if (s == null)` dominates
- * itself.
- */
- predicate dominates(BasicBlock bb) {
- bb = this or
- this.strictlyDominates(bb)
- }
-
- /**
- * Holds if `df` is in the dominance frontier of this basic block.
- * That is, this basic block dominates a predecessor of `df`, but
- * does not dominate `df` itself.
- *
- * Example:
- *
- * ```csharp
- * if (x < 0) {
- * x = -x;
- * if (x > 10)
- * x--;
- * }
- * Console.Write(x);
- * ```
- *
- * The basic block on line 6 is in the dominance frontier
- * of the basic block starting on line 2 because that block
- * dominates the basic block on line 4, which is a predecessor of
- * `Console.Write(x);`. Also, the basic block starting on line 2
- * does not dominate the basic block on line 6.
- */
- predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) }
-
- /**
- * Gets the basic block that immediately dominates this basic block, if any.
- *
- * That is, the result is the unique basic block satisfying:
- * 1. The result strictly dominates this basic block.
- * 2. There exists no other basic block that is strictly dominated by the
- * result and which strictly dominates this basic block.
- *
- * All basic blocks, except entry basic blocks, have a unique immediate
- * dominator.
- *
- * Example:
- *
- * ```csharp
- * int M(string s) {
- * if (s == null)
- * throw new ArgumentNullException(nameof(s));
- * return s.Length;
- * }
- * ```
- *
- * The basic block starting on line 2 is an immediate dominator of
- * the basic block online 4 (all paths from the entry point of `M`
- * to `return s.Length;` must go through the null check.
- */
- BasicBlock getImmediateDominator() { result = super.getImmediateDominator() }
-
- /**
- * Holds if the edge with successor type `s` out of this basic block is a
- * dominating edge for `dominated`.
- *
- * That is, all paths reaching `dominated` from the entry point basic
- * block must go through the `s` edge out of this basic block.
- *
- * Edge dominance is similar to node dominance except it concerns edges
- * instead of nodes: A basic block is dominated by a _basic block_ `bb` if it
- * can only be reached through `bb` and dominated by an _edge_ `e` if it can
- * only be reached through `e`.
- *
- * Note that where all basic blocks (except the entry basic block) are
- * strictly dominated by at least one basic block, a basic block may not be
- * dominated by any edge. If an edge dominates a basic block `bb`, then
- * both endpoints of the edge dominates `bb`. The converse is not the case,
- * as there may be multiple paths between the endpoints with none of them
- * dominating.
- */
- predicate edgeDominates(BasicBlock dominated, ControlFlow::SuccessorType s) {
- super.edgeDominates(dominated, s)
- }
-
- /**
- * Holds if this basic block strictly post-dominates basic block `bb`.
- *
- * That is, all paths reaching a normal exit point basic block from basic
- * block `bb` must go through this basic block (which must be different
- * from `bb`).
- *
- * Example:
- *
- * ```csharp
- * int M(string s) {
- * try {
- * return s.Length;
- * }
- * finally {
- * Console.WriteLine("M");
- * }
- * }
- * ```
- *
- * The basic block on line 6 strictly post-dominates the basic block on
- * line 3 (all paths to the exit point of `M` from `return s.Length;`
- * must go through the `WriteLine` call).
- */
- predicate strictlyPostDominates(BasicBlock bb) { super.strictlyPostDominates(bb) }
-
- /**
- * Holds if this basic block post-dominates basic block `bb`.
- *
- * That is, all paths reaching a normal exit point basic block from basic
- * block `bb` must go through this basic block.
- *
- * Example:
- *
- * ```csharp
- * int M(string s) {
- * try {
- * return s.Length;
- * }
- * finally {
- * Console.WriteLine("M");
- * }
- * }
- * ```
- *
- * The basic block on line 6 post-dominates the basic block on line 3
- * (all paths to the exit point of `M` from `return s.Length;` must go
- * through the `WriteLine` call).
- *
- * This predicate is *reflexive*, so for example `Console.WriteLine("M");`
- * post-dominates itself.
- */
- predicate postDominates(BasicBlock bb) { super.postDominates(bb) }
-
- /**
- * Holds if this basic block is in a loop in the control flow graph. This
- * includes loops created by `goto` statements. This predicate may not hold
- * even if this basic block is syntactically inside a `while` loop if the
- * necessary back edges are unreachable.
- */
- predicate inLoop() { this.getASuccessor+() = this }
-}
-
-/**
- * An entry basic block, that is, a basic block whose first node is
- * an entry node.
- */
-final class EntryBasicBlock extends BasicBlock, BasicBlocksImpl::EntryBasicBlock { }
-
-/**
- * An annotated exit basic block, that is, a basic block that contains an
- * annotated exit node.
- */
-final class AnnotatedExitBasicBlock extends BasicBlock, BasicBlocksImpl::AnnotatedExitBasicBlock { }
-
-/**
- * An exit basic block, that is, a basic block whose last node is
- * an exit node.
- */
-final class ExitBasicBlock extends BasicBlock, BasicBlocksImpl::ExitBasicBlock { }
-
-/** A basic block with more than one predecessor. */
-final class JoinBlock extends BasicBlock, BasicBlocksImpl::JoinBasicBlock {
- JoinBlockPredecessor getJoinBlockPredecessor(int i) { result = super.getJoinBlockPredecessor(i) }
-}
-
-/** A basic block that is an immediate predecessor of a join block. */
-final class JoinBlockPredecessor extends BasicBlock, BasicBlocksImpl::JoinPredecessorBasicBlock { }
-
-/**
- * A basic block that terminates in a condition, splitting the subsequent
- * control flow.
- */
-final class ConditionBlock extends BasicBlock, BasicBlocksImpl::ConditionBasicBlock {
- /** DEPRECATED: Use `edgeDominates` instead. */
- deprecated predicate immediatelyControls(BasicBlock succ, ConditionalSuccessor s) {
- this.getASuccessor(s) = succ and
- BasicBlocksImpl::dominatingEdge(this, succ)
- }
-
- /** DEPRECATED: Use `edgeDominates` instead. */
- deprecated predicate controls(BasicBlock controlled, ConditionalSuccessor s) {
- super.edgeDominates(controlled, s)
- }
-}
-
-private class BasicBlockAlias = BasicBlock;
-
-private class EntryBasicBlockAlias = EntryBasicBlock;
-
-module Cfg implements BB::CfgSig {
- class ControlFlowNode = ControlFlow::Node;
-
- class BasicBlock = BasicBlockAlias;
-
- class EntryBasicBlock = EntryBasicBlockAlias;
-
- predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) {
- BasicBlocksImpl::dominatingEdge(bb1, bb2)
- }
-}
diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowElement.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowElement.qll
index 0d0ed6819698..a0a578e23884 100644
--- a/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowElement.qll
+++ b/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowElement.qll
@@ -4,20 +4,22 @@ import csharp
private import semmle.code.csharp.ExprOrStmtParent
private import semmle.code.csharp.commons.Compilation
private import ControlFlow
-private import ControlFlow::BasicBlocks
private import semmle.code.csharp.Caching
-private import internal.ControlFlowGraphImpl as Impl
+
+private class TControlFlowElementOrCallable = @callable or @control_flow_element;
+
+class ControlFlowElementOrCallable extends ExprOrStmtParent, TControlFlowElementOrCallable { }
/**
* A program element that can possess control flow. That is, either a statement or
* an expression.
*
- * A control flow element can be mapped to a control flow node (`ControlFlow::Node`)
+ * A control flow element can be mapped to a control flow node (`ControlFlowNode`)
* via `getAControlFlowNode()`. There is a one-to-many relationship between
* control flow elements and control flow nodes. This allows control flow
* splitting, for example modeling the control flow through `finally` blocks.
*/
-class ControlFlowElement extends ExprOrStmtParent, @control_flow_element {
+class ControlFlowElement extends ControlFlowElementOrCallable, @control_flow_element {
/** Gets the enclosing callable of this element, if any. */
Callable getEnclosingCallable() { none() }
@@ -32,34 +34,15 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element {
/**
* Gets a control flow node for this element. That is, a node in the
* control flow graph that corresponds to this element.
- *
- * Typically, there is exactly one `ControlFlow::Node` associated with a
- * `ControlFlowElement`, but a `ControlFlowElement` may be split into
- * several `ControlFlow::Node`s, for example to represent the continuation
- * flow in a `try/catch/finally` construction.
*/
- Nodes::ElementNode getAControlFlowNode() { result.getAstNode() = this }
+ ControlFlowNodes::ElementNode getAControlFlowNode() { result = this.getControlFlowNode() }
/** Gets the control flow node for this element. */
- ControlFlow::Node getControlFlowNode() { result.getAstNode() = this }
+ ControlFlowNode getControlFlowNode() { result.injects(this) }
/** Gets the basic block in which this element occurs. */
BasicBlock getBasicBlock() { result = this.getAControlFlowNode().getBasicBlock() }
- /**
- * Gets a first control flow node executed within this element.
- */
- Nodes::ElementNode getAControlFlowEntryNode() {
- result = Impl::getAControlFlowEntryNode(this).(ControlFlowElement).getAControlFlowNode()
- }
-
- /**
- * Gets a potential last control flow node executed within this element.
- */
- Nodes::ElementNode getAControlFlowExitNode() {
- result = Impl::getAControlFlowExitNode(this).(ControlFlowElement).getAControlFlowNode()
- }
-
/**
* Holds if this element is live, that is this element can be reached
* from the entry point of its enclosing callable.
@@ -86,22 +69,4 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element {
this.getAControlFlowNode().getBasicBlock().getASuccessor+().getANode() =
result.getAControlFlowNode()
}
-
- /**
- * DEPRECATED: Use `Guard` class instead.
- *
- * Holds if basic block `controlled` is controlled by this control flow element
- * with conditional value `s`. That is, `controlled` can only be reached from
- * the callable entry point by going via the `s` edge out of *some* basic block
- * ending with this element.
- *
- * `cb` records all of the possible condition blocks for this control flow element
- * that a path from the callable entry point to `controlled` may go through.
- */
- deprecated predicate controlsBlock(
- BasicBlock controlled, ConditionalSuccessor s, ConditionBlock cb
- ) {
- cb.getLastNode() = this.getAControlFlowNode() and
- cb.edgeDominates(controlled, s)
- }
}
diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowGraph.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowGraph.qll
index 438174fe2970..56f558d74666 100644
--- a/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowGraph.qll
+++ b/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowGraph.qll
@@ -1,315 +1,550 @@
-import csharp
-
/**
* Provides classes representing the control flow graph within callables.
*/
-module ControlFlow {
- private import semmle.code.csharp.controlflow.BasicBlocks as BBs
- import semmle.code.csharp.controlflow.internal.SuccessorType
- private import internal.ControlFlowGraphImpl as Impl
- private import internal.Splitting as Splitting
- /**
- * A control flow node.
- *
- * Either a callable entry node (`EntryNode`), a callable exit node (`ExitNode`),
- * or a control flow node for a control flow element, that is, an expression or a
- * statement (`ElementNode`).
- *
- * A control flow node is a node in the control flow graph (CFG). There is a
- * many-to-one relationship between `ElementNode`s and `ControlFlowElement`s.
- * This allows control flow splitting, for example modeling the control flow
- * through `finally` blocks.
- *
- * Only nodes that can be reached from the callable entry point are included in
- * the CFG.
- */
- class Node extends Impl::Node {
- /** Gets the control flow element that this node corresponds to, if any. */
- final ControlFlowElement getAstNode() { result = super.getAstNode() }
-
- /** Gets the basic block that this control flow node belongs to. */
- BasicBlock getBasicBlock() { result.getANode() = this }
-
- /**
- * Holds if this node dominates `that` node.
- *
- * That is, all paths reaching `that` node from some callable entry
- * node (`EntryNode`) must go through this node.
- *
- * Example:
- *
- * ```csharp
- * int M(string s)
- * {
- * if (s == null)
- * throw new ArgumentNullException(nameof(s));
- * return s.Length;
- * }
- * ```
- *
- * The node on line 3 dominates the node on line 5 (all paths from the
- * entry point of `M` to `return s.Length;` must go through the null check).
- *
- * This predicate is *reflexive*, so for example `if (s == null)` dominates
- * itself.
- */
- // potentially very large predicate, so must be inlined
- pragma[inline]
- predicate dominates(Node that) {
- this.strictlyDominates(that)
- or
- this = that
- }
+import csharp
+private import codeql.controlflow.ControlFlowGraph
+private import codeql.controlflow.SuccessorType
+private import semmle.code.csharp.commons.Compilation
+private import semmle.code.csharp.controlflow.internal.NonReturning as NonReturning
+private import semmle.code.csharp.controlflow.internal.Completion as Completion
+
+private module Cfg0 = Make0;
+
+private module Cfg1 = Make1;
+
+private module Cfg2 = Make2;
+
+private import Cfg0
+private import Cfg1
+private import Cfg2
+import Public
+
+/** Provides an implementation of the AST signature for C#. */
+private module Ast implements AstSig {
+ private import csharp as CS
+
+ class AstNode = ControlFlowElementOrCallable;
+
+ private AstNode getExprChild0(Expr e, int i) {
+ not e instanceof NameOfExpr and
+ not e instanceof QualifiableExpr and
+ not e instanceof AnonymousFunctionExpr and
+ result = e.getChild(i)
+ or
+ e = any(ExtensionMethodCall emc | result = emc.getArgument(i))
+ or
+ e =
+ any(QualifiableExpr qe |
+ not qe instanceof ExtensionMethodCall and
+ result = qe.getChild(i)
+ )
+ }
- /**
- * Holds if this node strictly dominates `that` node.
- *
- * That is, all paths reaching `that` node from some callable entry
- * node (`EntryNode`) must go through this node (which must
- * be different from `that` node).
- *
- * Example:
- *
- * ```csharp
- * int M(string s)
- * {
- * if (s == null)
- * throw new ArgumentNullException(nameof(s));
- * return s.Length;
- * }
- * ```
- *
- * The node on line 3 strictly dominates the node on line 5
- * (all paths from the entry point of `M` to `return s.Length;` must go
- * through the null check).
- */
- // potentially very large predicate, so must be inlined
- pragma[inline]
- predicate strictlyDominates(Node that) {
- this.getBasicBlock().strictlyDominates(that.getBasicBlock())
- or
- exists(BasicBlock bb, int i, int j |
- bb.getNode(i) = this and
- bb.getNode(j) = that and
- i < j
+ private AstNode getStmtChild0(Stmt s, int i) {
+ not s instanceof FixedStmt and
+ not s instanceof UsingBlockStmt and
+ result = s.getChild(i)
+ or
+ s =
+ any(FixedStmt fs |
+ result = fs.getVariableDeclExpr(i)
+ or
+ result = fs.getBody() and
+ i = max(int j | exists(fs.getVariableDeclExpr(j))) + 1
)
- }
+ or
+ s =
+ any(UsingBlockStmt us |
+ result = us.getExpr() and
+ i = 0
+ or
+ result = us.getVariableDeclExpr(i)
+ or
+ result = us.getBody() and
+ i = max([1, count(us.getVariableDeclExpr(_))])
+ )
+ }
- /**
- * Holds if this node post-dominates `that` node.
- *
- * That is, all paths reaching a normal callable exit node (an `AnnotatedExitNode`
- * with a normal exit type) from `that` node must go through this node.
- *
- * Example:
- *
- * ```csharp
- * int M(string s)
- * {
- * try
- * {
- * return s.Length;
- * }
- * finally
- * {
- * Console.WriteLine("M");
- * }
- * }
- * ```
- *
- * The node on line 9 post-dominates the node on line 5 (all paths to the
- * exit point of `M` from `return s.Length;` must go through the `WriteLine`
- * call).
- *
- * This predicate is *reflexive*, so for example `Console.WriteLine("M");`
- * post-dominates itself.
- */
- // potentially very large predicate, so must be inlined
- pragma[inline]
- predicate postDominates(Node that) {
- this.strictlyPostDominates(that)
- or
- this = that
- }
+ AstNode getChild(AstNode n, int index) {
+ result = getStmtChild0(n, index)
+ or
+ result = getExprChild0(n, index)
+ }
- /**
- * Holds if this node strictly post-dominates `that` node.
- *
- * That is, all paths reaching a normal callable exit node (an `AnnotatedExitNode`
- * with a normal exit type) from `that` node must go through this node
- * (which must be different from `that` node).
- *
- * Example:
- *
- * ```csharp
- * int M(string s)
- * {
- * try
- * {
- * return s.Length;
- * }
- * finally
- * {
- * Console.WriteLine("M");
- * }
- * }
- * ```
- *
- * The node on line 9 strictly post-dominates the node on line 5 (all
- * paths to the exit point of `M` from `return s.Length;` must go through
- * the `WriteLine` call).
- */
- // potentially very large predicate, so must be inlined
- pragma[inline]
- predicate strictlyPostDominates(Node that) {
- this.getBasicBlock().strictlyPostDominates(that.getBasicBlock())
- or
- exists(BasicBlock bb, int i, int j |
- bb.getNode(i) = this and
- bb.getNode(j) = that and
- i > j
- )
- }
+ private AstNode getParent(AstNode n) { n = getChild(result, _) }
- /** Gets a successor node of a given type, if any. */
- Node getASuccessorByType(SuccessorType t) { result = this.getASuccessor(t) }
-
- /** Gets an immediate successor, if any. */
- Node getASuccessor() { result = this.getASuccessorByType(_) }
-
- /** Gets an immediate predecessor node of a given flow type, if any. */
- Node getAPredecessorByType(SuccessorType t) { result.getASuccessorByType(t) = this }
-
- /** Gets an immediate predecessor, if any. */
- Node getAPredecessor() { result = this.getAPredecessorByType(_) }
-
- /**
- * Gets an immediate `true` successor, if any.
- *
- * An immediate `true` successor is a successor that is reached when
- * this condition evaluates to `true`.
- *
- * Example:
- *
- * ```csharp
- * if (x < 0)
- * x = -x;
- * ```
- *
- * The node on line 2 is an immediate `true` successor of the node
- * on line 1.
- */
- Node getATrueSuccessor() {
- result = this.getASuccessorByType(any(BooleanSuccessor t | t.getValue() = true))
- }
+ Callable getEnclosingCallable(AstNode node) {
+ result = node.(ControlFlowElement).getEnclosingCallable() or
+ result.(ObjectInitMethod).initializes(getParent*(node)) or
+ Initializers::staticMemberInitializer(result, getParent*(node))
+ }
- /**
- * Gets an immediate `false` successor, if any.
- *
- * An immediate `false` successor is a successor that is reached when
- * this condition evaluates to `false`.
- *
- * Example:
- *
- * ```csharp
- * if (!(x >= 0))
- * x = -x;
- * ```
- *
- * The node on line 2 is an immediate `false` successor of the node
- * on line 1.
- */
- Node getAFalseSuccessor() {
- result = this.getASuccessorByType(any(BooleanSuccessor t | t.getValue() = false))
- }
+ class Callable = CS::Callable;
- /** Gets the enclosing callable of this control flow node. */
- final Callable getEnclosingCallable() { result = Impl::getNodeCfgScope(this) }
+ AstNode callableGetBody(Callable c) {
+ result = c.getBody() or
+ result = c.(Constructor).getObjectInitializerCall() or
+ result = c.(Constructor).getInitializer() or
+ c.(ObjectInitMethod).initializes(result) or
+ Initializers::staticMemberInitializer(c, result)
}
- /** Provides different types of control flow nodes. */
- module Nodes {
- /** A node for a callable entry point. */
- class EntryNode extends Node instanceof Impl::EntryNode {
- /** Gets the callable that this entry applies to. */
- Callable getCallable() { result = this.getScope() }
+ class Stmt = CS::Stmt;
- override BasicBlocks::EntryBlock getBasicBlock() { result = Node.super.getBasicBlock() }
- }
+ class Expr = CS::Expr;
- /** A node for a callable exit point, annotated with the type of exit. */
- class AnnotatedExitNode extends Node instanceof Impl::AnnotatedExitNode {
- /** Holds if this node represent a normal exit. */
- final predicate isNormal() { super.isNormal() }
+ class BlockStmt = CS::BlockStmt;
- /** Gets the callable that this exit applies to. */
- Callable getCallable() { result = this.getScope() }
+ class ExprStmt = CS::ExprStmt;
- override BasicBlocks::AnnotatedExitBlock getBasicBlock() {
- result = Node.super.getBasicBlock()
- }
- }
+ class IfStmt = CS::IfStmt;
- /** A control flow node indicating normal termination of a callable. */
- class NormalExitNode extends AnnotatedExitNode instanceof Impl::NormalExitNode { }
+ class LoopStmt = CS::LoopStmt;
- /** A node for a callable exit point. */
- class ExitNode extends Node instanceof Impl::ExitNode {
- /** Gets the callable that this exit applies to. */
- Callable getCallable() { result = this.getScope() }
+ class WhileStmt = CS::WhileStmt;
- override BasicBlocks::ExitBlock getBasicBlock() { result = Node.super.getBasicBlock() }
- }
+ class DoStmt = CS::DoStmt;
+
+ final private class FinalForStmt = CS::ForStmt;
- /**
- * A node for a control flow element, that is, an expression or a statement.
- *
- * Each control flow element maps to zero or more `ElementNode`s: zero when
- * the element is in unreachable (dead) code, and multiple when there are
- * different splits for the element.
- */
- class ElementNode extends Node instanceof Impl::AstCfgNode {
- /** Gets a comma-separated list of strings for each split in this node, if any. */
- final string getSplitsString() { result = super.getSplitsString() }
-
- /** Gets a split for this control flow node, if any. */
- final Split getASplit() { result = super.getASplit() }
+ class ForStmt extends FinalForStmt {
+ Expr getInit(int index) { result = this.getInitializer(index) }
+ }
+
+ final private class FinalForeachStmt = CS::ForeachStmt;
+
+ class ForeachStmt extends FinalForeachStmt {
+ Expr getVariable() {
+ result = this.getVariableDeclExpr() or result = this.getVariableDeclTuple()
}
- /** A control-flow node for an expression. */
- class ExprNode extends ElementNode {
- Expr e;
+ Expr getCollection() { result = this.getIterableExpr() }
+ }
+
+ class BreakStmt = CS::BreakStmt;
+
+ class ContinueStmt = CS::ContinueStmt;
+
+ class GotoStmt = CS::GotoStmt;
+
+ class ReturnStmt = CS::ReturnStmt;
+
+ class Throw = CS::ThrowElement;
+
+ final private class FinalTryStmt = CS::TryStmt;
+
+ class TryStmt extends FinalTryStmt {
+ Stmt getBody() { result = this.getBlock() }
+
+ CatchClause getCatch(int index) { result = this.getCatchClause(index) }
+
+ Stmt getFinally() { result = super.getFinally() }
+ }
+
+ final private class FinalCatchClause = CS::CatchClause;
+
+ class CatchClause extends FinalCatchClause {
+ AstNode getVariable() { result = this.(CS::SpecificCatchClause).getVariableDeclExpr() }
+
+ Expr getCondition() { result = this.getFilterClause() }
+
+ Stmt getBody() { result = this.getBlock() }
+ }
+
+ final private class FinalSwitch = CS::Switch;
+
+ class Switch extends FinalSwitch {
+ Case getCase(int index) { result = super.getCase(index) }
+
+ Stmt getStmt(int index) { result = this.(CS::SwitchStmt).getStmt(index) }
+ }
+
+ final private class FinalCase = CS::Case;
+
+ class Case extends FinalCase {
+ AstNode getAPattern() { result = this.getPattern() }
+
+ Expr getGuard() { result = this.getCondition() }
+
+ AstNode getBody() { result = super.getBody() }
+ }
+
+ class DefaultCase extends Case instanceof CS::DefaultCase { }
+
+ class ConditionalExpr = CS::ConditionalExpr;
+
+ class BinaryExpr = CS::BinaryOperation;
+
+ class LogicalAndExpr = CS::LogicalAndExpr;
+
+ class LogicalOrExpr = CS::LogicalOrExpr;
+
+ class NullCoalescingExpr = CS::NullCoalescingExpr;
+
+ class UnaryExpr = CS::UnaryOperation;
+
+ class LogicalNotExpr = CS::LogicalNotExpr;
+
+ class Assignment = CS::Assignment;
+
+ class AssignExpr = CS::AssignExpr;
+
+ class CompoundAssignment = CS::AssignOperation;
+
+ class AssignLogicalAndExpr extends CompoundAssignment {
+ AssignLogicalAndExpr() { none() }
+ }
+
+ class AssignLogicalOrExpr extends CompoundAssignment {
+ AssignLogicalOrExpr() { none() }
+ }
+
+ class AssignNullCoalescingExpr = CS::AssignCoalesceExpr;
+
+ final private class FinalBoolLiteral = CS::BoolLiteral;
+
+ class BooleanLiteral extends FinalBoolLiteral {
+ boolean getValue() { result = this.getBoolValue() }
+ }
+
+ final private class FinalIsExpr = CS::IsExpr;
+
+ class PatternMatchExpr extends FinalIsExpr {
+ AstNode getPattern() { result = super.getPattern() }
+ }
+}
+
+/**
+ * A compilation.
+ *
+ * Unlike the standard `Compilation` class, this class also supports buildless
+ * extraction.
+ */
+private newtype CompilationExt =
+ TCompilation(Compilation c) { not extractionIsStandalone() } or
+ TBuildless() { extractionIsStandalone() }
+
+/** Gets the compilation that source file `f` belongs to. */
+private CompilationExt getCompilation(File f) {
+ exists(Compilation c |
+ f = c.getAFileCompiled() and
+ result = TCompilation(c)
+ )
+ or
+ result = TBuildless()
+}
+
+private module Initializers {
+ private import semmle.code.csharp.ExprOrStmtParent as ExprOrStmtParent
+
+ /**
+ * The `expr_parent_top_level_adjusted()` relation restricted to exclude relations
+ * between properties and their getters' expression bodies in properties such as
+ * `int P => 0`.
+ *
+ * This is in order to only associate the expression body with one CFG scope, namely
+ * the getter (and not the declaration itself).
+ */
+ private predicate expr_parent_top_level_adjusted2(
+ Expr child, int i, @top_level_exprorstmt_parent parent
+ ) {
+ ExprOrStmtParent::expr_parent_top_level_adjusted(child, i, parent) and
+ not exists(Getter g |
+ g.getDeclaration() = parent and
+ i = 0
+ )
+ }
+
+ /**
+ * Holds if `init` is a static member initializer and `staticCtor` is the
+ * static constructor in the same declaring type. Hence, `staticCtor` can be
+ * considered to execute `init` prior to the execution of its body.
+ */
+ predicate staticMemberInitializer(Constructor staticCtor, Expr init) {
+ exists(Assignable a |
+ a.(Modifiable).isStatic() and
+ expr_parent_top_level_adjusted2(init, _, a) and
+ a.getDeclaringType() = staticCtor.getDeclaringType() and
+ staticCtor.isStatic()
+ )
+ }
+
+ /**
+ * Gets the `i`th static member initializer expression for static constructor `staticCtor`.
+ */
+ Expr initializedStaticMemberOrder(Constructor staticCtor, int i) {
+ result =
+ rank[i + 1](Expr init, Location l |
+ staticMemberInitializer(staticCtor, init) and
+ l = init.getLocation()
+ |
+ init order by l.getStartLine(), l.getStartColumn(), l.getFile().getAbsolutePath()
+ )
+ }
- ExprNode() { e = unique(Expr e_ | e_ = this.getAstNode() | e_) }
+ /**
+ * Gets the `i`th member initializer expression for object initializer method `obinit`
+ * in compilation `comp`.
+ */
+ AssignExpr initializedInstanceMemberOrder(ObjectInitMethod obinit, CompilationExt comp, int i) {
+ obinit.initializes(result) and
+ result =
+ rank[i + 1](AssignExpr ae0, Location l |
+ obinit.initializes(ae0) and
+ l = ae0.getLocation() and
+ getCompilation(l.getFile()) = comp
+ |
+ ae0 order by l.getStartLine(), l.getStartColumn(), l.getFile().getAbsolutePath()
+ )
+ }
- /** Gets the expression that this control-flow node belongs to. */
- Expr getExpr() { result = e }
+ /**
+ * Gets the last member initializer expression for object initializer method `obinit`
+ * in compilation `comp`.
+ */
+ AssignExpr lastInitializer(ObjectInitMethod obinit, CompilationExt comp) {
+ exists(int i |
+ result = initializedInstanceMemberOrder(obinit, comp, i) and
+ not exists(initializedInstanceMemberOrder(obinit, comp, i + 1))
+ )
+ }
+}
+
+private module Input implements InputSig1, InputSig2 {
+ predicate cfgCachedStageRef() { CfgCachedStage::ref() }
+
+ predicate catchAll(Ast::CatchClause catch) { catch instanceof GeneralCatchClause }
+
+ predicate matchAll(Ast::Case c) { c instanceof DefaultCase or c.(SwitchCaseExpr).matchesAll() }
- /** Gets the value of this expression node, if any. */
- string getValue() { result = e.getValue() }
+ private newtype TLabel =
+ TLblGoto(string label) { any(GotoLabelStmt goto).getLabel() = label } or
+ TLblSwitchCase(string value) { any(GotoCaseStmt goto).getLabel() = value } or
+ TLblSwitchDefault()
- /** Gets the type of this expression node. */
- Type getType() { result = e.getType() }
+ class Label extends TLabel {
+ string toString() {
+ this = TLblGoto(result)
+ or
+ this = TLblSwitchCase(result)
+ or
+ this = TLblSwitchDefault() and result = "default"
}
+ }
+
+ predicate hasLabel(Ast::AstNode n, Label l) {
+ l = TLblGoto(n.(GotoLabelStmt).getLabel())
+ or
+ l = TLblSwitchCase(n.(GotoCaseStmt).getLabel())
+ or
+ l = TLblSwitchDefault() and n instanceof GotoDefaultStmt
+ or
+ l = TLblGoto(n.(LabelStmt).getLabel())
+ }
+
+ predicate inConditionalContext(Ast::AstNode n, ConditionKind kind) {
+ kind.isNullness() and
+ exists(QualifiableExpr qe | qe.isConditional() |
+ n = qe.getQualifier() or qe.(ExtensionMethodCall).getArgument(0) = n
+ )
+ }
+
+ predicate postOrInOrder(Ast::AstNode n) { n instanceof YieldBreakStmt or n instanceof Call }
+
+ predicate beginAbruptCompletion(
+ Ast::AstNode ast, PreControlFlowNode n, AbruptCompletion c, boolean always
+ ) {
+ // `yield break` behaves like a return statement
+ ast instanceof YieldBreakStmt and
+ n.isIn(ast) and
+ c.asSimpleAbruptCompletion() instanceof ReturnSuccessor and
+ always = true
+ or
+ Completion::mayThrowException(ast) and
+ n.isIn(ast) and
+ c.asSimpleAbruptCompletion() instanceof ExceptionSuccessor and
+ always = false
+ or
+ ast instanceof NonReturning::NonReturningCall and
+ n.isIn(ast) and
+ c.asSimpleAbruptCompletion() instanceof ExceptionSuccessor and
+ always = true
+ }
- class Split = Splitting::Split;
+ predicate endAbruptCompletion(Ast::AstNode ast, PreControlFlowNode n, AbruptCompletion c) {
+ exists(SwitchStmt switch, Label l, Ast::Case case |
+ ast.(Stmt).getParent() = switch and
+ c.getSuccessorType() instanceof GotoSuccessor and
+ c.hasLabel(l) and
+ n.isAfterValue(case, any(MatchingSuccessor t | t.getValue() = true))
+ |
+ exists(string value, ConstCase cc |
+ l = TLblSwitchCase(value) and
+ switch.getAConstCase() = cc and
+ cc.getLabel() = value and
+ cc = case
+ )
+ or
+ l = TLblSwitchDefault() and switch.getDefaultCase() = case
+ )
}
- class BasicBlock = BBs::BasicBlock;
+ pragma[noinline]
+ private MethodCall getObjectInitializerCall(Constructor ctor, CompilationExt comp) {
+ result = ctor.getObjectInitializerCall() and
+ comp = getCompilation(result.getFile())
+ }
+
+ pragma[noinline]
+ private ConstructorInitializer getInitializer(Constructor ctor, CompilationExt comp) {
+ result = ctor.getInitializer() and
+ comp = getCompilation(result.getFile())
+ }
+
+ pragma[noinline]
+ private Ast::AstNode getBody(Constructor ctor, CompilationExt comp) {
+ result = ctor.getBody() and
+ comp = getCompilation(result.getFile())
+ }
+
+ predicate step(PreControlFlowNode n1, PreControlFlowNode n2) {
+ exists(Constructor ctor |
+ n1.(EntryNodeImpl).getEnclosingCallable() = ctor and
+ if Initializers::staticMemberInitializer(ctor, _)
+ then n2.isBefore(Initializers::initializedStaticMemberOrder(ctor, 0))
+ else
+ if exists(ctor.getObjectInitializerCall())
+ then n2.isBefore(ctor.getObjectInitializerCall())
+ else
+ if exists(ctor.getInitializer())
+ then n2.isBefore(ctor.getInitializer())
+ else n2.isBefore(ctor.getBody())
+ or
+ exists(int i | n1.isAfter(Initializers::initializedStaticMemberOrder(ctor, i)) |
+ n2.isBefore(Initializers::initializedStaticMemberOrder(ctor, i + 1))
+ or
+ not exists(Initializers::initializedStaticMemberOrder(ctor, i + 1)) and
+ n2.isBefore(ctor.getBody())
+ )
+ or
+ exists(CompilationExt comp |
+ n1.isAfter(getObjectInitializerCall(ctor, comp)) and
+ if exists(getInitializer(ctor, comp))
+ then n2.isBefore(getInitializer(ctor, comp))
+ else
+ // This is only relevant in the context of compilation errors, since
+ // normally the existence of an object initializer call implies the
+ // existence of an initializer.
+ if exists(getBody(ctor, comp))
+ then n2.isBefore(getBody(ctor, comp))
+ else n2.(NormalExitNodeImpl).getEnclosingCallable() = ctor
+ or
+ n1.isAfter(getInitializer(ctor, comp)) and
+ if exists(getBody(ctor, comp))
+ then n2.isBefore(getBody(ctor, comp))
+ else n2.(NormalExitNodeImpl).getEnclosingCallable() = ctor
+ )
+ or
+ n1.isAfter(ctor.getBody()) and
+ n2.(NormalExitNodeImpl).getEnclosingCallable() = ctor
+ )
+ or
+ exists(ObjectInitMethod obinit |
+ n1.(EntryNodeImpl).getEnclosingCallable() = obinit and
+ n2.isBefore(Initializers::initializedInstanceMemberOrder(obinit, _, 0))
+ or
+ exists(CompilationExt comp, int i |
+ // Flow from one member initializer to the next
+ n1.isAfter(Initializers::initializedInstanceMemberOrder(obinit, comp, i)) and
+ n2.isBefore(Initializers::initializedInstanceMemberOrder(obinit, comp, i + 1))
+ )
+ or
+ n1.isAfter(Initializers::lastInitializer(obinit, _)) and
+ n2.(NormalExitNodeImpl).getEnclosingCallable() = obinit
+ )
+ or
+ exists(QualifiableExpr qe | qe.isConditional() |
+ n1.isBefore(qe) and n2.isBefore(qe.getQualifier())
+ or
+ exists(NullnessSuccessor t | n1.isAfterValue(qe.getQualifier(), t) |
+ if t.isNull()
+ then (
+ // if `q` is null in `q?.f = x` then the assignment is skipped. This
+ // holds for both regular, compound, and null-coalescing assignments.
+ // On the other hand, the CFG definition for the assignment can treat
+ // the LHS the same regardless of whether it's a conditionally
+ // qualified access or not, as it just connects to the "before" and
+ // "after" nodes of the LHS, and the "after" node is skipped in this
+ // case.
+ exists(AssignableDefinition def |
+ def.getTargetAccess() = qe and
+ n2.isAfterValue(def.getExpr(), t)
+ )
+ or
+ not qe instanceof AssignableWrite and
+ n2.isAfterValue(qe, t)
+ ) else (
+ n2.isBefore(Ast::getChild(qe, 1))
+ or
+ n2.isIn(qe) and not exists(Ast::getChild(qe, 1))
+ )
+ )
+ or
+ exists(int i | i >= 1 and n1.isAfter(Ast::getChild(qe, i)) |
+ n2.isBefore(Ast::getChild(qe, i + 1))
+ or
+ not exists(Ast::getChild(qe, i + 1)) and n2.isIn(qe)
+ )
+ or
+ n1.isIn(qe) and n2.isAfter(qe) and not beginAbruptCompletion(qe, n1, _, true)
+ )
+ or
+ exists(ObjectCreation oc |
+ n1.isBefore(oc) and n2.isBefore(oc.getArgument(0))
+ or
+ n1.isBefore(oc) and n2.isIn(oc) and not exists(oc.getAnArgument())
+ or
+ exists(int i | n1.isAfter(oc.getArgument(i)) |
+ n2.isBefore(oc.getArgument(i + 1))
+ or
+ not exists(oc.getArgument(i + 1)) and n2.isIn(oc)
+ )
+ or
+ n1.isIn(oc) and n2.isBefore(oc.getInitializer())
+ or
+ n1.isIn(oc) and n2.isAfter(oc) and not exists(oc.getInitializer())
+ or
+ n1.isAfter(oc.getInitializer()) and n2.isAfter(oc)
+ )
+ }
+}
- /** Provides different types of basic blocks. */
- module BasicBlocks {
- class EntryBlock = BBs::EntryBasicBlock;
+/** Provides different types of control flow nodes. */
+module ControlFlowNodes {
+ /**
+ * A node for a control flow element, that is, an expression or a statement.
+ *
+ * Each control flow element maps to zero or one `ElementNode`s: zero when
+ * the element is in unreachable (dead) code, and otherwise one.
+ */
+ class ElementNode extends ControlFlowNode {
+ ElementNode() { exists(this.asExpr()) or exists(this.asStmt()) }
+ }
- class AnnotatedExitBlock = BBs::AnnotatedExitBasicBlock;
+ /** A control-flow node for an expression. */
+ class ExprNode extends ElementNode {
+ Expr e;
- class ExitBlock = BBs::ExitBasicBlock;
+ ExprNode() { e = this.asExpr() }
- class JoinBlock = BBs::JoinBlock;
+ /** Gets the expression that this control-flow node belongs to. */
+ Expr getExpr() { result = e }
- class JoinBlockPredecessor = BBs::JoinBlockPredecessor;
+ /** Gets the value of this expression node, if any. */
+ string getValue() { result = e.getValue() }
- class ConditionBlock = BBs::ConditionBlock;
+ /** Gets the type of this expression node. */
+ Type getType() { result = e.getType() }
}
}
diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowReachability.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowReachability.qll
index aafe14402c7f..33d96a61fc7e 100644
--- a/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowReachability.qll
+++ b/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowReachability.qll
@@ -4,16 +4,13 @@
import csharp
private import codeql.controlflow.ControlFlowReachability
-private import semmle.code.csharp.controlflow.BasicBlocks
private import semmle.code.csharp.controlflow.Guards as Guards
private import semmle.code.csharp.ExprOrStmtParent
-private module ControlFlowInput implements
- InputSig
-{
+private module ControlFlowInput implements InputSig {
private import csharp as CS
- AstNode getEnclosingAstNode(ControlFlow::Node node) {
+ AstNode getEnclosingAstNode(ControlFlowNode node) {
node.getAstNode() = result
or
not exists(node.getAstNode()) and result = node.getEnclosingCallable()
diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll
index 40ec3722edd2..3a6a7e52a15c 100644
--- a/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll
+++ b/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll
@@ -7,7 +7,6 @@ private import ControlFlow
private import semmle.code.csharp.commons.Assertions
private import semmle.code.csharp.commons.ComparisonTest
private import semmle.code.csharp.commons.StructuralComparison as SC
-private import semmle.code.csharp.controlflow.BasicBlocks
private import semmle.code.csharp.controlflow.internal.Completion
private import semmle.code.csharp.frameworks.System
private import semmle.code.csharp.frameworks.system.Linq
@@ -15,12 +14,10 @@ private import semmle.code.csharp.frameworks.system.Collections
private import semmle.code.csharp.frameworks.system.collections.Generic
private import codeql.controlflow.Guards as SharedGuards
-private module GuardsInput implements
- SharedGuards::InputSig
-{
+private module GuardsInput implements SharedGuards::InputSig {
private import csharp as CS
- class NormalExitNode = ControlFlow::Nodes::NormalExitNode;
+ class NormalExitNode = ControlFlow::NormalExitNode;
class AstNode = ControlFlowElement;
@@ -96,21 +93,14 @@ private module GuardsInput implements
ConstantExpr asConstantCase() { super.getPattern() = result }
- private predicate hasEdge(BasicBlock bb1, BasicBlock bb2, MatchingCompletion c) {
- exists(PatternExpr pe |
- super.getPattern() = pe and
- c.isValidFor(pe) and
- bb1.getLastNode() = pe.getAControlFlowNode() and
- bb1.getASuccessor(c.getAMatchingSuccessorType()) = bb2
- )
- }
-
predicate matchEdge(BasicBlock bb1, BasicBlock bb2) {
- exists(MatchingCompletion c | this.hasEdge(bb1, bb2, c) and c.isMatch())
+ bb1.getASuccessor(any(MatchingSuccessor s | s.getValue() = true)) = bb2 and
+ bb1.getLastNode() = AstNode.super.getControlFlowNode()
}
predicate nonMatchEdge(BasicBlock bb1, BasicBlock bb2) {
- exists(MatchingCompletion c | this.hasEdge(bb1, bb2, c) and c.isNonMatch())
+ bb1.getASuccessor(any(MatchingSuccessor s | s.getValue() = false)) = bb2 and
+ bb1.getLastNode() = AstNode.super.getControlFlowNode()
}
}
@@ -119,14 +109,14 @@ private module GuardsInput implements
class AndExpr extends BinExpr {
AndExpr() {
this instanceof LogicalAndExpr or
- this instanceof BitwiseAndExpr
+ this instanceof BitwiseAndOperation
}
}
class OrExpr extends BinExpr {
OrExpr() {
this instanceof LogicalOrExpr or
- this instanceof BitwiseOrExpr
+ this instanceof BitwiseOrOperation
}
}
@@ -292,7 +282,7 @@ private module LogicInput implements GuardsImpl::LogicInputSig {
v1.isNonNullValue() and
v2 = v1
or
- g2 = g1.(NullCoalescingExpr).getAnOperand() and
+ g2 = g1.(NullCoalescingOperation).getAnOperand() and
v1.isNullValue() and
v2 = v1
or
@@ -322,7 +312,7 @@ class Guard extends Guards::Guard {
* In case `cfn` or `sub` access an SSA variable in their left-most qualifier, then
* so must the other (accessing the same SSA variable).
*/
- predicate controlsNode(ControlFlow::Nodes::ElementNode cfn, AccessOrCallExpr sub, GuardValue v) {
+ predicate controlsNode(ControlFlowNodes::ElementNode cfn, AccessOrCallExpr sub, GuardValue v) {
isGuardedByNode(cfn, this, sub, v)
}
@@ -332,7 +322,7 @@ class Guard extends Guards::Guard {
* Note: This predicate is inlined.
*/
pragma[inline]
- predicate controlsNode(ControlFlow::Nodes::ElementNode cfn, GuardValue v) {
+ predicate controlsNode(ControlFlowNodes::ElementNode cfn, GuardValue v) {
guardControls(this, cfn.getBasicBlock(), v)
}
@@ -450,7 +440,8 @@ class DereferenceableExpr extends Expr {
predicate guardSuggestsMaybeNull(Guards::Guard guard) {
not nonNullValueImplied(this) and
(
- exists(NullnessCompletion c | c.isValidFor(this) and c.isNull() and guard = this)
+ exists(guard.getControlFlowNode().getASuccessor(any(NullnessSuccessor n | n.isNull()))) and
+ guard = this
or
LogicInput::additionalNullCheck(guard, _, this, true)
or
@@ -605,7 +596,7 @@ class AccessOrCallExpr extends Expr {
* An expression can have more than one SSA qualifier in the presence
* of control flow splitting.
*/
- Ssa::Definition getAnSsaQualifier(ControlFlow::Node cfn) { result = getAnSsaQualifier(this, cfn) }
+ Ssa::Definition getAnSsaQualifier(ControlFlowNode cfn) { result = getAnSsaQualifier(this, cfn) }
}
private Declaration getDeclarationTarget(Expr e) {
@@ -613,14 +604,14 @@ private Declaration getDeclarationTarget(Expr e) {
result = e.(Call).getTarget()
}
-private Ssa::Definition getAnSsaQualifier(Expr e, ControlFlow::Node cfn) {
+private Ssa::Definition getAnSsaQualifier(Expr e, ControlFlowNode cfn) {
e = getATrackedAccess(result, cfn)
or
not e = getATrackedAccess(_, _) and
result = getAnSsaQualifier(e.(QualifiableExpr).getQualifier(), cfn)
}
-private AssignableAccess getATrackedAccess(Ssa::Definition def, ControlFlow::Node cfn) {
+private AssignableAccess getATrackedAccess(Ssa::Definition def, ControlFlowNode cfn) {
result = def.getAReadAtNode(cfn)
or
result = def.(Ssa::ExplicitDefinition).getADefinition().getTargetAccess() and
@@ -729,7 +720,7 @@ class GuardedExpr extends AccessOrCallExpr {
* In the example above, the node for `x.ToString()` is null-guarded in the
* split `b == true`, but not in the split `b == false`.
*/
-class GuardedControlFlowNode extends ControlFlow::Nodes::ElementNode {
+class GuardedControlFlowNode extends ControlFlowNodes::ElementNode {
private Guard g;
private AccessOrCallExpr sub0;
private GuardValue v0;
@@ -785,7 +776,7 @@ class GuardedDataFlowNode extends DataFlow::ExprNode {
private GuardValue v0;
GuardedDataFlowNode() {
- exists(ControlFlow::Nodes::ElementNode cfn | exists(this.getExprAtNode(cfn)) |
+ exists(ControlFlowNodes::ElementNode cfn | exists(this.getExprAtNode(cfn)) |
g.controlsNode(cfn, sub0, v0)
)
}
@@ -840,14 +831,14 @@ module Internal {
or
e1 = e2.(Cast).getExpr()
or
- e2 = e1.(NullCoalescingExpr).getAnOperand()
+ e2 = e1.(NullCoalescingOperation).getAnOperand()
}
/** Holds if expression `e3` is a `null` value whenever `e1` and `e2` are. */
predicate nullValueImpliedBinary(Expr e1, Expr e2, Expr e3) {
e3 = any(ConditionalExpr ce | e1 = ce.getThen() and e2 = ce.getElse())
or
- e3 = any(NullCoalescingExpr nce | e1 = nce.getLeftOperand() and e2 = nce.getRightOperand())
+ e3 = any(NullCoalescingOperation no | e1 = no.getLeftOperand() and e2 = no.getRightOperand())
}
predicate nullValueImplied(Expr e) {
@@ -907,7 +898,7 @@ module Internal {
or
// "In string concatenation operations, the C# compiler treats a null string the same as an empty string."
// (https://docs.microsoft.com/en-us/dotnet/csharp/how-to/concatenate-multiple-strings)
- e instanceof AddExpr and
+ e instanceof AddOperation and
e.getType() instanceof StringType
or
e.(DefaultValueExpr).getType().isValueType()
@@ -922,11 +913,9 @@ module Internal {
/** Holds if expression `e2` is a non-`null` value whenever `e1` is. */
predicate nonNullValueImpliedUnary(Expr e1, Expr e2) {
- e1 = e2.(CastExpr).getExpr()
- or
- e1 = e2.(AssignExpr).getRValue()
- or
- e1 = e2.(NullCoalescingExpr).getAnOperand()
+ e1 = e2.(CastExpr).getExpr() or
+ e1 = e2.(AssignExpr).getRValue() or
+ e1 = e2.(NullCoalescingOperation).getAnOperand()
}
/**
@@ -953,10 +942,13 @@ module Internal {
)
or
// In C#, `null + 1` has type `int?` with value `null`
- exists(BinaryArithmeticOperation bao, Expr o |
- result = bao and
- bao.getAnOperand() = e and
- bao.getAnOperand() = o and
+ exists(BinaryOperation bo, Expr o |
+ bo instanceof BinaryArithmeticOperation or
+ bo instanceof AssignArithmeticOperation
+ |
+ result = bo and
+ bo.getAnOperand() = e and
+ bo.getAnOperand() = o and
// The other operand must be provably non-null in order
// for `only if` to hold
nonNullValueImplied(o) and
@@ -972,10 +964,10 @@ module Internal {
any(QualifiableExpr qe |
qe.isConditional() and
result = qe.getQualifier()
- )
- or
+ ) or
// In C#, `null + 1` has type `int?` with value `null`
- e = any(BinaryArithmeticOperation bao | result = bao.getAnOperand())
+ e = any(BinaryArithmeticOperation bao | result = bao.getAnOperand()) or
+ e = any(AssignArithmeticOperation aao | result = aao.getAnOperand())
}
deprecated predicate isGuard(Expr e, GuardValue val) {
@@ -1114,7 +1106,7 @@ module Internal {
pragma[nomagic]
private predicate nodeIsGuardedBySameSubExpr0(
- ControlFlow::Node guardedCfn, BasicBlock guardedBB, AccessOrCallExpr guarded, Guard g,
+ ControlFlowNode guardedCfn, BasicBlock guardedBB, AccessOrCallExpr guarded, Guard g,
AccessOrCallExpr sub, GuardValue v
) {
Stages::GuardsStage::forceCachingInSameStage() and
@@ -1127,7 +1119,7 @@ module Internal {
pragma[nomagic]
private predicate nodeIsGuardedBySameSubExpr(
- ControlFlow::Node guardedCfn, BasicBlock guardedBB, AccessOrCallExpr guarded, Guard g,
+ ControlFlowNode guardedCfn, BasicBlock guardedBB, AccessOrCallExpr guarded, Guard g,
AccessOrCallExpr sub, GuardValue v
) {
nodeIsGuardedBySameSubExpr0(guardedCfn, guardedBB, guarded, g, sub, v) and
@@ -1136,8 +1128,8 @@ module Internal {
pragma[nomagic]
private predicate nodeIsGuardedBySameSubExprSsaDef0(
- ControlFlow::Node cfn, BasicBlock guardedBB, AccessOrCallExpr guarded, Guard g,
- ControlFlow::Node subCfn, BasicBlock subCfnBB, AccessOrCallExpr sub, GuardValue v,
+ ControlFlowNode cfn, BasicBlock guardedBB, AccessOrCallExpr guarded, Guard g,
+ ControlFlowNode subCfn, BasicBlock subCfnBB, AccessOrCallExpr sub, GuardValue v,
Ssa::Definition def
) {
nodeIsGuardedBySameSubExpr(cfn, guardedBB, guarded, g, sub, v) and
@@ -1147,7 +1139,7 @@ module Internal {
pragma[nomagic]
private predicate nodeIsGuardedBySameSubExprSsaDef(
- ControlFlow::Node guardedCfn, AccessOrCallExpr guarded, Guard g, ControlFlow::Node subCfn,
+ ControlFlowNode guardedCfn, AccessOrCallExpr guarded, Guard g, ControlFlowNode subCfn,
AccessOrCallExpr sub, GuardValue v, Ssa::Definition def
) {
exists(BasicBlock guardedBB, BasicBlock subCfnBB |
@@ -1161,7 +1153,7 @@ module Internal {
private predicate isGuardedByExpr0(
AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, GuardValue v
) {
- forex(ControlFlow::Node cfn | cfn = guarded.getAControlFlowNode() |
+ forex(ControlFlowNode cfn | cfn = guarded.getAControlFlowNode() |
nodeIsGuardedBySameSubExpr(cfn, _, guarded, g, sub, v)
)
}
@@ -1169,7 +1161,7 @@ module Internal {
cached
predicate isGuardedByExpr(AccessOrCallExpr guarded, Guard g, AccessOrCallExpr sub, GuardValue v) {
isGuardedByExpr0(guarded, g, sub, v) and
- forall(ControlFlow::Node subCfn, Ssa::Definition def |
+ forall(ControlFlowNode subCfn, Ssa::Definition def |
nodeIsGuardedBySameSubExprSsaDef(_, guarded, g, subCfn, sub, v, def)
|
def = guarded.getAnSsaQualifier(_)
@@ -1178,17 +1170,14 @@ module Internal {
cached
predicate isGuardedByNode(
- ControlFlow::Nodes::ElementNode guarded, Guard g, AccessOrCallExpr sub, GuardValue v
+ ControlFlowNodes::ElementNode guarded, Guard g, AccessOrCallExpr sub, GuardValue v
) {
nodeIsGuardedBySameSubExpr(guarded, _, _, g, sub, v) and
- forall(ControlFlow::Node subCfn, Ssa::Definition def |
+ forall(ControlFlowNode subCfn, Ssa::Definition def |
nodeIsGuardedBySameSubExprSsaDef(guarded, _, g, subCfn, sub, v, def)
|
def =
- guarded
- .getAstNode()
- .(AccessOrCallExpr)
- .getAnSsaQualifier(guarded.getBasicBlock().getANode())
+ guarded.asExpr().(AccessOrCallExpr).getAnSsaQualifier(guarded.getBasicBlock().getANode())
)
}
}
diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Completion.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Completion.qll
index ab8bb233e2cc..85a514d5236c 100644
--- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Completion.qll
+++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Completion.qll
@@ -23,283 +23,9 @@ import csharp
private import semmle.code.csharp.commons.Assertions
private import semmle.code.csharp.commons.Constants
private import semmle.code.csharp.frameworks.System
-private import ControlFlowGraphImpl
private import NonReturning
private import SuccessorType
-private newtype TCompletion =
- TSimpleCompletion() or
- TBooleanCompletion(boolean b) { b = true or b = false } or
- TNullnessCompletion(boolean isNull) { isNull = true or isNull = false } or
- TMatchingCompletion(boolean isMatch) { isMatch = true or isMatch = false } or
- TEmptinessCompletion(boolean isEmpty) { isEmpty = true or isEmpty = false } or
- TReturnCompletion() or
- TBreakCompletion() or
- TContinueCompletion() or
- TGotoCompletion(string label) { label = any(GotoStmt gs).getLabel() } or
- TThrowCompletion(ExceptionClass ec) or
- TExitCompletion() or
- TNestedCompletion(Completion inner, Completion outer, int nestLevel) {
- inner = TBreakCompletion() and
- outer instanceof NonNestedNormalCompletion and
- nestLevel = 0
- or
- inner instanceof NormalCompletion and
- nestedFinallyCompletion(outer, nestLevel)
- }
-
-pragma[nomagic]
-private int getAFinallyNestLevel() { result = any(Statements::TryStmtTree t).nestLevel() }
-
-pragma[nomagic]
-private predicate nestedFinallyCompletion(Completion outer, int nestLevel) {
- (
- outer = TReturnCompletion()
- or
- outer = TBreakCompletion()
- or
- outer = TContinueCompletion()
- or
- outer = TGotoCompletion(_)
- or
- outer = TThrowCompletion(_)
- or
- outer = TExitCompletion()
- ) and
- nestLevel = getAFinallyNestLevel()
-}
-
-pragma[noinline]
-private predicate completionIsValidForStmt(Stmt s, Completion c) {
- s instanceof BreakStmt and
- c = TBreakCompletion()
- or
- s instanceof ContinueStmt and
- c = TContinueCompletion()
- or
- s instanceof GotoStmt and
- c = TGotoCompletion(s.(GotoStmt).getLabel())
- or
- s instanceof ReturnStmt and
- c = TReturnCompletion()
- or
- s instanceof YieldBreakStmt and
- // `yield break` behaves like a return statement
- c = TReturnCompletion()
- or
- mustHaveEmptinessCompletion(s) and
- c = TEmptinessCompletion(_)
-}
-
-/**
- * A completion of a statement or an expression.
- */
-abstract class Completion extends TCompletion {
- /**
- * Holds if this completion is valid for control flow element `cfe`.
- *
- * If `cfe` is part of a `try` statement and `cfe` may throw an exception, this
- * completion can be a throw completion.
- *
- * If `cfe` is used in a Boolean context, this completion is a Boolean completion,
- * otherwise it is a normal non-Boolean completion.
- */
- predicate isValidFor(ControlFlowElement cfe) {
- this = cfe.(NonReturningCall).getACompletion()
- or
- this = TThrowCompletion(cfe.(TriedControlFlowElement).getAThrownException())
- or
- cfe instanceof ThrowElement and
- this = TThrowCompletion(cfe.(ThrowElement).getThrownExceptionType())
- or
- this = assertionCompletion(cfe, _)
- or
- completionIsValidForStmt(cfe, this)
- or
- mustHaveBooleanCompletion(cfe) and
- (
- exists(boolean value | isBooleanConstant(cfe, value) | this = TBooleanCompletion(value))
- or
- not isBooleanConstant(cfe, _) and
- this = TBooleanCompletion(_)
- or
- // Corner case: In `if (x ?? y) { ... }`, `x` must have both a `true`
- // completion, a `false` completion, and a `null` completion (but not a
- // non-`null` completion)
- mustHaveNullnessCompletion(cfe) and
- this = TNullnessCompletion(true)
- )
- or
- mustHaveNullnessCompletion(cfe) and
- not mustHaveBooleanCompletion(cfe) and
- (
- exists(boolean value | isNullnessConstant(cfe, value) | this = TNullnessCompletion(value))
- or
- not isNullnessConstant(cfe, _) and
- this = TNullnessCompletion(_)
- )
- or
- mustHaveMatchingCompletion(cfe) and
- (
- exists(boolean value | isMatchingConstant(cfe, value) | this = TMatchingCompletion(value))
- or
- not isMatchingConstant(cfe, _) and
- this = TMatchingCompletion(_)
- )
- or
- not cfe instanceof NonReturningCall and
- not cfe instanceof ThrowElement and
- not cfe instanceof BreakStmt and
- not cfe instanceof ContinueStmt and
- not cfe instanceof GotoStmt and
- not cfe instanceof ReturnStmt and
- not cfe instanceof YieldBreakStmt and
- not mustHaveBooleanCompletion(cfe) and
- not mustHaveNullnessCompletion(cfe) and
- not mustHaveMatchingCompletion(cfe) and
- not mustHaveEmptinessCompletion(cfe) and
- this = TSimpleCompletion()
- }
-
- /**
- * Holds if this completion will continue a loop when it is the completion
- * of a loop body.
- */
- predicate continuesLoop() {
- this instanceof NormalCompletion or
- this instanceof ContinueCompletion
- }
-
- /**
- * Gets the inner completion. This is either the inner completion,
- * when the completion is nested, or the completion itself.
- */
- Completion getInnerCompletion() { result = this }
-
- /**
- * Gets the outer completion. This is either the outer completion,
- * when the completion is nested, or the completion itself.
- */
- Completion getOuterCompletion() { result = this }
-
- /** Gets a successor type that matches this completion. */
- abstract SuccessorType getAMatchingSuccessorType();
-
- /** Gets a textual representation of this completion. */
- abstract string toString();
-}
-
-/** Holds if expression `e` has the Boolean constant value `value`. */
-private predicate isBooleanConstant(Expr e, boolean value) {
- mustHaveBooleanCompletion(e) and
- (
- e.getValue() = "true" and
- value = true
- or
- e.getValue() = "false" and
- value = false
- or
- isConstantComparison(e, value)
- or
- exists(Method m, Call c, Expr expr |
- m = any(SystemStringClass s).getIsNullOrEmptyMethod() and
- c.getTarget() = m and
- e = c and
- expr = c.getArgument(0) and
- expr.hasValue() and
- if expr.getValue().length() > 0 and not expr instanceof NullLiteral
- then value = false
- else value = true
- )
- )
-}
-
-/**
- * Holds if expression `e` is constantly `null` (`value = true`) or constantly
- * non-`null` (`value = false`).
- */
-private predicate isNullnessConstant(Expr e, boolean value) {
- mustHaveNullnessCompletion(e) and
- exists(Expr stripped | stripped = e.stripCasts() |
- stripped.getType() =
- any(ValueType t |
- not t instanceof NullableType and
- // Extractor bug: the type of `x?.Length` is reported as `int`, but it should
- // be `int?`
- not getQualifier*(stripped).(QualifiableExpr).isConditional()
- ) and
- value = false
- or
- stripped instanceof NullLiteral and
- value = true
- or
- stripped.hasValue() and
- not stripped instanceof NullLiteral and
- value = false
- )
-}
-
-private Expr getQualifier(QualifiableExpr e) {
- // `e.getQualifier()` does not work for calls to extension methods
- result = e.getChildExpr(-1)
-}
-
-pragma[noinline]
-private predicate typePatternMustHaveMatchingCompletion(
- TypePatternExpr tpe, Type t, Type strippedType
-) {
- exists(Expr e, Expr stripped | mustHaveMatchingCompletion(e, tpe) |
- stripped = e.stripCasts() and
- t = tpe.getCheckedType() and
- strippedType = stripped.getType() and
- not t.containsTypeParameters() and
- not strippedType.containsTypeParameters()
- )
-}
-
-pragma[noinline]
-private Type typePatternCommonSubTypeLeft(Type t) {
- typePatternMustHaveMatchingCompletion(_, t, _) and
- result.isImplicitlyConvertibleTo(t) and
- not result instanceof DynamicType
-}
-
-pragma[noinline]
-private Type typePatternCommonSubTypeRight(Type strippedType) {
- typePatternMustHaveMatchingCompletion(_, _, strippedType) and
- result.isImplicitlyConvertibleTo(strippedType) and
- not result instanceof DynamicType
-}
-
-pragma[noinline]
-private predicate typePatternCommonSubType(Type t, Type strippedType) {
- typePatternCommonSubTypeLeft(t) = typePatternCommonSubTypeRight(strippedType)
-}
-
-/**
- * Holds if pattern expression `pe` constantly matches (`value = true`) or
- * constantly non-matches (`value = false`).
- */
-private predicate isMatchingConstant(PatternExpr pe, boolean value) {
- exists(Expr e, string exprValue, string patternValue |
- mustHaveMatchingCompletion(e, pe) and
- exprValue = e.stripCasts().getValue() and
- patternValue = pe.getValue() and
- if exprValue = patternValue then value = true else value = false
- )
- or
- pe instanceof DiscardPatternExpr and
- value = true
- or
- exists(Type t, Type strippedType |
- not t instanceof UnknownType and
- not strippedType instanceof UnknownType and
- typePatternMustHaveMatchingCompletion(pe, t, strippedType) and
- not typePatternCommonSubType(t, strippedType) and
- value = false
- )
-}
-
private class Overflowable extends UnaryOperation {
Overflowable() {
not this instanceof UnaryBitwiseOperation and
@@ -307,6 +33,13 @@ private class Overflowable extends UnaryOperation {
}
}
+/** Holds if `cfe` is a control flow element that may throw an exception. */
+predicate mayThrowException(ControlFlowElement cfe) {
+ exists(cfe.(TriedControlFlowElement).getAThrownException())
+ or
+ cfe instanceof Assertion
+}
+
/** A control flow element that is inside a `try` block. */
private class TriedControlFlowElement extends ControlFlowElement {
TriedControlFlowElement() {
@@ -344,7 +77,7 @@ private class TriedControlFlowElement extends ControlFlowElement {
result instanceof SystemOutOfMemoryExceptionClass
or
this =
- any(AddExpr ae |
+ any(AddOperation ae |
ae.getType() instanceof StringType and
result instanceof SystemOutOfMemoryExceptionClass
or
@@ -353,24 +86,24 @@ private class TriedControlFlowElement extends ControlFlowElement {
)
or
this =
- any(SubExpr se |
+ any(SubOperation se |
se.getType() instanceof IntegralType and
result instanceof SystemOverflowExceptionClass
)
or
this =
- any(MulExpr me |
+ any(MulOperation me |
me.getType() instanceof IntegralType and
result instanceof SystemOverflowExceptionClass
)
or
this =
- any(DivExpr de |
+ any(DivOperation de |
not de.getDenominator().getValue().toFloat() != 0 and
result instanceof SystemDivideByZeroExceptionClass
)
or
- this instanceof RemExpr and
+ this instanceof RemOperation and
result instanceof SystemDivideByZeroExceptionClass
or
this instanceof DynamicExpr and
@@ -389,508 +122,3 @@ pragma[nomagic]
private predicate invalidCastCandidate(CastExpr ce) {
ce.getExpr().getType() = getACastExprBaseType(ce)
}
-
-/** Gets a valid completion when argument `i` fails in assertion `a`. */
-Completion assertionCompletion(Assertion a, int i) {
- exists(AssertMethod am | am = a.getAssertMethod() |
- if am.getAssertionFailure(i).isExit()
- then result = TExitCompletion()
- else
- exists(Class c |
- am.getAssertionFailure(i).isException(c) and
- result = TThrowCompletion(c)
- )
- )
-}
-
-/**
- * Holds if a normal completion of `e` must be a Boolean completion.
- */
-private predicate mustHaveBooleanCompletion(Expr e) {
- inBooleanContext(e) and
- not e instanceof NonReturningCall
-}
-
-/**
- * Holds if `e` is used in a Boolean context. That is, whether the value
- * that `e` evaluates to determines a true/false branch successor.
- */
-private predicate inBooleanContext(Expr e) {
- e = any(IfStmt is).getCondition()
- or
- e = any(LoopStmt ls).getCondition()
- or
- e = any(Case c).getCondition()
- or
- e = any(SpecificCatchClause scc).getFilterClause()
- or
- e = any(LogicalNotExpr lne | inBooleanContext(lne)).getAnOperand()
- or
- exists(LogicalAndExpr lae |
- lae.getLeftOperand() = e
- or
- inBooleanContext(lae) and
- lae.getRightOperand() = e
- )
- or
- exists(LogicalOrExpr lae |
- lae.getLeftOperand() = e
- or
- inBooleanContext(lae) and
- lae.getRightOperand() = e
- )
- or
- exists(ConditionalExpr ce |
- ce.getCondition() = e
- or
- inBooleanContext(ce) and
- e in [ce.getThen(), ce.getElse()]
- )
- or
- e = any(NullCoalescingExpr nce | inBooleanContext(nce)).getAnOperand()
- or
- e = any(SwitchExpr se | inBooleanContext(se)).getACase()
- or
- e = any(SwitchCaseExpr sce | inBooleanContext(sce)).getBody()
-}
-
-/**
- * Holds if a normal completion of `e` must be a nullness completion.
- */
-private predicate mustHaveNullnessCompletion(Expr e) {
- inNullnessContext(e) and
- not e instanceof NonReturningCall
-}
-
-/**
- * Holds if `e` is used in a nullness context. That is, whether the value
- * that `e` evaluates to determines a `null`/non-`null` branch successor.
- */
-private predicate inNullnessContext(Expr e) {
- e = any(NullCoalescingExpr nce).getLeftOperand()
- or
- exists(QualifiableExpr qe | qe.isConditional() | e = qe.getChildExpr(-1))
- or
- exists(ConditionalExpr ce | inNullnessContext(ce) | (e = ce.getThen() or e = ce.getElse()))
- or
- exists(NullCoalescingExpr nce | inNullnessContext(nce) | e = nce.getRightOperand())
- or
- e = any(SwitchExpr se | inNullnessContext(se)).getACase()
- or
- e = any(SwitchCaseExpr sce | inNullnessContext(sce)).getBody()
-}
-
-/**
- * Holds if `pe` is the pattern inside case `c`, belonging to `switch` `s`, that
- * has the matching completion.
- */
-predicate switchMatching(Switch s, Case c, PatternExpr pe) {
- s.getACase() = c and
- pe = c.getPattern()
-}
-
-/**
- * Holds if a normal completion of `cfe` must be a matching completion. Thats is,
- * whether `cfe` determines a match in a `switch/if` statement or `catch` clause.
- */
-private predicate mustHaveMatchingCompletion(ControlFlowElement cfe) {
- switchMatching(_, _, cfe)
- or
- cfe instanceof SpecificCatchClause
- or
- cfe = any(IsExpr ie | inBooleanContext(ie)).getPattern()
- or
- cfe = any(RecursivePatternExpr rpe).getAChildExpr()
- or
- cfe = any(PositionalPatternExpr ppe).getPattern(_)
- or
- cfe = any(PropertyPatternExpr ppe).getPattern(_)
- or
- cfe = any(UnaryPatternExpr upe | mustHaveMatchingCompletion(upe)).getPattern()
- or
- cfe = any(BinaryPatternExpr bpe).getAnOperand()
-}
-
-/**
- * Holds if `pe` must have a matching completion, and `e` is the expression
- * that is being matched.
- */
-private predicate mustHaveMatchingCompletion(Expr e, PatternExpr pe) {
- exists(Switch s |
- switchMatching(s, _, pe) and
- e = s.getExpr()
- )
- or
- e = any(IsExpr ie | pe = ie.getPattern()).getExpr() and
- mustHaveMatchingCompletion(pe)
- or
- exists(PatternExpr mid | mustHaveMatchingCompletion(e, mid) |
- pe = mid.(UnaryPatternExpr).getPattern()
- or
- pe = mid.(RecursivePatternExpr).getAChildExpr()
- or
- pe = mid.(BinaryPatternExpr).getAnOperand()
- )
-}
-
-/**
- * Holds if `cfe` is the element inside foreach statement `fs` that has the emptiness
- * completion.
- */
-predicate foreachEmptiness(ForeachStmt fs, ControlFlowElement cfe) {
- cfe = fs // use `foreach` statement itself to represent the emptiness test
-}
-
-/**
- * Holds if a normal completion of `cfe` must be an emptiness completion. Thats is,
- * whether `cfe` determines whether to execute the body of a `foreach` statement.
- */
-private predicate mustHaveEmptinessCompletion(ControlFlowElement cfe) { foreachEmptiness(_, cfe) }
-
-/**
- * A completion that represents normal evaluation of a statement or an
- * expression.
- */
-abstract class NormalCompletion extends Completion { }
-
-abstract private class NonNestedNormalCompletion extends NormalCompletion { }
-
-/** A simple (normal) completion. */
-class SimpleCompletion extends NonNestedNormalCompletion, TSimpleCompletion {
- override DirectSuccessor getAMatchingSuccessorType() { any() }
-
- override string toString() { result = "normal" }
-}
-
-/**
- * A completion that represents evaluation of an expression, whose value determines
- * the successor. Either a Boolean completion (`BooleanCompletion`), a nullness
- * completion (`NullnessCompletion`), a matching completion (`MatchingCompletion`),
- * or an emptiness completion (`EmptinessCompletion`).
- */
-abstract class ConditionalCompletion extends NonNestedNormalCompletion {
- /** Gets the Boolean value of this completion. */
- abstract boolean getValue();
-
- /** Gets the dual completion. */
- abstract ConditionalCompletion getDual();
-}
-
-/**
- * A completion that represents evaluation of an expression
- * with a Boolean value.
- */
-class BooleanCompletion extends ConditionalCompletion {
- private boolean value;
-
- BooleanCompletion() { this = TBooleanCompletion(value) }
-
- override boolean getValue() { result = value }
-
- override BooleanCompletion getDual() { result = TBooleanCompletion(value.booleanNot()) }
-
- override BooleanSuccessor getAMatchingSuccessorType() { result.getValue() = value }
-
- override string toString() { result = value.toString() }
-}
-
-/** A Boolean `true` completion. */
-class TrueCompletion extends BooleanCompletion {
- TrueCompletion() { this.getValue() = true }
-}
-
-/** A Boolean `false` completion. */
-class FalseCompletion extends BooleanCompletion {
- FalseCompletion() { this.getValue() = false }
-}
-
-/**
- * A completion that represents evaluation of an expression that is either
- * `null` or non-`null`.
- */
-class NullnessCompletion extends ConditionalCompletion, TNullnessCompletion {
- private boolean value;
-
- NullnessCompletion() { this = TNullnessCompletion(value) }
-
- /** Holds if the last sub expression of this expression evaluates to `null`. */
- predicate isNull() { value = true }
-
- /** Holds if the last sub expression of this expression evaluates to a non-`null` value. */
- predicate isNonNull() { value = false }
-
- override boolean getValue() { result = value }
-
- override NullnessCompletion getDual() { result = TNullnessCompletion(value.booleanNot()) }
-
- override NullnessSuccessor getAMatchingSuccessorType() { result.getValue() = value }
-
- override string toString() { if this.isNull() then result = "null" else result = "non-null" }
-}
-
-/**
- * A completion that represents matching, for example a `case` statement in a
- * `switch` statement.
- */
-class MatchingCompletion extends ConditionalCompletion, TMatchingCompletion {
- private boolean value;
-
- MatchingCompletion() { this = TMatchingCompletion(value) }
-
- /** Holds if there is a match. */
- predicate isMatch() { value = true }
-
- /** Holds if there is not a match. */
- predicate isNonMatch() { value = false }
-
- override boolean getValue() { result = value }
-
- override MatchingCompletion getDual() { result = TMatchingCompletion(value.booleanNot()) }
-
- override MatchingSuccessor getAMatchingSuccessorType() { result.getValue() = value }
-
- override string toString() { if this.isMatch() then result = "match" else result = "no-match" }
-}
-
-/**
- * A completion that represents evaluation of an emptiness test, for example
- * a test in a `foreach` statement.
- */
-class EmptinessCompletion extends ConditionalCompletion, TEmptinessCompletion {
- private boolean value;
-
- EmptinessCompletion() { this = TEmptinessCompletion(value) }
-
- /** Holds if the emptiness test evaluates to `true`. */
- predicate isEmpty() { value = true }
-
- override boolean getValue() { result = value }
-
- override EmptinessCompletion getDual() { result = TEmptinessCompletion(value.booleanNot()) }
-
- override EmptinessSuccessor getAMatchingSuccessorType() { result.getValue() = value }
-
- override string toString() { if this.isEmpty() then result = "empty" else result = "non-empty" }
-}
-
-/**
- * A nested completion. For example, in
- *
- * ```csharp
- * void M(bool b1, bool b2)
- * {
- * try
- * {
- * if (b1)
- * throw new Exception();
- * }
- * finally
- * {
- * if (b2)
- * System.Console.WriteLine("M called");
- * }
- * }
- * ```
- *
- * `b2` has an outer throw completion (inherited from `throw new Exception`)
- * and an inner `false` completion. `b2` also has a (normal) `true` completion.
- */
-class NestedCompletion extends Completion, TNestedCompletion {
- Completion inner;
- Completion outer;
- int nestLevel;
-
- NestedCompletion() { this = TNestedCompletion(inner, outer, nestLevel) }
-
- /** Gets a completion that is compatible with the inner completion. */
- Completion getAnInnerCompatibleCompletion() {
- result.getOuterCompletion() = this.getInnerCompletion()
- }
-
- /** Gets the level of this nested completion. */
- int getNestLevel() { result = nestLevel }
-
- override Completion getInnerCompletion() { result = inner }
-
- override Completion getOuterCompletion() { result = outer }
-
- override SuccessorType getAMatchingSuccessorType() { none() }
-
- override string toString() { result = outer + " [" + inner + "] (" + nestLevel + ")" }
-}
-
-/**
- * A nested completion for a loop that exists with a `break`.
- *
- * This completion is added for technical reasons only: when a loop
- * body can complete with a break completion, the loop itself completes
- * normally. However, if we choose `TSimpleCompletion` as the completion
- * of the loop, we lose the information that the last element actually
- * completed with a break, meaning that the control flow edge out of the
- * breaking node cannot be marked with a `break` label.
- *
- * Example:
- *
- * ```csharp
- * while (...) {
- * ...
- * break;
- * }
- * return;
- * ```
- *
- * The `break` on line 3 completes with a `TBreakCompletion`, therefore
- * the `while` loop can complete with a `NestedBreakCompletion`, so we
- * get an edge `break --break--> return`. (If we instead used a
- * `TSimpleCompletion`, we would get a less precise edge
- * `break --normal--> return`.)
- */
-class NestedBreakCompletion extends NormalCompletion, NestedCompletion {
- NestedBreakCompletion() {
- inner = TBreakCompletion() and
- outer instanceof NonNestedNormalCompletion
- }
-
- override BreakCompletion getInnerCompletion() { result = inner }
-
- override NonNestedNormalCompletion getOuterCompletion() { result = outer }
-
- override Completion getAnInnerCompatibleCompletion() {
- result = inner and
- outer = TSimpleCompletion()
- or
- result = TNestedCompletion(outer, inner, _)
- }
-
- override SuccessorType getAMatchingSuccessorType() {
- outer instanceof SimpleCompletion and
- result instanceof BreakSuccessor
- or
- result = outer.(ConditionalCompletion).getAMatchingSuccessorType()
- }
-}
-
-/**
- * A completion that represents evaluation of a statement or an
- * expression resulting in a return from a callable.
- */
-class ReturnCompletion extends Completion {
- ReturnCompletion() {
- this = TReturnCompletion() or
- this = TNestedCompletion(_, TReturnCompletion(), _)
- }
-
- override ReturnSuccessor getAMatchingSuccessorType() { any() }
-
- override string toString() {
- // `NestedCompletion` defines `toString()` for the other case
- this = TReturnCompletion() and result = "return"
- }
-}
-
-/**
- * A completion that represents evaluation of a statement or an
- * expression resulting in a break (in a loop or in a `switch`
- * statement).
- */
-class BreakCompletion extends Completion {
- BreakCompletion() {
- this = TBreakCompletion() or
- this = TNestedCompletion(_, TBreakCompletion(), _)
- }
-
- override BreakSuccessor getAMatchingSuccessorType() { any() }
-
- override string toString() {
- // `NestedCompletion` defines `toString()` for the other case
- this = TBreakCompletion() and result = "break"
- }
-}
-
-/**
- * A completion that represents evaluation of a statement or an
- * expression resulting in a loop continuation (a `continue`
- * statement).
- */
-class ContinueCompletion extends Completion {
- ContinueCompletion() {
- this = TContinueCompletion() or
- this = TNestedCompletion(_, TContinueCompletion(), _)
- }
-
- override ContinueSuccessor getAMatchingSuccessorType() { any() }
-
- override string toString() {
- // `NestedCompletion` defines `toString()` for the other case
- this = TContinueCompletion() and result = "continue"
- }
-}
-
-/**
- * A completion that represents evaluation of a statement or an
- * expression resulting in a `goto` jump.
- */
-class GotoCompletion extends Completion {
- private string label;
-
- GotoCompletion() {
- this = TGotoCompletion(label) or
- this = TNestedCompletion(_, TGotoCompletion(label), _)
- }
-
- /** Gets the label of the `goto` completion. */
- string getLabel() { result = label }
-
- override GotoSuccessor getAMatchingSuccessorType() { any() }
-
- override string toString() {
- // `NestedCompletion` defines `toString()` for the other case
- this = TGotoCompletion(label) and result = "goto(" + label + ")"
- }
-}
-
-/**
- * A completion that represents evaluation of a statement or an
- * expression resulting in a thrown exception.
- */
-class ThrowCompletion extends Completion {
- private ExceptionClass ec;
-
- ThrowCompletion() {
- this = TThrowCompletion(ec) or
- this = TNestedCompletion(_, TThrowCompletion(ec), _)
- }
-
- /** Gets the type of the exception being thrown. */
- ExceptionClass getExceptionClass() { result = ec }
-
- override ExceptionSuccessor getAMatchingSuccessorType() { any() }
-
- override string toString() {
- // `NestedCompletion` defines `toString()` for the other case
- this = TThrowCompletion(ec) and result = "throw(" + ec + ")"
- }
-}
-
-/**
- * A completion that represents evaluation of a statement or an
- * expression resulting in a program exit, for example
- * `System.Environment.Exit(0)`.
- *
- * An exit completion is different from a `return` completion; the former
- * exits the whole application, and exists inside `try` statements skip
- * `finally` blocks.
- */
-class ExitCompletion extends Completion {
- ExitCompletion() {
- this = TExitCompletion() or
- this = TNestedCompletion(_, TExitCompletion(), _)
- }
-
- override ExitSuccessor getAMatchingSuccessorType() { any() }
-
- override string toString() {
- // `NestedCompletion` defines `toString()` for the other case
- this = TExitCompletion() and result = "exit"
- }
-}
diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll
deleted file mode 100644
index 0bdf1f795db2..000000000000
--- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll
+++ /dev/null
@@ -1,1866 +0,0 @@
-/**
- * Provides an implementation for constructing control-flow graphs (CFGs) from
- * abstract syntax trees (ASTs), using the shared library from `codeql.controlflow.Cfg`.
- */
-
-import csharp
-private import codeql.controlflow.Cfg as CfgShared
-private import Completion
-private import semmle.code.csharp.ExprOrStmtParent
-private import semmle.code.csharp.commons.Compilation
-
-private module Initializers {
- /**
- * Gets the `i`th member initializer expression for object initializer method `obinit`
- * in compilation `comp`.
- */
- AssignExpr initializedInstanceMemberOrder(ObjectInitMethod obinit, CompilationExt comp, int i) {
- obinit.initializes(result) and
- result =
- rank[i + 1](AssignExpr ae0, Location l |
- obinit.initializes(ae0) and
- l = ae0.getLocation() and
- getCompilation(l.getFile()) = comp
- |
- ae0 order by l.getStartLine(), l.getStartColumn(), l.getFile().getAbsolutePath()
- )
- }
-
- /**
- * Gets the last member initializer expression for object initializer method `obinit`
- * in compilation `comp`.
- */
- AssignExpr lastInitializer(ObjectInitMethod obinit, CompilationExt comp) {
- exists(int i |
- result = initializedInstanceMemberOrder(obinit, comp, i) and
- not exists(initializedInstanceMemberOrder(obinit, comp, i + 1))
- )
- }
-}
-
-/** An element that defines a new CFG scope. */
-class CfgScope extends Element, @top_level_exprorstmt_parent {
- CfgScope() {
- this.getFile().fromSource() and
- (
- this =
- any(Callable c |
- c.(Constructor).hasInitializer()
- or
- c.(ObjectInitMethod).initializes(_)
- or
- c.hasBody()
- )
- or
- // For now, static initializer values have their own scope. Eventually, they
- // should be treated like instance initializers.
- this.(Assignable).(Modifiable).isStatic() and
- expr_parent_top_level_adjusted2(_, _, this)
- )
- }
-}
-
-private class TAstNode = @callable or @control_flow_element;
-
-private Element getAChild(Element p) {
- result = p.getAChild() or
- 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() { astNode(this) }
-
- int getId() { idOf(this, result) }
-}
-
-private predicate id(AstNode x, AstNode y) { x = y }
-
-private predicate idOf(AstNode x, int y) = equivalenceRelation(id/2)(x, y)
-
-private module CfgInput implements CfgShared::InputSig {
- private import ControlFlowGraphImpl as Impl
- private import Completion as Comp
- private import SuccessorType as ST
- private import semmle.code.csharp.Caching
-
- class AstNode = Impl::AstNode;
-
- class Completion = Comp::Completion;
-
- predicate completionIsNormal(Completion c) { c instanceof Comp::NormalCompletion }
-
- predicate completionIsSimple(Completion c) { c instanceof Comp::SimpleCompletion }
-
- predicate completionIsValidFor(Completion c, AstNode e) { c.isValidFor(e) }
-
- class CfgScope = Impl::CfgScope;
-
- CfgScope getCfgScope(AstNode n) {
- Stages::ControlFlowStage::forceCachingInSameStage() and
- result = n.(ControlFlowElement).getEnclosingCallable()
- }
-
- predicate scopeFirst(CfgScope scope, AstNode first) { Impl::scopeFirst(scope, first) }
-
- predicate scopeLast(CfgScope scope, AstNode last, Completion c) {
- Impl::scopeLast(scope, last, c)
- }
-
- private class SuccessorType = ST::SuccessorType;
-
- SuccessorType getAMatchingSuccessorType(Completion c) { result = c.getAMatchingSuccessorType() }
-
- int idOfAstNode(AstNode node) { result = node.getId() }
-
- int idOfCfgScope(CfgScope node) { result = idOfAstNode(node) }
-}
-
-private module CfgSplittingInput implements CfgShared::SplittingInputSig {
- private import Splitting as S
-
- class SplitKindBase = S::TSplitKind;
-
- class Split = S::Split;
-}
-
-private module ConditionalCompletionSplittingInput implements
- CfgShared::ConditionalCompletionSplittingInputSig
-{
- import Splitting::ConditionalCompletionSplitting::ConditionalCompletionSplittingInput
-}
-
-import CfgShared::MakeWithSplitting
-
-/**
- * A compilation.
- *
- * Unlike the standard `Compilation` class, this class also supports buildless
- * extraction.
- */
-newtype CompilationExt =
- TCompilation(Compilation c) { not extractionIsStandalone() } or
- TBuildless() { extractionIsStandalone() }
-
-/** Gets the compilation that source file `f` belongs to. */
-CompilationExt getCompilation(File f) {
- exists(Compilation c |
- f = c.getAFileCompiled() and
- result = TCompilation(c)
- )
- or
- result = TBuildless()
-}
-
-/**
- * The `expr_parent_top_level_adjusted()` relation restricted to exclude relations
- * between properties and their getters' expression bodies in properties such as
- * `int P => 0`.
- *
- * This is in order to only associate the expression body with one CFG scope, namely
- * the getter (and not the declaration itself).
- */
-private predicate expr_parent_top_level_adjusted2(
- Expr child, int i, @top_level_exprorstmt_parent parent
-) {
- expr_parent_top_level_adjusted(child, i, parent) and
- not exists(Getter g |
- g.getDeclaration() = parent and
- i = 0
- )
-}
-
-/** Holds if `first` is first executed when entering `scope`. */
-predicate scopeFirst(CfgScope scope, AstNode first) {
- scope =
- any(Callable c |
- if exists(c.(Constructor).getObjectInitializerCall())
- then first(c.(Constructor).getObjectInitializerCall(), first)
- else
- if exists(c.(Constructor).getInitializer())
- then first(c.(Constructor).getInitializer(), first)
- else first(c.getBody(), first)
- )
- or
- first(Initializers::initializedInstanceMemberOrder(scope, _, 0), first)
- or
- expr_parent_top_level_adjusted2(any(Expr e | first(e, first)), _, scope) and
- not scope instanceof Callable
-}
-
-/** Holds if `scope` is exited when `last` finishes with completion `c`. */
-predicate scopeLast(CfgScope scope, AstNode last, Completion c) {
- scope =
- any(Callable callable |
- last(callable.getBody(), last, c) and
- not c instanceof GotoCompletion
- or
- last(callable.(Constructor).getInitializer(), last, c) and
- not callable.hasBody()
- or
- // This is only relevant in the context of compilation errors, since
- // normally the existence of an object initializer call implies the
- // existence of an initializer.
- last(callable.(Constructor).getObjectInitializerCall(), last, c) and
- not callable.(Constructor).hasInitializer() and
- not callable.hasBody()
- )
- or
- last(Initializers::lastInitializer(scope, _), last, c)
- or
- expr_parent_top_level_adjusted2(any(Expr e | last(e, last, c)), _, scope) and
- not scope instanceof Callable
-}
-
-private class ObjectInitTree extends ControlFlowTree instanceof ObjectInitMethod {
- final override predicate propagatesAbnormal(AstNode child) { none() }
-
- final override predicate first(AstNode first) { none() }
-
- final override predicate last(AstNode last, Completion c) { none() }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- exists(CompilationExt comp, int i |
- // Flow from one member initializer to the next
- last(Initializers::initializedInstanceMemberOrder(this, comp, i), pred, c) and
- c instanceof NormalCompletion and
- first(Initializers::initializedInstanceMemberOrder(this, comp, i + 1), succ)
- )
- }
-}
-
-private class ConstructorTree extends ControlFlowTree instanceof Constructor {
- final override predicate propagatesAbnormal(AstNode child) { none() }
-
- final override predicate first(AstNode first) { none() }
-
- final override predicate last(AstNode last, Completion c) { none() }
-
- /** Gets the body of this constructor belonging to compilation `comp`. */
- pragma[noinline]
- AstNode getBody(CompilationExt comp) {
- result = super.getBody() and
- comp = getCompilation(result.getFile())
- }
-
- pragma[noinline]
- private MethodCall getObjectInitializerCall(CompilationExt comp) {
- result = super.getObjectInitializerCall() and
- comp = getCompilation(result.getFile())
- }
-
- pragma[noinline]
- private ConstructorInitializer getInitializer(CompilationExt comp) {
- result = super.getInitializer() and
- comp = getCompilation(result.getFile())
- }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- exists(CompilationExt comp |
- last(this.getObjectInitializerCall(comp), pred, c) and
- c instanceof NormalCompletion
- |
- first(this.getInitializer(comp), succ)
- or
- // This is only relevant in the context of compilation errors, since
- // normally the existence of an object initializer call implies the
- // existence of an initializer.
- not exists(this.getInitializer(comp)) and
- first(this.getBody(comp), succ)
- )
- }
-}
-
-cached
-private module SwithStmtInternal {
- // Reorders default to be last if needed
- cached
- CaseStmt getCase(SwitchStmt ss, int i) {
- exists(int index, int rankIndex |
- caseIndex(ss, result, index) and
- rankIndex = i + 1 and
- index = rank[rankIndex](int j, CaseStmt cs | caseIndex(ss, cs, j) | j)
- )
- }
-
- /** Implicitly reorder case statements to put the default case last if needed. */
- private predicate caseIndex(SwitchStmt ss, CaseStmt case, int index) {
- exists(int i | case = ss.getChildStmt(i) |
- if case instanceof DefaultCase
- then index = max(int j | exists(ss.getChildStmt(j))) + 1
- else index = i
- )
- }
-
- /**
- * Gets the `i`th statement in the body of this `switch` statement.
- *
- * Example:
- *
- * ```csharp
- * switch (x) {
- * case "abc": // i = 0
- * return 0;
- * case int i when i > 0: // i = 1
- * return 1;
- * case string s: // i = 2
- * Console.WriteLine(s);
- * return 2; // i = 3
- * default: // i = 4
- * return 3; // i = 5
- * }
- * ```
- *
- * Note that each non-`default` case is a labeled statement, so the statement
- * that follows is a child of the labeled statement, and not the `switch` block.
- */
- cached
- Stmt getStmt(SwitchStmt ss, int i) {
- exists(int index, int rankIndex |
- result = ss.getChildStmt(index) and
- rankIndex = i + 1 and
- index =
- rank[rankIndex](int j, Stmt s |
- // `getChild` includes both labeled statements and the targeted
- // statements of labeled statement as separate children, but we
- // only want the labeled statement
- s = getLabeledStmt(ss, j)
- |
- j
- )
- )
- }
-
- private Stmt getLabeledStmt(SwitchStmt ss, int i) {
- result = ss.getChildStmt(i) and
- not result = caseStmtGetBody(_)
- }
-}
-
-private ControlFlowElement caseGetBody(Case c) {
- result = c.getBody() or result = caseStmtGetBody(c)
-}
-
-private ControlFlowElement caseStmtGetBody(CaseStmt c) {
- exists(int i, Stmt next |
- c = c.getParent().getChild(i) and
- next = c.getParent().getChild(i + 1)
- |
- result = next and
- not result instanceof CaseStmt
- or
- result = caseStmtGetBody(next)
- )
-}
-
-// Reorders default to be last if needed
-private Case switchGetCase(Switch s, int i) {
- result = s.(SwitchExpr).getCase(i) or result = SwithStmtInternal::getCase(s, i)
-}
-
-abstract private class SwitchTree extends ControlFlowTree instanceof Switch {
- override predicate propagatesAbnormal(AstNode child) { child = super.getExpr() }
-
- override predicate succ(AstNode pred, AstNode succ, Completion c) {
- // Flow from last element of switch expression to first element of first case
- last(super.getExpr(), pred, c) and
- c instanceof NormalCompletion and
- first(switchGetCase(this, 0), succ)
- or
- // Flow from last element of case pattern to next case
- exists(Case case, int i | case = switchGetCase(this, i) |
- last(case.getPattern(), pred, c) and
- c.(MatchingCompletion).isNonMatch() and
- first(switchGetCase(this, i + 1), succ)
- )
- or
- // Flow from last element of condition to next case
- exists(Case case, int i | case = switchGetCase(this, i) |
- last(case.getCondition(), pred, c) and
- c instanceof FalseCompletion and
- first(switchGetCase(this, i + 1), succ)
- )
- }
-}
-
-abstract private class CaseTree extends ControlFlowTree instanceof Case {
- final override predicate propagatesAbnormal(AstNode child) {
- child in [super.getPattern().(ControlFlowElement), super.getCondition(), caseGetBody(this)]
- }
-
- override predicate succ(AstNode pred, AstNode succ, Completion c) {
- last(super.getPattern(), pred, c) and
- c.(MatchingCompletion).isMatch() and
- (
- if exists(super.getCondition())
- then
- // Flow from the last element of pattern to the condition
- first(super.getCondition(), succ)
- else
- // Flow from last element of pattern to first element of body
- first(caseGetBody(this), succ)
- )
- or
- // Flow from last element of condition to first element of body
- last(super.getCondition(), pred, c) and
- c instanceof TrueCompletion and
- first(caseGetBody(this), succ)
- }
-}
-
-module Expressions {
- /** An expression that should not be included in the control flow graph. */
- abstract private class NoNodeExpr extends Expr { }
-
- private class SimpleNoNodeExpr extends NoNodeExpr {
- SimpleNoNodeExpr() {
- this instanceof TypeAccess and
- not this instanceof TypeAccessPatternExpr
- }
- }
-
- /** A write access that is not also a read access. */
- private class WriteAccess extends AssignableWrite {
- WriteAccess() {
- // `x++` is both a read and write access
- not this instanceof AssignableRead
- }
- }
-
- private class WriteAccessNoNodeExpr extends WriteAccess, NoNodeExpr {
- WriteAccessNoNodeExpr() {
- // For example a write to a static field, `Foo.Bar = 0`.
- forall(Expr e | e = this.getAChildExpr() | e instanceof NoNodeExpr)
- }
- }
-
- private AstNode getExprChild0(Expr e, int i) {
- not e instanceof NameOfExpr and
- not e instanceof QualifiableExpr and
- not e instanceof Assignment and
- not e instanceof AnonymousFunctionExpr and
- result = e.getChild(i)
- or
- e = any(ExtensionMethodCall emc | result = emc.getArgument(i))
- or
- e =
- any(QualifiableExpr qe |
- not qe instanceof ExtensionMethodCall and
- result = qe.getChild(i)
- )
- or
- e =
- any(Assignment a |
- // The left-hand side of an assignment is evaluated before the right-hand side
- i = 0 and result = a.getLValue()
- or
- i = 1 and result = a.getRValue()
- )
- }
-
- private AstNode getExprChild(Expr e, int i) {
- result =
- rank[i + 1](AstNode cfe, int j |
- cfe = getExprChild0(e, j) and
- not cfe instanceof NoNodeExpr
- |
- cfe order by j
- )
- }
-
- private AstNode getLastExprChild(Expr e) {
- exists(int last |
- result = getExprChild(e, last) and
- not exists(getExprChild(e, last + 1))
- )
- }
-
- private class StandardExpr extends StandardPostOrderTree instanceof Expr {
- StandardExpr() {
- // The following expressions need special treatment
- not this instanceof LogicalNotExpr and
- not this instanceof LogicalAndExpr and
- not this instanceof LogicalOrExpr and
- not this instanceof NullCoalescingExpr and
- not this instanceof ConditionalExpr and
- not this instanceof AssignOperationWithExpandedAssignment and
- not this instanceof ConditionallyQualifiedExpr and
- not this instanceof ThrowExpr and
- not this instanceof ObjectCreation and
- not this instanceof ArrayCreation and
- not this instanceof QualifiedWriteAccess and
- not this instanceof QualifiedAccessorWrite and
- not this instanceof NoNodeExpr and
- not this instanceof SwitchExpr and
- not this instanceof SwitchCaseExpr and
- not this instanceof ConstructorInitializer and
- not this instanceof NotPatternExpr and
- not this instanceof OrPatternExpr and
- not this instanceof AndPatternExpr and
- not this instanceof RecursivePatternExpr and
- not this instanceof PositionalPatternExpr and
- not this instanceof PropertyPatternExpr
- }
-
- final override AstNode getChildNode(int i) { result = getExprChild(this, i) }
- }
-
- /**
- * A qualified write access.
- *
- * The successor declaration in `QualifiedAccessorWrite` ensures that the access itself
- * is evaluated after the qualifier and the indexer arguments (if any)
- * and the right hand side of the assignment.
- *
- * When a qualified write access is used as an `out/ref` argument, the access itself is evaluated immediately.
- */
- private class QualifiedWriteAccess extends ControlFlowTree instanceof WriteAccess, QualifiableExpr
- {
- QualifiedWriteAccess() {
- (
- this.hasQualifier()
- or
- // Member initializers like
- // ```csharp
- // new Dictionary() { [0] = "Zero", [1] = "One", [2] = "Two" }
- // ```
- // need special treatment, because the accesses `[0]`, `[1]`, and `[2]`
- // have no qualifier.
- this = any(MemberInitializer mi).getLValue()
- ) and
- not exists(AssignableDefinitions::OutRefDefinition def | def.getTargetAccess() = this)
- }
-
- final override predicate propagatesAbnormal(AstNode child) { child = getExprChild(this, _) }
-
- final override predicate first(AstNode first) { first(getExprChild(this, 0), first) }
-
- final override predicate last(AstNode last, Completion c) {
- // Skip the access in a qualified write access
- last(getLastExprChild(this), last, c)
- or
- // Qualifier exits with a null completion
- super.isConditional() and
- last(super.getQualifier(), last, c) and
- c.(NullnessCompletion).isNull()
- }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- exists(int i |
- last(getExprChild(this, i), pred, c) and
- c instanceof NormalCompletion and
- (if i = 0 then not c.(NullnessCompletion).isNull() else any()) and
- first(getExprChild(this, i + 1), succ)
- )
- }
- }
-
- /**
- * An expression that writes via a qualifiable expression, for example `x.Prop = 0`,
- * where `Prop` is a property.
- *
- * Accessor writes need special attention, because we need to model the fact
- * that the accessor is called *after* the assigned value has been evaluated.
- * In the example above, this means we want a CFG that looks like
- *
- * ```csharp
- * x -> 0 -> set_Prop -> x.Prop = 0
- * ```
- *
- * For consistency, control flow is implemented the same way for other qualified writes.
- * For example, `x.Field = 0`, where `Field` is a field, we want a CFG that looks like
- *
- * ```csharp
- * x -> 0 -> x.Field -> x.Field = 0
- * ```
- */
- private class QualifiedAccessorWrite extends PostOrderTree instanceof Expr {
- AssignableDefinition def;
-
- QualifiedAccessorWrite() {
- def.getExpr() = this and
- def.getTargetAccess().(WriteAccess) instanceof QualifiableExpr and
- not def instanceof AssignableDefinitions::OutRefDefinition and
- not this instanceof AssignOperationWithExpandedAssignment
- }
-
- /**
- * Gets the `i`th accessor being called in this write. More than one call
- * can happen in tuple assignments.
- */
- QualifiableExpr getAccess(int i) {
- result =
- rank[i + 1](AssignableDefinitions::TupleAssignmentDefinition tdef |
- tdef.getExpr() = this and
- tdef.getTargetAccess() instanceof QualifiableExpr
- |
- tdef order by tdef.getEvaluationOrder()
- ).getTargetAccess()
- or
- i = 0 and
- result = def.getTargetAccess() and
- not def instanceof AssignableDefinitions::TupleAssignmentDefinition
- }
-
- final override predicate propagatesAbnormal(AstNode child) {
- child = getExprChild(this, _)
- or
- child = this.getAccess(_)
- }
-
- final override predicate last(AstNode last, Completion c) {
- PostOrderTree.super.last(last, c)
- or
- last(getExprChild(this, 0), last, c) and c.(NullnessCompletion).isNull()
- }
-
- final override predicate first(AstNode first) { first(getExprChild(this, 0), first) }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- // Standard left-to-right evaluation
- exists(int i |
- last(getExprChild(this, i), pred, c) and
- c instanceof NormalCompletion and
- (if i = 0 then not c.(NullnessCompletion).isNull() else any()) and
- first(getExprChild(this, i + 1), succ)
- )
- or
- // Flow from last element of last child to first accessor call
- last(getLastExprChild(this), pred, c) and
- succ = this.getAccess(0) and
- c instanceof NormalCompletion
- or
- // Flow from one call to the next
- exists(int i | pred = this.getAccess(i) |
- succ = this.getAccess(i + 1) and
- c.isValidFor(pred) and
- c instanceof NormalCompletion
- )
- or
- // Post-order: flow from last call to element itself
- exists(int last | last = max(int i | exists(this.getAccess(i))) |
- pred = this.getAccess(last) and
- succ = this and
- c.isValidFor(pred) and
- c instanceof NormalCompletion
- )
- }
- }
-
- private class LogicalNotExprTree extends PostOrderTree instanceof LogicalNotExpr {
- private Expr operand;
-
- LogicalNotExprTree() { operand = this.getOperand() }
-
- final override predicate propagatesAbnormal(AstNode child) { child = operand }
-
- final override predicate first(AstNode first) { first(operand, first) }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- succ = this and
- last(operand, pred, c) and
- c instanceof NormalCompletion
- }
- }
-
- private class LogicalAndExprTree extends PostOrderTree instanceof LogicalAndExpr {
- final override predicate propagatesAbnormal(AstNode child) {
- child in [super.getLeftOperand(), super.getRightOperand()]
- }
-
- final override predicate first(AstNode first) { first(super.getLeftOperand(), first) }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- // Flow from last element of left operand to first element of right operand
- last(super.getLeftOperand(), pred, c) and
- c instanceof TrueCompletion and
- first(super.getRightOperand(), succ)
- or
- // Post-order: flow from last element of left operand to element itself
- last(super.getLeftOperand(), pred, c) and
- c instanceof FalseCompletion and
- succ = this
- or
- // Post-order: flow from last element of right operand to element itself
- last(super.getRightOperand(), pred, c) and
- c instanceof NormalCompletion and
- succ = this
- }
- }
-
- private class LogicalOrExprTree extends PostOrderTree instanceof LogicalOrExpr {
- final override predicate propagatesAbnormal(AstNode child) {
- child in [super.getLeftOperand(), super.getRightOperand()]
- }
-
- final override predicate first(AstNode first) { first(super.getLeftOperand(), first) }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- // Flow from last element of left operand to first element of right operand
- last(super.getLeftOperand(), pred, c) and
- c instanceof FalseCompletion and
- first(super.getRightOperand(), succ)
- or
- // Post-order: flow from last element of left operand to element itself
- last(super.getLeftOperand(), pred, c) and
- c instanceof TrueCompletion and
- succ = this
- or
- // Post-order: flow from last element of right operand to element itself
- last(super.getRightOperand(), pred, c) and
- c instanceof NormalCompletion and
- succ = this
- }
- }
-
- private class NullCoalescingExprTree extends PostOrderTree instanceof NullCoalescingExpr {
- final override predicate propagatesAbnormal(AstNode child) {
- child in [super.getLeftOperand(), super.getRightOperand()]
- }
-
- final override predicate first(AstNode first) { first(super.getLeftOperand(), first) }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- // Flow from last element of left operand to first element of right operand
- last(super.getLeftOperand(), pred, c) and
- c.(NullnessCompletion).isNull() and
- first(super.getRightOperand(), succ)
- or
- // Post-order: flow from last element of left operand to element itself
- last(super.getLeftOperand(), pred, c) and
- succ = this and
- c instanceof NormalCompletion and
- not c.(NullnessCompletion).isNull()
- or
- // Post-order: flow from last element of right operand to element itself
- last(super.getRightOperand(), pred, c) and
- c instanceof NormalCompletion and
- succ = this
- }
- }
-
- private class ConditionalExprTree extends PostOrderTree instanceof ConditionalExpr {
- final override predicate propagatesAbnormal(AstNode child) {
- child in [super.getCondition(), super.getThen(), super.getElse()]
- }
-
- final override predicate first(AstNode first) { first(super.getCondition(), first) }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- // Flow from last element of condition to first element of then branch
- last(super.getCondition(), pred, c) and
- c instanceof TrueCompletion and
- first(super.getThen(), succ)
- or
- // Flow from last element of condition to first element of else branch
- last(super.getCondition(), pred, c) and
- c instanceof FalseCompletion and
- first(super.getElse(), succ)
- or
- // Post-order: flow from last element of a branch to element itself
- last([super.getThen(), super.getElse()], pred, c) and
- c instanceof NormalCompletion and
- succ = this
- }
- }
-
- /**
- * An assignment operation that has an expanded version. We use the expanded
- * version in the control flow graph in order to get better data flow / taint
- * tracking.
- */
- private class AssignOperationWithExpandedAssignment extends ControlFlowTree instanceof AssignOperation
- {
- private Expr expanded;
-
- AssignOperationWithExpandedAssignment() { expanded = this.getExpandedAssignment() }
-
- final override predicate first(AstNode first) { first(expanded, first) }
-
- final override predicate last(AstNode last, Completion c) { last(expanded, last, c) }
-
- final override predicate propagatesAbnormal(AstNode child) { none() }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) { none() }
- }
-
- /** A conditionally qualified expression. */
- private class ConditionallyQualifiedExpr extends PostOrderTree instanceof QualifiableExpr {
- private Expr qualifier;
-
- ConditionallyQualifiedExpr() {
- this.isConditional() and qualifier = getExprChild(this, 0) and not this instanceof WriteAccess
- }
-
- final override predicate propagatesAbnormal(AstNode child) { child = qualifier }
-
- final override predicate first(AstNode first) { first(qualifier, first) }
-
- pragma[nomagic]
- private predicate lastQualifier(AstNode last, Completion c) { last(qualifier, last, c) }
-
- final override predicate last(AstNode last, Completion c) {
- PostOrderTree.super.last(last, c)
- or
- // Qualifier exits with a `null` completion
- this.lastQualifier(last, c) and
- c.(NullnessCompletion).isNull()
- }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- exists(int i |
- last(getExprChild(this, i), pred, c) and
- c instanceof NormalCompletion and
- if i = 0 then c.(NullnessCompletion).isNonNull() else any()
- |
- // Post-order: flow from last element of last child to element itself
- i = max(int j | exists(getExprChild(this, j))) and
- succ = this
- or
- // Standard left-to-right evaluation
- first(getExprChild(this, i + 1), succ)
- )
- }
- }
-
- private class ThrowExprTree extends PostOrderTree instanceof ThrowExpr {
- final override predicate propagatesAbnormal(AstNode child) { child = super.getExpr() }
-
- final override predicate first(AstNode first) { first(super.getExpr(), first) }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- last(super.getExpr(), pred, c) and
- c instanceof NormalCompletion and
- succ = this
- }
- }
-
- private class ObjectCreationTree extends ControlFlowTree instanceof ObjectCreation {
- private Expr getObjectCreationArgument(int i) {
- i >= 0 and
- if super.hasInitializer()
- then result = getExprChild(this, i + 1)
- else result = getExprChild(this, i)
- }
-
- final override predicate propagatesAbnormal(AstNode child) {
- child = this.getObjectCreationArgument(_)
- }
-
- final override predicate first(AstNode first) {
- first(this.getObjectCreationArgument(0), first)
- or
- not exists(this.getObjectCreationArgument(0)) and
- first = this
- }
-
- final override predicate last(AstNode last, Completion c) {
- // Post-order: element itself (when no initializer)
- last = this and
- not super.hasInitializer() and
- c.isValidFor(this)
- or
- // Last element of initializer
- last(super.getInitializer(), last, c)
- }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- // Flow from last element of argument `i` to first element of argument `i+1`
- exists(int i | last(this.getObjectCreationArgument(i), pred, c) |
- first(this.getObjectCreationArgument(i + 1), succ) and
- c instanceof NormalCompletion
- )
- or
- // Flow from last element of last argument to self
- exists(int last | last = max(int i | exists(this.getObjectCreationArgument(i))) |
- last(this.getObjectCreationArgument(last), pred, c) and
- succ = this and
- c instanceof NormalCompletion
- )
- or
- // Flow from self to first element of initializer
- pred = this and
- first(super.getInitializer(), succ) and
- c instanceof SimpleCompletion
- }
- }
-
- private class ArrayCreationTree extends ControlFlowTree instanceof ArrayCreation {
- final override predicate propagatesAbnormal(AstNode child) {
- child = super.getALengthArgument()
- }
-
- final override predicate first(AstNode first) {
- // First element of first length argument
- first(super.getLengthArgument(0), first)
- or
- // No length argument: element itself
- not exists(super.getLengthArgument(0)) and
- first = this
- }
-
- final override predicate last(AstNode last, Completion c) {
- // Post-order: element itself (when no initializer)
- last = this and
- not super.hasInitializer() and
- c.isValidFor(this)
- or
- // Last element of initializer
- last(super.getInitializer(), last, c)
- }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- // Flow from self to first element of initializer
- pred = this and
- first(super.getInitializer(), succ) and
- c instanceof SimpleCompletion
- or
- exists(int i |
- last(super.getLengthArgument(i), pred, c) and
- c instanceof SimpleCompletion
- |
- // Flow from last length argument to self
- i = max(int j | exists(super.getLengthArgument(j))) and
- succ = this
- or
- // Flow from one length argument to the next
- first(super.getLengthArgument(i + 1), succ)
- )
- }
- }
-
- private class SwitchExprTree extends PostOrderTree, SwitchTree instanceof SwitchExpr {
- final override predicate propagatesAbnormal(AstNode child) {
- SwitchTree.super.propagatesAbnormal(child)
- or
- child = super.getACase()
- }
-
- final override predicate first(AstNode first) { first(super.getExpr(), first) }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- SwitchTree.super.succ(pred, succ, c)
- or
- last(super.getACase(), pred, c) and
- succ = this and
- c instanceof NormalCompletion
- }
- }
-
- private class SwitchCaseExprTree extends PostOrderTree, CaseTree instanceof SwitchCaseExpr {
- final override predicate first(AstNode first) { first(super.getPattern(), first) }
-
- pragma[noinline]
- private predicate lastNoMatch(AstNode last, ConditionalCompletion cc) {
- last([super.getPattern(), super.getCondition()], last, cc) and
- (cc.(MatchingCompletion).isNonMatch() or cc instanceof FalseCompletion)
- }
-
- final override predicate last(AstNode last, Completion c) {
- PostOrderTree.super.last(last, c)
- or
- // Last case exists with a non-match
- exists(SwitchExpr se, int i, ConditionalCompletion cc |
- this = se.getCase(i) and
- not super.matchesAll() and
- not exists(se.getCase(i + 1)) and
- this.lastNoMatch(last, cc) and
- c =
- any(NestedCompletion nc |
- nc.getNestLevel() = 0 and
- nc.getInnerCompletion() = cc and
- nc.getOuterCompletion()
- .(ThrowCompletion)
- .getExceptionClass()
- .hasFullyQualifiedName("System", "InvalidOperationException")
- )
- )
- }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- CaseTree.super.succ(pred, succ, c)
- or
- last(super.getBody(), pred, c) and
- succ = this and
- c instanceof NormalCompletion
- }
- }
-
- private class ConstructorInitializerTree extends PostOrderTree instanceof ConstructorInitializer {
- private ControlFlowTree getChildNode(int i) { result = getExprChild(this, i) }
-
- final override predicate propagatesAbnormal(AstNode child) { child = this.getChildNode(_) }
-
- final override predicate first(AstNode first) {
- first(this.getChildNode(0), first)
- or
- not exists(this.getChildNode(0)) and
- first = this
- }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- // Post-order: flow from last element of last child to element itself
- exists(int lst |
- lst = max(int i | exists(this.getChildNode(i))) and
- last(this.getChildNode(lst), pred, c) and
- succ = this and
- c instanceof NormalCompletion
- )
- or
- // Standard left-to-right evaluation
- exists(int i |
- last(this.getChildNode(i), pred, c) and
- c instanceof NormalCompletion and
- first(this.getChildNode(i + 1), succ)
- )
- or
- exists(ConstructorTree con, CompilationExt comp |
- last(this, pred, c) and
- con = super.getConstructor() and
- comp = getCompilation(this.getFile()) and
- c instanceof NormalCompletion and
- first(con.getBody(comp), succ)
- )
- }
- }
-
- private class NotPatternExprTree extends PostOrderTree instanceof NotPatternExpr {
- final override predicate propagatesAbnormal(AstNode child) { child = super.getPattern() }
-
- final override predicate first(AstNode first) { first(super.getPattern(), first) }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- succ = this and
- last(super.getPattern(), pred, c) and
- c instanceof NormalCompletion
- }
- }
-
- private class AndPatternExprTree extends PostOrderTree instanceof AndPatternExpr {
- final override predicate propagatesAbnormal(AstNode child) { child = super.getAnOperand() }
-
- final override predicate first(AstNode first) { first(super.getLeftOperand(), first) }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- // Flow from last element of left operand to first element of right operand
- last(super.getLeftOperand(), pred, c) and
- c.(MatchingCompletion).getValue() = true and
- first(super.getRightOperand(), succ)
- or
- // Post-order: flow from last element of left operand to element itself
- last(super.getLeftOperand(), pred, c) and
- c.(MatchingCompletion).getValue() = false and
- succ = this
- or
- // Post-order: flow from last element of right operand to element itself
- last(super.getRightOperand(), pred, c) and
- c instanceof MatchingCompletion and
- succ = this
- }
- }
-
- private class OrPatternExprTree extends PostOrderTree instanceof OrPatternExpr {
- final override predicate propagatesAbnormal(AstNode child) { child = super.getAnOperand() }
-
- final override predicate first(AstNode first) { first(super.getLeftOperand(), first) }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- // Flow from last element of left operand to first element of right operand
- last(super.getLeftOperand(), pred, c) and
- c.(MatchingCompletion).getValue() = false and
- first(super.getRightOperand(), succ)
- or
- // Post-order: flow from last element of left operand to element itself
- last(super.getLeftOperand(), pred, c) and
- c.(MatchingCompletion).getValue() = true and
- succ = this
- or
- // Post-order: flow from last element of right operand to element itself
- last(super.getRightOperand(), pred, c) and
- c instanceof MatchingCompletion and
- succ = this
- }
- }
-}
-
-private class RecursivePatternExprTree extends PostOrderTree instanceof RecursivePatternExpr {
- private Expr getTypeExpr() {
- result = super.getVariableDeclExpr()
- or
- not exists(super.getVariableDeclExpr()) and
- result = super.getTypeAccess()
- }
-
- private PatternExpr getChildPattern() {
- result = super.getPositionalPatterns()
- or
- result = super.getPropertyPatterns()
- }
-
- final override predicate propagatesAbnormal(AstNode child) { child = this.getChildPattern() }
-
- final override predicate first(AstNode first) {
- first(this.getTypeExpr(), first)
- or
- not exists(this.getTypeExpr()) and
- first(this.getChildPattern(), first)
- }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- // Flow from type test to child pattern
- last(this.getTypeExpr(), pred, c) and
- first(this.getChildPattern(), succ) and
- c.(MatchingCompletion).getValue() = true
- or
- // Flow from type test to self
- last(this.getTypeExpr(), pred, c) and
- succ = this and
- c.(MatchingCompletion).getValue() = false
- or
- // Flow from child pattern to self
- last(this.getChildPattern(), pred, c) and
- succ = this and
- c instanceof MatchingCompletion
- }
-}
-
-private class PositionalPatternExprTree extends PreOrderTree instanceof PositionalPatternExpr {
- final override predicate propagatesAbnormal(AstNode child) { child = super.getPattern(_) }
-
- final override predicate last(AstNode last, Completion c) {
- last = this and
- c.(MatchingCompletion).getValue() = false
- or
- last(super.getPattern(_), last, c) and
- c.(MatchingCompletion).getValue() = false
- or
- exists(int lst |
- last(super.getPattern(lst), last, c) and
- not exists(super.getPattern(lst + 1))
- )
- }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- // Flow from self to first pattern
- pred = this and
- c.(MatchingCompletion).getValue() = true and
- first(super.getPattern(0), succ)
- or
- // Flow from one pattern to the next
- exists(int i |
- last(super.getPattern(i), pred, c) and
- c.(MatchingCompletion).getValue() = true and
- first(super.getPattern(i + 1), succ)
- )
- }
-}
-
-private class PropertyPatternExprExprTree extends PostOrderTree instanceof PropertyPatternExpr {
- final override predicate propagatesAbnormal(AstNode child) { child = super.getPattern(_) }
-
- final override predicate first(AstNode first) {
- first(super.getPattern(0), first)
- or
- not exists(super.getPattern(0)) and
- first = this
- }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- // Flow from one pattern to the next
- exists(int i |
- last(super.getPattern(i), pred, c) and
- c.(MatchingCompletion).getValue() = true and
- first(super.getPattern(i + 1), succ)
- )
- or
- // Post-order: flow from last element of failing pattern to element itself
- last(super.getPattern(_), pred, c) and
- c.(MatchingCompletion).getValue() = false and
- succ = this
- or
- // Post-order: flow from last element of last pattern to element itself
- exists(int last |
- last(super.getPattern(last), pred, c) and
- not exists(super.getPattern(last + 1)) and
- c instanceof MatchingCompletion and
- succ = this
- )
- }
-}
-
-module Statements {
- private class StandardStmt extends StandardPreOrderTree instanceof Stmt {
- StandardStmt() {
- // The following statements need special treatment
- not this instanceof IfStmt and
- not this instanceof SwitchStmt and
- not this instanceof CaseStmt and
- not this instanceof LoopStmt and
- not this instanceof TryStmt and
- not this instanceof SpecificCatchClause and
- not this instanceof JumpStmt and
- not this instanceof LabeledStmt
- }
-
- private ControlFlowTree getChildNode0(int i) {
- not this instanceof GeneralCatchClause and
- not this instanceof FixedStmt and
- not this instanceof UsingBlockStmt and
- result = this.getChild(i)
- or
- this = any(GeneralCatchClause gcc | i = 0 and result = gcc.getBlock())
- or
- this =
- any(FixedStmt fs |
- result = fs.getVariableDeclExpr(i)
- or
- result = fs.getBody() and
- i = max(int j | exists(fs.getVariableDeclExpr(j))) + 1
- )
- or
- this =
- any(UsingBlockStmt us |
- result = us.getExpr() and
- i = 0
- or
- result = us.getVariableDeclExpr(i)
- or
- result = us.getBody() and
- i = max([1, count(us.getVariableDeclExpr(_))])
- )
- }
-
- final override AstNode getChildNode(int i) {
- result = rank[i + 1](AstNode cfe, int j | cfe = this.getChildNode0(j) | cfe order by j)
- }
- }
-
- private class IfStmtTree extends PreOrderTree instanceof IfStmt {
- final override predicate propagatesAbnormal(AstNode child) { child = super.getCondition() }
-
- final override predicate last(AstNode last, Completion c) {
- // Condition exits with a false completion and there is no `else` branch
- last(super.getCondition(), last, c) and
- c instanceof FalseCompletion and
- not exists(super.getElse())
- or
- // Then branch exits with any completion
- last(super.getThen(), last, c)
- or
- // Else branch exits with any completion
- last(super.getElse(), last, c)
- }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- // Pre-order: flow from statement itself to first element of condition
- pred = this and
- first(super.getCondition(), succ) and
- c instanceof SimpleCompletion
- or
- last(super.getCondition(), pred, c) and
- (
- // Flow from last element of condition to first element of then branch
- c instanceof TrueCompletion and first(super.getThen(), succ)
- or
- // Flow from last element of condition to first element of else branch
- c instanceof FalseCompletion and first(super.getElse(), succ)
- )
- }
- }
-
- private class SwitchStmtTree extends PreOrderTree, SwitchTree instanceof SwitchStmt {
- final override predicate last(AstNode last, Completion c) {
- // Switch expression exits normally and there are no cases
- not exists(super.getACase()) and
- last(super.getExpr(), last, c) and
- c instanceof NormalCompletion
- or
- // A statement exits with a `break` completion
- last(SwithStmtInternal::getStmt(this, _), last,
- c.(NestedBreakCompletion).getAnInnerCompatibleCompletion())
- or
- // A statement exits abnormally
- last(SwithStmtInternal::getStmt(this, _), last, c) and
- not c instanceof BreakCompletion and
- not c instanceof NormalCompletion and
- not any(LabeledStmtTree t |
- t.hasLabelInCallable(c.(GotoCompletion).getLabel(), super.getEnclosingCallable())
- ) instanceof CaseStmt
- or
- // Last case exits with a non-match
- exists(CaseStmt cs, int last_ |
- last_ = max(int i | exists(SwithStmtInternal::getCase(this, i))) and
- cs = SwithStmtInternal::getCase(this, last_)
- |
- last(cs.getPattern(), last, c) and
- not c.(MatchingCompletion).isMatch()
- or
- last(cs.getCondition(), last, c) and
- c instanceof FalseCompletion
- )
- }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- SwitchTree.super.succ(pred, succ, c)
- or
- // Pre-order: flow from statement itself to first switch expression
- pred = this and
- first(super.getExpr(), succ) and
- c instanceof SimpleCompletion
- or
- // Flow from last element of non-`case` statement `i` to first element of statement `i+1`
- exists(int i | last(SwithStmtInternal::getStmt(this, i), pred, c) |
- not SwithStmtInternal::getStmt(this, i) instanceof CaseStmt and
- c instanceof NormalCompletion and
- first(SwithStmtInternal::getStmt(this, i + 1), succ)
- )
- or
- // Flow from last element of `case` statement `i` to first element of statement `i+1`
- exists(int i, Stmt body |
- body = caseStmtGetBody(SwithStmtInternal::getStmt(this, i)) and
- // in case of fall-through cases, make sure to not jump from their shared body back
- // to one of the fall-through cases
- not body = caseStmtGetBody(SwithStmtInternal::getStmt(this, i + 1)) and
- last(body, pred, c)
- |
- c instanceof NormalCompletion and
- first(SwithStmtInternal::getStmt(this, i + 1), succ)
- )
- }
- }
-
- private class CaseStmtTree extends PreOrderTree, CaseTree instanceof CaseStmt {
- final override predicate last(AstNode last, Completion c) {
- // Condition exists with a `false` completion
- last(super.getCondition(), last, c) and
- c instanceof FalseCompletion
- or
- // Case pattern exits with a non-match
- last(super.getPattern(), last, c) and
- not c.(MatchingCompletion).isMatch()
- or
- // Case body exits with any completion
- last(caseStmtGetBody(this), last, c)
- }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- CaseTree.super.succ(pred, succ, c)
- or
- pred = this and
- first(super.getPattern(), succ) and
- c instanceof SimpleCompletion
- }
- }
-
- abstract private class LoopStmtTree extends PreOrderTree instanceof LoopStmt {
- final override predicate propagatesAbnormal(AstNode child) { child = super.getCondition() }
-
- final override predicate last(AstNode last, Completion c) {
- // Condition exits with a false completion
- last(super.getCondition(), last, c) and
- c instanceof FalseCompletion
- or
- // Body exits with a break completion
- last(super.getBody(), last, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion())
- or
- // Body exits with a completion that does not continue the loop
- last(super.getBody(), last, c) and
- not c instanceof BreakCompletion and
- not c.continuesLoop()
- }
-
- override predicate succ(AstNode pred, AstNode succ, Completion c) {
- // Flow from last element of condition to first element of loop body
- last(super.getCondition(), pred, c) and
- c instanceof TrueCompletion and
- first(super.getBody(), succ)
- or
- // Flow from last element of loop body back to first element of condition
- not this instanceof ForStmt and
- last(super.getBody(), pred, c) and
- c.continuesLoop() and
- first(super.getCondition(), succ)
- }
- }
-
- private class WhileStmtTree extends LoopStmtTree instanceof WhileStmt {
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- LoopStmtTree.super.succ(pred, succ, c)
- or
- pred = this and
- first(super.getCondition(), succ) and
- c instanceof SimpleCompletion
- }
- }
-
- private class DoStmtTree extends LoopStmtTree instanceof DoStmt {
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- LoopStmtTree.super.succ(pred, succ, c)
- or
- pred = this and
- first(super.getBody(), succ) and
- c instanceof SimpleCompletion
- }
- }
-
- private class ForStmtTree extends LoopStmtTree instanceof ForStmt {
- /** Gets the condition if it exists, otherwise the body. */
- private AstNode getConditionOrBody() {
- result = super.getCondition()
- or
- not exists(super.getCondition()) and
- result = super.getBody()
- }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- LoopStmtTree.super.succ(pred, succ, c)
- or
- // Pre-order: flow from statement itself to first element of first initializer/
- // condition/loop body
- exists(AstNode next |
- pred = this and
- first(next, succ) and
- c instanceof SimpleCompletion
- |
- next = super.getInitializer(0)
- or
- not exists(super.getInitializer(0)) and
- next = this.getConditionOrBody()
- )
- or
- // Flow from last element of initializer `i` to first element of initializer `i+1`
- exists(int i | last(super.getInitializer(i), pred, c) |
- c instanceof NormalCompletion and
- first(super.getInitializer(i + 1), succ)
- )
- or
- // Flow from last element of last initializer to first element of condition/loop body
- exists(int last | last = max(int i | exists(super.getInitializer(i))) |
- last(super.getInitializer(last), pred, c) and
- c instanceof NormalCompletion and
- first(this.getConditionOrBody(), succ)
- )
- or
- // Flow from last element of condition into first element of loop body
- last(super.getCondition(), pred, c) and
- c instanceof TrueCompletion and
- first(super.getBody(), succ)
- or
- // Flow from last element of loop body to first element of update/condition/self
- exists(AstNode next |
- last(super.getBody(), pred, c) and
- c.continuesLoop() and
- first(next, succ) and
- if exists(super.getUpdate(0))
- then next = super.getUpdate(0)
- else next = this.getConditionOrBody()
- )
- or
- // Flow from last element of update to first element of next update/condition/loop body
- exists(AstNode next, int i |
- last(super.getUpdate(i), pred, c) and
- c instanceof NormalCompletion and
- first(next, succ) and
- if exists(super.getUpdate(i + 1))
- then next = super.getUpdate(i + 1)
- else next = this.getConditionOrBody()
- )
- }
- }
-
- private class ForeachStmtTree extends ControlFlowTree instanceof ForeachStmt {
- final override predicate propagatesAbnormal(AstNode child) { child = super.getIterableExpr() }
-
- final override predicate first(AstNode first) {
- // Unlike most other statements, `foreach` statements are not modeled in
- // pre-order, because we use the `foreach` node itself to represent the
- // emptiness test that determines whether to execute the loop body
- first(super.getIterableExpr(), first)
- }
-
- final override predicate last(AstNode last, Completion c) {
- // Emptiness test exits with no more elements
- last = this and
- c.(EmptinessCompletion).isEmpty()
- or
- // Body exits with a break completion
- last(super.getBody(), last, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion())
- or
- // Body exits abnormally
- last(super.getBody(), last, c) and
- not c instanceof NormalCompletion and
- not c instanceof ContinueCompletion and
- not c instanceof BreakCompletion
- }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- // Flow from last element of iterator expression to emptiness test
- last(super.getIterableExpr(), pred, c) and
- c instanceof NormalCompletion and
- succ = this
- or
- // Flow from emptiness test to first element of variable declaration/loop body
- pred = this and
- c = any(EmptinessCompletion ec | not ec.isEmpty()) and
- (
- first(super.getVariableDeclExpr(), succ)
- or
- first(super.getVariableDeclTuple(), succ)
- or
- not exists(super.getVariableDeclExpr()) and
- not exists(super.getVariableDeclTuple()) and
- first(super.getBody(), succ)
- )
- or
- // Flow from last element of variable declaration to first element of loop body
- (
- last(super.getVariableDeclExpr(), pred, c) or
- last(super.getVariableDeclTuple(), pred, c)
- ) and
- c instanceof SimpleCompletion and
- first(super.getBody(), succ)
- or
- // Flow from last element of loop body back to emptiness test
- last(super.getBody(), pred, c) and
- c.continuesLoop() and
- succ = this
- }
- }
-
- pragma[nomagic]
- private AstNode lastLastCatchClause(CatchClause cc, Completion c) {
- cc.isLast() and
- last(cc, result, c)
- }
-
- pragma[nomagic]
- private AstNode lastCatchClauseBlock(CatchClause cc, Completion c) {
- last(cc.getBlock(), result, c)
- }
-
- /** Gets a child of `cfe` that is in CFG scope `scope`. */
- pragma[noinline]
- private ControlFlowElement getAChildInScope(AstNode cfe, Callable scope) {
- result = getAChild(cfe) and
- scope = result.getEnclosingCallable()
- }
-
- class TryStmtTree extends PreOrderTree instanceof TryStmt {
- final override predicate propagatesAbnormal(AstNode child) { child = super.getFinally() }
-
- /**
- * Gets a descendant that belongs to the finally block of this try statement.
- */
- AstNode getAFinallyDescendant() {
- result = super.getFinally()
- or
- exists(ControlFlowElement mid |
- mid = this.getAFinallyDescendant() and
- result = getAChildInScope(mid, mid.getEnclosingCallable()) and
- not exists(TryStmt nestedTry |
- result = nestedTry.getFinally() and
- nestedTry != this
- )
- )
- }
-
- /**
- * Holds if `innerTry` has a finally block and is immediately nested inside the
- * finally block of this try statement.
- */
- private predicate nestedFinally(TryStmt innerTry) {
- exists(AstNode innerFinally |
- innerFinally = getAChildInScope(this.getAFinallyDescendant(), super.getEnclosingCallable()) and
- innerFinally = innerTry.getFinally()
- )
- }
-
- /**
- * Gets the finally-nesting level of this try statement. That is, the number of
- * finally blocks that this try statement is nested under.
- */
- int nestLevel() { result = count(TryStmtTree outer | outer.nestedFinally+(this)) }
-
- /** Holds if `last` is a last element of the block of this try statement. */
- pragma[nomagic]
- predicate lastBlock(AstNode last, Completion c) { last(super.getBlock(), last, c) }
-
- /**
- * Gets a last element from a `try` or `catch` block of this try statement
- * that may finish with completion `c`, such that control may be transferred
- * to the finally block (if it exists), but only if `finalizable = true`.
- */
- pragma[nomagic]
- AstNode getAFinallyPredecessor(Completion c, boolean finalizable) {
- // Exit completions skip the finally block
- (if c instanceof ExitCompletion then finalizable = false else finalizable = true) and
- (
- this.lastBlock(result, c) and
- (
- // Any non-throw completion from the `try` block will always continue directly
- // to the finally block
- not c instanceof ThrowCompletion
- or
- // Any completion from the `try` block will continue to the finally block
- // when there are no catch clauses
- not exists(super.getACatchClause())
- )
- or
- // Last element from any of the `catch` clause blocks continues to the finally block
- result = lastCatchClauseBlock(super.getACatchClause(), c)
- or
- // Last element of last `catch` clause continues to the finally block
- result = lastLastCatchClause(super.getACatchClause(), c)
- )
- }
-
- pragma[nomagic]
- private predicate lastFinally0(AstNode last, Completion c) { last(super.getFinally(), last, c) }
-
- pragma[nomagic]
- private predicate lastFinally(
- AstNode last, NormalCompletion finally, Completion outer, int nestLevel
- ) {
- this.lastFinally0(last, finally) and
- exists(
- this.getAFinallyPredecessor(any(Completion c0 | outer = c0.getOuterCompletion()), true)
- ) and
- nestLevel = this.nestLevel()
- }
-
- final override predicate last(AstNode last, Completion c) {
- exists(boolean finalizable | last = this.getAFinallyPredecessor(c, finalizable) |
- // If there is no finally block, last elements are from the body, from
- // the blocks of one of the `catch` clauses, or from the last `catch` clause
- not super.hasFinally()
- or
- finalizable = false
- )
- or
- this.lastFinally(last, c, any(NormalCompletion nc), _)
- or
- // If the finally block completes normally, it inherits any non-normal
- // completion that was current before the finally block was entered
- exists(int nestLevel |
- c =
- any(NestedCompletion nc |
- this.lastFinally(last, nc.getAnInnerCompatibleCompletion(), nc.getOuterCompletion(),
- nestLevel) and
- // unbind
- nc.getNestLevel() >= nestLevel and
- nc.getNestLevel() <= nestLevel
- )
- )
- }
-
- /**
- * Gets an exception type that is thrown by `cfe` in the block of this `try`
- * statement. Throw completion `c` matches the exception type.
- */
- ExceptionClass getAThrownException(AstNode cfe, ThrowCompletion c) {
- this.lastBlock(cfe, c) and
- result = c.getExceptionClass()
- }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- // Pre-order: flow from statement itself to first element of body
- pred = this and
- first(super.getBlock(), succ) and
- c instanceof SimpleCompletion
- or
- // Flow from last element of body to first `catch` clause
- exists(this.getAThrownException(pred, c)) and
- first(super.getCatchClause(0), succ)
- or
- exists(CatchClause cc, int i | cc = super.getCatchClause(i) |
- // Flow from one `catch` clause to the next
- pred = cc and
- last(super.getCatchClause(i), cc, c) and
- first(super.getCatchClause(i + 1), succ) and
- c = any(MatchingCompletion mc | not mc.isMatch())
- or
- // Flow from last element of `catch` clause filter to next `catch` clause
- last(super.getCatchClause(i), pred, c) and
- last(cc.getFilterClause(), pred, _) and
- first(super.getCatchClause(i + 1), succ) and
- c instanceof FalseCompletion
- )
- or
- // Flow into finally block
- pred = this.getAFinallyPredecessor(c, true) and
- first(super.getFinally(), succ)
- }
- }
-
- private class SpecificCatchClauseTree extends PreOrderTree instanceof SpecificCatchClause {
- final override predicate propagatesAbnormal(AstNode child) { child = super.getFilterClause() }
-
- pragma[nomagic]
- private predicate lastFilterClause(AstNode last, Completion c) {
- last(super.getFilterClause(), last, c)
- }
-
- /**
- * Holds if the `try` block that this catch clause belongs to may throw an
- * exception of type `c`, where no `catch` clause is guaranteed to catch it.
- * This catch clause is the last catch clause in the try statement that
- * it belongs to.
- */
- pragma[nomagic]
- private predicate throwMayBeUncaught(ThrowCompletion c) {
- exists(TryStmt ts |
- ts = super.getTryStmt() and
- ts.(TryStmtTree).lastBlock(_, c) and
- not ts.getACatchClause() instanceof GeneralCatchClause and
- forall(SpecificCatchClause scc | scc = ts.getACatchClause() |
- scc.hasFilterClause()
- or
- not c.getExceptionClass().getABaseType*() = scc.getCaughtExceptionType()
- ) and
- super.isLast()
- )
- }
-
- final override predicate last(AstNode last, Completion c) {
- // Last element of `catch` block
- last(super.getBlock(), last, c)
- or
- not super.isLast() and
- (
- // Incompatible exception type: clause itself
- last = this and
- c.(MatchingCompletion).isNonMatch()
- or
- // Incompatible filter
- this.lastFilterClause(last, c) and
- c instanceof FalseCompletion
- )
- or
- // Last `catch` clause inherits throw completions from the `try` block,
- // when the clause does not match
- super.isLast() and
- c =
- any(NestedCompletion nc |
- nc.getNestLevel() = 0 and
- this.throwMayBeUncaught(nc.getOuterCompletion()) and
- (
- // Incompatible exception type: clause itself
- last = this and
- nc.getInnerCompletion() =
- any(MatchingCompletion mc |
- mc.isNonMatch() and
- mc.isValidFor(this)
- )
- or
- // Incompatible filter
- this.lastFilterClause(last, nc.getInnerCompletion().(FalseCompletion))
- )
- )
- }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- // Flow from catch clause to variable declaration/filter clause/block
- pred = this and
- c.(MatchingCompletion).isMatch() and
- exists(AstNode next | first(next, succ) |
- if exists(super.getVariableDeclExpr())
- then next = super.getVariableDeclExpr()
- else
- if exists(super.getFilterClause())
- then next = super.getFilterClause()
- else next = super.getBlock()
- )
- or
- // Flow from variable declaration to filter clause/block
- last(super.getVariableDeclExpr(), pred, c) and
- c instanceof SimpleCompletion and
- exists(AstNode next | first(next, succ) |
- if exists(super.getFilterClause())
- then next = super.getFilterClause()
- else next = super.getBlock()
- )
- or
- // Flow from filter to block
- last(super.getFilterClause(), pred, c) and
- c instanceof TrueCompletion and
- first(super.getBlock(), succ)
- }
- }
-
- private class JumpStmtTree extends PostOrderTree instanceof JumpStmt {
- final override predicate propagatesAbnormal(AstNode child) { child = this.getChild(0) }
-
- final override predicate first(AstNode first) {
- first(this.getChild(0), first)
- or
- not exists(this.getChild(0)) and first = this
- }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- last(this.getChild(0), pred, c) and
- succ = this and
- c instanceof NormalCompletion
- }
- }
-
- pragma[nomagic]
- private predicate goto(ControlFlowElement cfe, GotoCompletion gc, string label, Callable enclosing) {
- last(_, cfe, gc) and
- // Special case: when a `goto` happens inside a try statement with a
- // finally block, flow does not go directly to the target, but instead
- // to the finally block (and from there possibly to the target)
- not cfe =
- any(Statements::TryStmtTree t | t.(TryStmt).hasFinally()).getAFinallyPredecessor(_, true) and
- label = gc.getLabel() and
- enclosing = cfe.getEnclosingCallable()
- }
-
- private class LabeledStmtTree extends PreOrderTree instanceof LabeledStmt {
- final override predicate propagatesAbnormal(AstNode child) { none() }
-
- final override predicate last(AstNode last, Completion c) {
- if this instanceof DefaultCase
- then last(super.getStmt(), last, c)
- else (
- not this instanceof CaseStmt and
- last = this and
- c.isValidFor(this)
- )
- }
-
- pragma[noinline]
- predicate hasLabelInCallable(string label, Callable c) {
- super.getEnclosingCallable() = c and
- label = super.getLabel()
- }
-
- final override predicate succ(AstNode pred, AstNode succ, Completion c) {
- this instanceof DefaultCase and
- pred = this and
- first(super.getStmt(), succ) and
- c instanceof SimpleCompletion
- or
- // Flow from element with matching `goto` completion to this statement
- exists(string label, Callable enclosing |
- goto(pred, c, label, enclosing) and
- this.hasLabelInCallable(label, enclosing) and
- succ = this
- )
- }
- }
-}
-
-/** A control flow element that is split into multiple control flow nodes. */
-class SplitAstNode extends AstNode, ControlFlowElement {
- SplitAstNode() { strictcount(this.getAControlFlowNode()) > 1 }
-}
diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/NonReturning.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/NonReturning.qll
index 10f92d882b79..45f802619bed 100644
--- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/NonReturning.qll
+++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/NonReturning.qll
@@ -9,13 +9,9 @@ import csharp
private import semmle.code.csharp.ExprOrStmtParent
private import semmle.code.csharp.commons.Assertions
private import semmle.code.csharp.frameworks.System
-private import Completion
/** A call that definitely does not return (conservative analysis). */
-abstract class NonReturningCall extends Call {
- /** Gets a valid completion for this non-returning call. */
- abstract Completion getACompletion();
-}
+abstract class NonReturningCall extends Call { }
private class ExitingCall extends NonReturningCall {
ExitingCall() {
@@ -23,36 +19,21 @@ private class ExitingCall extends NonReturningCall {
or
this = any(FailingAssertion fa | fa.getAssertionFailure().isExit())
}
-
- override ExitCompletion getACompletion() { not result instanceof NestedCompletion }
}
private class ThrowingCall extends NonReturningCall {
- private ThrowCompletion c;
-
ThrowingCall() {
- not c instanceof NestedCompletion and
- (
- c = this.getTarget().(ThrowingCallable).getACallCompletion()
- or
- this.(FailingAssertion).getAssertionFailure().isException(c.getExceptionClass())
- or
- this =
- any(MethodCall mc |
- mc.getTarget()
- .hasFullyQualifiedName("System.Runtime.ExceptionServices", "ExceptionDispatchInfo",
- "Throw") and
- (
- mc.hasNoArguments() and
- c.getExceptionClass() instanceof SystemExceptionClass
- or
- c.getExceptionClass() = mc.getArgument(0).getType()
- )
- )
- )
+ this.getTarget() instanceof ThrowingCallable
+ or
+ this.(FailingAssertion).getAssertionFailure().isException(_)
+ or
+ this =
+ any(MethodCall mc |
+ mc.getTarget()
+ .hasFullyQualifiedName("System.Runtime.ExceptionServices", "ExceptionDispatchInfo",
+ "Throw")
+ )
}
-
- override ThrowCompletion getACompletion() { result = c }
}
/** Holds if accessor `a` has an auto-implementation. */
@@ -107,44 +88,35 @@ private Stmt getAnExitingStmt() {
private class ThrowingCallable extends NonReturningCallable {
ThrowingCallable() {
- forex(ControlFlowElement body | body = this.getBody() | body = getAThrowingElement(_))
+ forex(ControlFlowElement body | body = this.getBody() | body = getAThrowingElement())
}
-
- /** Gets a valid completion for a call to this throwing callable. */
- ThrowCompletion getACallCompletion() { this.getBody() = getAThrowingElement(result) }
}
-private predicate directlyThrows(ThrowElement te, ThrowCompletion c) {
- c.getExceptionClass() = te.getThrownExceptionType() and
- not c instanceof NestedCompletion and
+private predicate directlyThrows(ThrowElement te) {
// For stub implementations, there may exist proper implementations that are not seen
// during compilation, so we conservatively rule those out
not isStub(te)
}
-private ControlFlowElement getAThrowingElement(ThrowCompletion c) {
- c = result.(ThrowingCall).getACompletion()
+private ControlFlowElement getAThrowingElement() {
+ result instanceof ThrowingCall
or
- directlyThrows(result, c)
+ directlyThrows(result)
or
- result = getAThrowingStmt(c)
+ result = getAThrowingStmt()
}
-private Stmt getAThrowingStmt(ThrowCompletion c) {
- directlyThrows(result, c)
+private Stmt getAThrowingStmt() {
+ directlyThrows(result)
or
- result.(ExprStmt).getExpr() = getAThrowingElement(c)
+ result.(ExprStmt).getExpr() = getAThrowingElement()
or
- result.(BlockStmt).getFirstStmt() = getAThrowingStmt(c)
+ result.(BlockStmt).getFirstStmt() = getAThrowingStmt()
or
- exists(IfStmt ifStmt, ThrowCompletion c1, ThrowCompletion c2 |
+ exists(IfStmt ifStmt |
result = ifStmt and
- ifStmt.getThen() = getAThrowingStmt(c1) and
- ifStmt.getElse() = getAThrowingStmt(c2)
- |
- c = c1
- or
- c = c2
+ ifStmt.getThen() = getAThrowingStmt() and
+ ifStmt.getElse() = getAThrowingStmt()
)
}
diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Splitting.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Splitting.qll
deleted file mode 100644
index 55b75ed31a71..000000000000
--- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Splitting.qll
+++ /dev/null
@@ -1,124 +0,0 @@
-/**
- * INTERNAL: Do not use.
- *
- * Provides classes and predicates relevant for splitting the control flow graph.
- */
-
-import csharp
-private import Completion as Comp
-private import Comp
-private import ControlFlowGraphImpl
-private import semmle.code.csharp.controlflow.ControlFlowGraph::ControlFlow as Cfg
-
-cached
-private module Cached {
- private import semmle.code.csharp.Caching
-
- cached
- newtype TSplitKind = TConditionalCompletionSplitKind()
-
- cached
- newtype TSplit = TConditionalCompletionSplit(ConditionalCompletion c)
-}
-
-import Cached
-
-/**
- * A split for a control flow element. For example, a tag that determines how to
- * continue execution after leaving a `finally` block.
- */
-class Split extends TSplit {
- /** Gets a textual representation of this split. */
- string toString() { none() }
-}
-
-module ConditionalCompletionSplitting {
- /**
- * A split for conditional completions. For example, in
- *
- * ```csharp
- * void M(int i)
- * {
- * if (x && !y)
- * System.Console.WriteLine("true")
- * }
- * ```
- *
- * we record whether `x`, `y`, and `!y` evaluate to `true` or `false`, and restrict
- * the edges out of `!y` and `x && !y` accordingly.
- */
- class ConditionalCompletionSplit extends Split, TConditionalCompletionSplit {
- ConditionalCompletion completion;
-
- ConditionalCompletionSplit() { this = TConditionalCompletionSplit(completion) }
-
- ConditionalCompletion getCompletion() { result = completion }
-
- override string toString() { result = completion.toString() }
- }
-
- private class ConditionalCompletionSplitKind_ extends SplitKind, TConditionalCompletionSplitKind {
- override int getListOrder() { result = 0 }
-
- override predicate isEnabled(AstNode cfe) { this.appliesTo(cfe) }
-
- override string toString() { result = "ConditionalCompletion" }
- }
-
- module ConditionalCompletionSplittingInput {
- private import Completion as Comp
-
- class ConditionalCompletion = Comp::ConditionalCompletion;
-
- class ConditionalCompletionSplitKind extends ConditionalCompletionSplitKind_, TSplitKind { }
-
- class ConditionalCompletionSplit = ConditionalCompletionSplitting::ConditionalCompletionSplit;
-
- bindingset[parent, parentCompletion]
- predicate condPropagateExpr(
- AstNode parent, ConditionalCompletion parentCompletion, AstNode child,
- ConditionalCompletion childCompletion
- ) {
- child = parent.(LogicalNotExpr).getOperand() and
- childCompletion.getDual() = parentCompletion
- or
- childCompletion = parentCompletion and
- (
- child = parent.(LogicalAndExpr).getAnOperand()
- or
- child = parent.(LogicalOrExpr).getAnOperand()
- or
- parent = any(ConditionalExpr ce | child = [ce.getThen(), ce.getElse()])
- or
- child = parent.(SwitchExpr).getACase()
- or
- child = parent.(SwitchCaseExpr).getBody()
- or
- parent =
- any(NullCoalescingExpr nce |
- if childCompletion instanceof NullnessCompletion
- then child = nce.getRightOperand()
- else child = nce.getAnOperand()
- )
- )
- or
- child = parent.(NotPatternExpr).getPattern() and
- childCompletion.getDual() = parentCompletion
- or
- child = parent.(IsExpr).getPattern() and
- parentCompletion.(BooleanCompletion).getValue() =
- childCompletion.(MatchingCompletion).getValue()
- or
- childCompletion = parentCompletion and
- (
- child = parent.(AndPatternExpr).getAnOperand()
- or
- child = parent.(OrPatternExpr).getAnOperand()
- or
- child = parent.(RecursivePatternExpr).getAChildExpr()
- or
- child = parent.(PropertyPatternExpr).getPattern(_)
- )
- }
- }
-}
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll
index c7ac34d3d01a..0affdf4f8da3 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll
@@ -42,7 +42,7 @@ private Expr maybeNullExpr(Expr reason) {
ce.getElse() = maybeNullExpr(reason)
)
or
- result.(NullCoalescingExpr).getRightOperand() = maybeNullExpr(reason)
+ result.(NullCoalescingOperation).getRightOperand() = maybeNullExpr(reason)
or
result =
any(QualifiableExpr qe |
@@ -126,7 +126,7 @@ private predicate nonNullDef(Ssa::ExplicitDefinition def) {
/**
* Holds if `node` is a dereference `d` of SSA definition `def`.
*/
-private predicate dereferenceAt(ControlFlow::Node node, Ssa::Definition def, Dereference d) {
+private predicate dereferenceAt(ControlFlowNode node, Ssa::Definition def, Dereference d) {
d = def.getAReadAtNode(node)
}
@@ -192,9 +192,7 @@ private predicate isNullDefaultArgument(Ssa::ImplicitParameterDefinition def, Al
}
/** Holds if `def` is an SSA definition that may be `null`. */
-private predicate defMaybeNull(
- Ssa::Definition def, ControlFlow::Node node, string msg, Element reason
-) {
+private predicate defMaybeNull(Ssa::Definition def, ControlFlowNode node, string msg, Element reason) {
not nonNullDef(def) and
(
// A variable compared to `null` might be `null`
@@ -224,7 +222,7 @@ private predicate defMaybeNull(
or
// If the source of a variable is `null` then the variable may be `null`
exists(AssignableDefinition adef | adef = def.(Ssa::ExplicitDefinition).getADefinition() |
- adef.getSource() = maybeNullExpr(node.getAstNode()) and
+ adef.getSource() = maybeNullExpr(node.asExpr()) and
reason = adef.getExpr() and
msg = "because of $@ assignment"
)
@@ -256,19 +254,19 @@ private Ssa::Definition getAnUltimateDefinition(Ssa::Definition def) {
* through an intermediate dereference that always throws a null reference
* exception.
*/
-private predicate defReaches(Ssa::Definition def, ControlFlow::Node cfn) {
+private predicate defReaches(Ssa::Definition def, ControlFlowNode cfn) {
exists(def.getAFirstReadAtNode(cfn))
or
- exists(ControlFlow::Node mid | defReaches(def, mid) |
+ exists(ControlFlowNode mid | defReaches(def, mid) |
SsaImpl::adjacentReadPairSameVar(_, mid, cfn) and
not mid = any(Dereference d | d.isAlwaysNull(def.getSourceVariable())).getAControlFlowNode()
)
}
private module NullnessConfig implements ControlFlowReachability::ConfigSig {
- predicate source(ControlFlow::Node node, Ssa::Definition def) { defMaybeNull(def, node, _, _) }
+ predicate source(ControlFlowNode node, Ssa::Definition def) { defMaybeNull(def, node, _, _) }
- predicate sink(ControlFlow::Node node, Ssa::Definition def) {
+ predicate sink(ControlFlowNode node, Ssa::Definition def) {
exists(Dereference d |
dereferenceAt(node, def, d) and
not d instanceof NonNullExpr
@@ -283,9 +281,7 @@ private module NullnessConfig implements ControlFlowReachability::ConfigSig {
private module NullnessFlow = ControlFlowReachability::Flow;
predicate maybeNullDeref(Dereference d, Ssa::SourceVariable v, string msg, Element reason) {
- exists(
- Ssa::Definition origin, Ssa::Definition ssa, ControlFlow::Node src, ControlFlow::Node sink
- |
+ exists(Ssa::Definition origin, Ssa::Definition ssa, ControlFlowNode src, ControlFlowNode sink |
defMaybeNull(origin, src, msg, reason) and
NullnessFlow::flow(src, origin, sink, ssa) and
ssa.getSourceVariable() = v and
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll
index e8180201b9a8..11124cc20949 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll
@@ -164,10 +164,8 @@ module Ssa {
*/
class Definition extends SsaImpl::Definition {
/** Gets the control flow node of this SSA definition. */
- final ControlFlow::Node getControlFlowNode() {
- exists(ControlFlow::BasicBlock bb, int i | this.definesAt(_, bb, i) |
- result = bb.getNode(0.maximum(i))
- )
+ final ControlFlowNode getControlFlowNode() {
+ exists(BasicBlock bb, int i | this.definesAt(_, bb, i) | result = bb.getNode(0.maximum(i)))
}
/**
@@ -176,9 +174,7 @@ module Ssa {
* point it is still live, without crossing another SSA definition of the
* same source variable.
*/
- final predicate isLiveAtEndOfBlock(ControlFlow::BasicBlock bb) {
- SsaImpl::isLiveAtEndOfBlock(this, bb)
- }
+ final predicate isLiveAtEndOfBlock(BasicBlock bb) { SsaImpl::isLiveAtEndOfBlock(this, bb) }
/**
* Gets a read of the source variable underlying this SSA definition that
@@ -236,7 +232,7 @@ module Ssa {
* - The reads of `this.Field` on lines 10 and 11 can be reached from the phi
* node between lines 9 and 10.
*/
- final AssignableRead getAReadAtNode(ControlFlow::Node cfn) {
+ final AssignableRead getAReadAtNode(ControlFlowNode cfn) {
result = SsaImpl::getAReadAtNode(this, cfn)
}
@@ -310,7 +306,7 @@ module Ssa {
* Subsequent reads can be found by following the steps defined by
* `AssignableRead.getANextRead()`.
*/
- final AssignableRead getAFirstReadAtNode(ControlFlow::Node cfn) {
+ final AssignableRead getAFirstReadAtNode(ControlFlowNode cfn) {
SsaImpl::firstReadSameVar(this, cfn) and
result.getAControlFlowNode() = cfn
}
@@ -373,7 +369,7 @@ module Ssa {
* - The read of `this.Field` on line 11 is a last read of the phi node
* between lines 9 and 10.
*/
- deprecated final AssignableRead getALastReadAtNode(ControlFlow::Node cfn) {
+ deprecated final AssignableRead getALastReadAtNode(ControlFlowNode cfn) {
SsaImpl::lastReadSameVar(this, cfn) and
result.getAControlFlowNode() = cfn
}
@@ -426,7 +422,9 @@ module Ssa {
* This is either an expression, for example `x = 0`, a parameter, or a
* callable. Phi nodes have no associated syntax element.
*/
- Element getElement() { result = this.getControlFlowNode().getAstNode() }
+ Element getElement() {
+ result.(ControlFlowElement).getControlFlowNode() = this.getControlFlowNode()
+ }
/** Gets the callable to which this SSA definition belongs. */
final Callable getEnclosingCallable() {
@@ -484,7 +482,7 @@ module Ssa {
* `M2` via the call on line 6.
*/
deprecated final predicate isCapturedVariableDefinitionFlowIn(
- ImplicitEntryDefinition def, ControlFlow::Nodes::ElementNode c, boolean additionalCalls
+ ImplicitEntryDefinition def, ControlFlowNodes::ElementNode c, boolean additionalCalls
) {
none()
}
@@ -520,9 +518,7 @@ module Ssa {
override Element getElement() { result = ad.getElement() }
- override string toString() {
- result = SsaImpl::getToStringPrefix(this) + "SSA def(" + this.getSourceVariable() + ")"
- }
+ override string toString() { result = "SSA def(" + this.getSourceVariable() + ")" }
override Location getLocation() { result = ad.getLocation() }
}
@@ -536,7 +532,7 @@ module Ssa {
*/
class ImplicitDefinition extends Definition, SsaImpl::WriteDefinition {
ImplicitDefinition() {
- exists(ControlFlow::BasicBlock bb, SourceVariable v, int i | this.definesAt(v, bb, i) |
+ exists(BasicBlock bb, SourceVariable v, int i | this.definesAt(v, bb, i) |
SsaImpl::implicitEntryDefinition(bb, v) and
i = -1
or
@@ -554,25 +550,21 @@ module Ssa {
*/
class ImplicitEntryDefinition extends ImplicitDefinition {
ImplicitEntryDefinition() {
- exists(ControlFlow::BasicBlock bb, SourceVariable v |
+ exists(BasicBlock bb, SourceVariable v |
this.definesAt(v, bb, -1) and
SsaImpl::implicitEntryDefinition(bb, v)
)
}
/** Gets the callable that this entry definition belongs to. */
- final Callable getCallable() { result = this.getBasicBlock().getCallable() }
+ final Callable getCallable() { result = this.getBasicBlock().getEnclosingCallable() }
override Element getElement() { result = this.getCallable() }
override string toString() {
if this.getSourceVariable().getAssignable() instanceof LocalScopeVariable
- then
- result =
- SsaImpl::getToStringPrefix(this) + "SSA capture def(" + this.getSourceVariable() + ")"
- else
- result =
- SsaImpl::getToStringPrefix(this) + "SSA entry def(" + this.getSourceVariable() + ")"
+ then result = "SSA capture def(" + this.getSourceVariable() + ")"
+ else result = "SSA entry def(" + this.getSourceVariable() + ")"
}
override Location getLocation() { result = this.getCallable().getLocation() }
@@ -582,7 +574,7 @@ module Ssa {
class C = ImplicitParameterDefinition;
predicate relevantLocations(ImplicitParameterDefinition def, Location l1, Location l2) {
- not def.getBasicBlock() instanceof ControlFlow::BasicBlocks::EntryBlock and
+ not def.getBasicBlock() instanceof EntryBasicBlock and
l1 = def.getParameter().getALocation() and
l2 = def.getBasicBlock().getLocation()
}
@@ -613,9 +605,7 @@ module Ssa {
override Element getElement() { result = this.getParameter() }
- override string toString() {
- result = SsaImpl::getToStringPrefix(this) + "SSA param(" + this.getParameter() + ")"
- }
+ override string toString() { result = "SSA param(" + this.getParameter() + ")" }
override Location getLocation() {
not NearestLocation::nearestLocation(this, _, _) and
@@ -634,7 +624,7 @@ module Ssa {
private Call c;
ImplicitCallDefinition() {
- exists(ControlFlow::BasicBlock bb, SourceVariable v, int i |
+ exists(BasicBlock bb, SourceVariable v, int i |
this.definesAt(v, bb, i) and
SsaImpl::updatesNamedFieldOrProp(bb, i, c, v, _)
)
@@ -656,9 +646,7 @@ module Ssa {
)
}
- override string toString() {
- result = SsaImpl::getToStringPrefix(this) + "SSA call def(" + this.getSourceVariable() + ")"
- }
+ override string toString() { result = "SSA call def(" + this.getSourceVariable() + ")" }
override Location getLocation() { result = this.getCall().getLocation() }
}
@@ -671,9 +659,7 @@ module Ssa {
private Definition q;
ImplicitQualifierDefinition() {
- exists(
- ControlFlow::BasicBlock bb, int i, SourceVariables::QualifiedFieldOrPropSourceVariable v
- |
+ exists(BasicBlock bb, int i, SourceVariables::QualifiedFieldOrPropSourceVariable v |
this.definesAt(v, bb, i)
|
SsaImpl::variableWriteQualifier(bb, i, v, _) and
@@ -684,10 +670,7 @@ module Ssa {
/** Gets the SSA definition for the qualifier. */
final Definition getQualifierDefinition() { result = q }
- override string toString() {
- result =
- SsaImpl::getToStringPrefix(this) + "SSA qualifier def(" + this.getSourceVariable() + ")"
- }
+ override string toString() { result = "SSA qualifier def(" + this.getSourceVariable() + ")" }
override Location getLocation() { result = this.getQualifierDefinition().getLocation() }
}
@@ -723,13 +706,11 @@ module Ssa {
final Definition getAnInput() { this.hasInputFromBlock(result, _) }
/** Holds if `inp` is an input to this phi node along the edge originating in `bb`. */
- predicate hasInputFromBlock(Definition inp, ControlFlow::BasicBlock bb) {
+ predicate hasInputFromBlock(Definition inp, BasicBlock bb) {
inp = SsaImpl::phiHasInputFromBlock(this, bb)
}
- override string toString() {
- result = SsaImpl::getToStringPrefix(this) + "SSA phi(" + this.getSourceVariable() + ")"
- }
+ override string toString() { result = "SSA phi(" + this.getSourceVariable() + ")" }
/*
* The location of a phi node is the same as the location of the first node
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/SignAnalysis.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/SignAnalysis.qll
index 6f6f38bd199f..e7d3590e0fde 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/SignAnalysis.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/SignAnalysis.qll
@@ -11,36 +11,36 @@ private import semmle.code.csharp.dataflow.internal.rangeanalysis.SignAnalysisCo
/** Holds if `e` can be positive and cannot be negative. */
predicate positiveExpr(Expr e) {
- forex(ControlFlow::Node cfn | cfn = e.getAControlFlowNode() |
+ forex(ControlFlowNode cfn | cfn = e.getAControlFlowNode() |
positive(cfn) or strictlyPositive(cfn)
)
}
/** Holds if `e` can be negative and cannot be positive. */
predicate negativeExpr(Expr e) {
- forex(ControlFlow::Node cfn | cfn = e.getAControlFlowNode() |
+ forex(ControlFlowNode cfn | cfn = e.getAControlFlowNode() |
negative(cfn) or strictlyNegative(cfn)
)
}
/** Holds if `e` is strictly positive. */
predicate strictlyPositiveExpr(Expr e) {
- forex(ControlFlow::Node cfn | cfn = e.getAControlFlowNode() | strictlyPositive(cfn))
+ forex(ControlFlowNode cfn | cfn = e.getAControlFlowNode() | strictlyPositive(cfn))
}
/** Holds if `e` is strictly negative. */
predicate strictlyNegativeExpr(Expr e) {
- forex(ControlFlow::Node cfn | cfn = e.getAControlFlowNode() | strictlyNegative(cfn))
+ forex(ControlFlowNode cfn | cfn = e.getAControlFlowNode() | strictlyNegative(cfn))
}
/** Holds if `e` can be positive and cannot be negative. */
-predicate positive(ControlFlow::Nodes::ExprNode e) { Common::positive(e) }
+predicate positive(ControlFlowNodes::ExprNode e) { Common::positive(e) }
/** Holds if `e` can be negative and cannot be positive. */
-predicate negative(ControlFlow::Nodes::ExprNode e) { Common::negative(e) }
+predicate negative(ControlFlowNodes::ExprNode e) { Common::negative(e) }
/** Holds if `e` is strictly positive. */
-predicate strictlyPositive(ControlFlow::Nodes::ExprNode e) { Common::strictlyPositive(e) }
+predicate strictlyPositive(ControlFlowNodes::ExprNode e) { Common::strictlyPositive(e) }
/** Holds if `e` is strictly negative. */
-predicate strictlyNegative(ControlFlow::Nodes::ExprNode e) { Common::strictlyNegative(e) }
+predicate strictlyNegative(ControlFlowNodes::ExprNode e) { Common::strictlyNegative(e) }
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/BaseSSA.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/BaseSSA.qll
index a994873274af..3af93ee29458 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/BaseSSA.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/BaseSSA.qll
@@ -5,7 +5,6 @@ import csharp
*/
module BaseSsa {
private import AssignableDefinitions
- private import semmle.code.csharp.controlflow.BasicBlocks as BasicBlocks
private import codeql.ssa.Ssa as SsaImplCommon
/**
@@ -13,7 +12,7 @@ module BaseSsa {
* targeting local scope variable `v`.
*/
private predicate definitionAt(
- AssignableDefinition def, ControlFlow::BasicBlock bb, int i, SsaInput::SourceVariable v
+ AssignableDefinition def, BasicBlock bb, int i, SsaInput::SourceVariable v
) {
bb.getNode(i) = def.getExpr().getAControlFlowNode() and
v = def.getTarget() and
@@ -25,11 +24,9 @@ module BaseSsa {
)
}
- private predicate implicitEntryDef(
- Callable c, ControlFlow::BasicBlocks::EntryBlock bb, SsaInput::SourceVariable v
- ) {
- exists(ControlFlow::BasicBlocks::EntryBlock entry |
- c = entry.getCallable() and
+ private predicate implicitEntryDef(Callable c, EntryBasicBlock bb, SsaInput::SourceVariable v) {
+ exists(EntryBasicBlock entry |
+ c = entry.getEnclosingCallable() and
// In case `c` has multiple bodies, we want each body to get its own implicit
// entry definition. In case `c` doesn't have multiple bodies, the line below
// is simply the same as `bb = entry`, because `entry.getFirstNode().getASuccessor()`
@@ -82,10 +79,10 @@ module BaseSsa {
}
}
- private module SsaInput implements SsaImplCommon::InputSig {
+ private module SsaInput implements SsaImplCommon::InputSig {
class SourceVariable = SimpleLocalScopeVariable;
- predicate variableWrite(ControlFlow::BasicBlock bb, int i, SourceVariable v, boolean certain) {
+ predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) {
exists(AssignableDefinition def |
definitionAt(def, bb, i, v) and
if def.isCertain() then certain = true else certain = false
@@ -96,7 +93,7 @@ module BaseSsa {
certain = true
}
- predicate variableRead(ControlFlow::BasicBlock bb, int i, SourceVariable v, boolean certain) {
+ predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
exists(AssignableRead read |
read.getAControlFlowNode() = bb.getNode(i) and
read.getTarget() = v and
@@ -105,25 +102,25 @@ module BaseSsa {
}
}
- private module SsaImpl = SsaImplCommon::Make;
+ private module SsaImpl = SsaImplCommon::Make;
class Definition extends SsaImpl::Definition {
final AssignableRead getARead() {
- exists(ControlFlow::BasicBlock bb, int i |
+ exists(BasicBlock bb, int i |
SsaImpl::ssaDefReachesRead(_, this, bb, i) and
result.getAControlFlowNode() = bb.getNode(i)
)
}
final AssignableDefinition getDefinition() {
- exists(ControlFlow::BasicBlock bb, int i, SsaInput::SourceVariable v |
+ exists(BasicBlock bb, int i, SsaInput::SourceVariable v |
this.definesAt(v, bb, i) and
definitionAt(result, bb, i, v)
)
}
final predicate isImplicitEntryDefinition(SsaInput::SourceVariable v) {
- exists(ControlFlow::BasicBlock bb |
+ exists(BasicBlock bb |
this.definesAt(v, bb, -1) and
implicitEntryDef(_, bb, v)
)
@@ -142,7 +139,7 @@ module BaseSsa {
override Location getLocation() {
result = this.getDefinition().getLocation()
or
- exists(Callable c, ControlFlow::BasicBlock bb, SsaInput::SourceVariable v |
+ exists(Callable c, BasicBlock bb, SsaInput::SourceVariable v |
this.definesAt(v, bb, -1) and
implicitEntryDef(c, bb, v) and
result = c.getLocation()
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll
index be183815c715..ed45135c4d83 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll
@@ -28,8 +28,8 @@ newtype TReturnKind =
private predicate hasMultipleSourceLocations(Callable c) { strictcount(getASourceLocation(c)) > 1 }
private predicate objectInitEntry(ObjectInitMethod m, ControlFlowElement first) {
- exists(ControlFlow::Nodes::EntryNode en |
- en.getCallable() = m and first.getControlFlowNode() = en.getASuccessor()
+ exists(ControlFlow::EntryNode en |
+ en.getEnclosingCallable() = m and first = en.getASuccessor().getAstNode()
)
}
@@ -73,12 +73,12 @@ private module Cached {
cached
newtype TDataFlowCall =
- TNonDelegateCall(ControlFlow::Nodes::ElementNode cfn, DispatchCall dc) {
+ TNonDelegateCall(ControlFlowNodes::ElementNode cfn, DispatchCall dc) {
DataFlowImplCommon::forceCachingInSameStage() and
- cfn.getAstNode() = dc.getCall()
+ cfn.asExpr() = dc.getCall()
} or
- TExplicitDelegateLikeCall(ControlFlow::Nodes::ElementNode cfn, DelegateLikeCall dc) {
- cfn.getAstNode() = dc
+ TExplicitDelegateLikeCall(ControlFlowNodes::ElementNode cfn, DelegateLikeCall dc) {
+ cfn.asExpr() = dc
} or
TSummaryCall(FlowSummary::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver) {
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
@@ -210,28 +210,28 @@ class DataFlowCallable extends TDataFlowCallable {
}
pragma[nomagic]
- private ControlFlow::Nodes::ElementNode getAMultiBodyEntryNode(ControlFlow::BasicBlock bb, int i) {
+ private ControlFlowNode getAMultiBodyEntryNode(BasicBlock bb, int i) {
this.isMultiBodied() and
exists(ControlFlowElement body, Location l |
body = this.asCallable(l).getBody() or
objectInitEntry(this.asCallable(l), body)
|
NearestLocation::nearestLocation(body, l, _) and
- result = body.getAControlFlowEntryNode()
+ result.isBefore(body)
) and
bb.getNode(i) = result
}
pragma[nomagic]
- private ControlFlow::Nodes::ElementNode getAMultiBodyControlFlowNodePred() {
+ private ControlFlowNode getAMultiBodyControlFlowNodePred() {
result = this.getAMultiBodyEntryNode(_, _).getAPredecessor()
or
result = this.getAMultiBodyControlFlowNodePred().getAPredecessor()
}
pragma[nomagic]
- private ControlFlow::Nodes::ElementNode getAMultiBodyControlFlowNodeSuccSameBasicBlock() {
- exists(ControlFlow::BasicBlock bb, int i, int j |
+ private ControlFlowNode getAMultiBodyControlFlowNodeSuccSameBasicBlock() {
+ exists(BasicBlock bb, int i, int j |
exists(this.getAMultiBodyEntryNode(bb, i)) and
result = bb.getNode(j) and
j > i
@@ -239,14 +239,14 @@ class DataFlowCallable extends TDataFlowCallable {
}
pragma[nomagic]
- private ControlFlow::BasicBlock getAMultiBodyBasicBlockSucc() {
+ private BasicBlock getAMultiBodyBasicBlockSucc() {
result = this.getAMultiBodyEntryNode(_, _).getBasicBlock().getASuccessor()
or
result = this.getAMultiBodyBasicBlockSucc().getASuccessor()
}
pragma[inline]
- private ControlFlow::Nodes::ElementNode getAMultiBodyControlFlowNode() {
+ private ControlFlowNode getAMultiBodyControlFlowNode() {
result =
[
this.getAMultiBodyEntryNode(_, _), this.getAMultiBodyControlFlowNodePred(),
@@ -257,7 +257,7 @@ class DataFlowCallable extends TDataFlowCallable {
/** Gets a control flow node belonging to this callable. */
pragma[inline]
- ControlFlow::Node getAControlFlowNode() {
+ ControlFlowNode getAControlFlowNode() {
result = this.getAMultiBodyControlFlowNode()
or
not this.isMultiBodied() and
@@ -307,7 +307,7 @@ abstract class DataFlowCall extends TDataFlowCall {
abstract DataFlowCallable getARuntimeTarget();
/** Gets the control flow node where this call happens, if any. */
- abstract ControlFlow::Nodes::ElementNode getControlFlowNode();
+ abstract ControlFlowNodes::ElementNode getControlFlowNode();
/** Gets the data flow node corresponding to this call, if any. */
abstract DataFlow::Node getNode();
@@ -363,7 +363,7 @@ private predicate folderDist(Folder f1, Folder f2, int i) =
/** A non-delegate C# call relevant for data flow. */
class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall {
- private ControlFlow::Nodes::ElementNode cfn;
+ private ControlFlowNodes::ElementNode cfn;
private DispatchCall dc;
NonDelegateDataFlowCall() { this = TNonDelegateCall(cfn, dc) }
@@ -436,7 +436,7 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall {
not dc.isReflection()
}
- override ControlFlow::Nodes::ElementNode getControlFlowNode() { result = cfn }
+ override ControlFlowNodes::ElementNode getControlFlowNode() { result = cfn }
override DataFlow::ExprNode getNode() { result.getControlFlowNode() = cfn }
@@ -452,7 +452,7 @@ abstract class DelegateDataFlowCall extends DataFlowCall { }
/** An explicit delegate or function pointer call relevant for data flow. */
class ExplicitDelegateLikeDataFlowCall extends DelegateDataFlowCall, TExplicitDelegateLikeCall {
- private ControlFlow::Nodes::ElementNode cfn;
+ private ControlFlowNodes::ElementNode cfn;
private DelegateLikeCall dc;
ExplicitDelegateLikeDataFlowCall() { this = TExplicitDelegateLikeCall(cfn, dc) }
@@ -464,7 +464,7 @@ class ExplicitDelegateLikeDataFlowCall extends DelegateDataFlowCall, TExplicitDe
none() // handled by the shared library
}
- override ControlFlow::Nodes::ElementNode getControlFlowNode() { result = cfn }
+ override ControlFlowNodes::ElementNode getControlFlowNode() { result = cfn }
override DataFlow::ExprNode getNode() { result.getControlFlowNode() = cfn }
@@ -495,7 +495,7 @@ class SummaryCall extends DelegateDataFlowCall, TSummaryCall {
none() // handled by the shared library
}
- override ControlFlow::Nodes::ElementNode getControlFlowNode() { none() }
+ override ControlFlowNodes::ElementNode getControlFlowNode() { none() }
override DataFlow::Node getNode() { none() }
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 03164960d410..7e8083a2a57b 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
@@ -41,7 +41,7 @@ predicate isArgumentNode(ArgumentNode arg, DataFlowCall c, ArgumentPosition pos)
* Gets a control flow node used for data flow purposes for the primary constructor
* parameter access `pa`.
*/
-private ControlFlow::Node getAPrimaryConstructorParameterCfn(ParameterAccess pa) {
+private ControlFlowNode getAPrimaryConstructorParameterCfn(ParameterAccess pa) {
pa.getTarget().getCallable() instanceof PrimaryConstructor and
(
result = pa.(ParameterRead).getAControlFlowNode()
@@ -72,7 +72,7 @@ abstract class NodeImpl extends Node {
/** Do not call: use `getControlFlowNode()` instead. */
cached
- abstract ControlFlow::Node getControlFlowNodeImpl();
+ abstract ControlFlowNode getControlFlowNodeImpl();
/** Do not call: use `getLocation()` instead. */
cached
@@ -83,22 +83,9 @@ abstract class NodeImpl extends Node {
abstract string toStringImpl();
}
-// TODO: Remove once static initializers are folded into the
-// static constructors
-private DataFlowCallable getEnclosingStaticFieldOrProperty(Expr e) {
- result.asFieldOrProperty() =
- any(FieldOrProperty f |
- f.isStatic() and
- e = f.getAChild+() and
- not exists(e.getEnclosingCallable())
- )
-}
-
private class ExprNodeImpl extends ExprNode, NodeImpl {
override DataFlowCallable getEnclosingCallableImpl() {
result.getAControlFlowNode() = this.getControlFlowNodeImpl()
- or
- result = getEnclosingStaticFieldOrProperty(this.asExpr())
}
override Type getTypeImpl() {
@@ -106,7 +93,7 @@ private class ExprNodeImpl extends ExprNode, NodeImpl {
result = this.getExpr().getType()
}
- override ControlFlow::Nodes::ElementNode getControlFlowNodeImpl() {
+ override ControlFlowNodes::ElementNode getControlFlowNodeImpl() {
forceCachingInSameStage() and this = TExprNode(result)
}
@@ -127,13 +114,13 @@ private class ExprNodeImpl extends ExprNode, NodeImpl {
* as if they were lambdas.
*/
abstract private class LocalFunctionCreationNode extends NodeImpl, TLocalFunctionCreationNode {
- ControlFlow::Nodes::ElementNode cfn;
+ ControlFlowNodes::ElementNode cfn;
LocalFunction function;
boolean isPostUpdate;
LocalFunctionCreationNode() {
this = TLocalFunctionCreationNode(cfn, isPostUpdate) and
- function = cfn.getAstNode().(LocalFunctionStmt).getLocalFunction()
+ function = cfn.asStmt().(LocalFunctionStmt).getLocalFunction()
}
LocalFunction getFunction() { result = function }
@@ -151,9 +138,9 @@ abstract private class LocalFunctionCreationNode extends NodeImpl, TLocalFunctio
override DataFlowType getDataFlowType() { result.asDelegate() = function }
- override ControlFlow::Nodes::ElementNode getControlFlowNodeImpl() { none() }
+ override ControlFlowNodes::ElementNode getControlFlowNodeImpl() { none() }
- ControlFlow::Nodes::ElementNode getUnderlyingControlFlowNode() { result = cfn }
+ ControlFlowNodes::ElementNode getUnderlyingControlFlowNode() { result = cfn }
override Location getLocationImpl() { result = cfn.getLocation() }
}
@@ -166,13 +153,11 @@ private class LocalFunctionCreationPreNode extends LocalFunctionCreationNode {
/** Calculation of the relative order in which `this` references are read. */
private module ThisFlow {
- private class BasicBlock = ControlFlow::BasicBlock;
-
/** Holds if `e` is a `this` access. */
predicate thisAccessExpr(Expr e) { e instanceof ThisAccess or e instanceof BaseAccess }
/** Holds if `n` is a `this` access at control flow node `cfn`. */
- private predicate thisAccess(Node n, ControlFlow::Node cfn) {
+ private predicate thisAccess(Node n, ControlFlowNode cfn) {
thisAccessExpr(n.asExprAtNode(cfn))
or
cfn = n.(InstanceParameterAccessPreNode).getUnderlyingControlFlowNode()
@@ -181,7 +166,7 @@ private module ThisFlow {
private predicate primaryConstructorThisAccess(Node n, BasicBlock bb, int ppos) {
exists(Parameter p |
n.(PrimaryConstructorThisAccessPreNode).getParameter() = p and
- bb.getCallable() = p.getCallable() and
+ bb.getEnclosingCallable() = p.getCallable() and
ppos = p.getPosition()
)
}
@@ -198,9 +183,9 @@ private module ThisFlow {
i = ppos - numberOfPrimaryConstructorParameters(bb)
)
or
- exists(DataFlowCallable c, ControlFlow::BasicBlocks::EntryBlock entry |
+ exists(DataFlowCallable c, EntryBasicBlock entry |
n.(InstanceParameterNode).isParameterOf(c, _) and
- exists(ControlFlow::Node succ |
+ exists(ControlFlowNode succ |
succ = c.getAControlFlowNode() and
succ = entry.getFirstNode().getASuccessor() and
// In case `c` has multiple bodies, we want each body to gets its own implicit
@@ -261,9 +246,8 @@ private module ThisFlow {
/** Provides logic related to captured variables. */
module VariableCapture {
private import codeql.dataflow.VariableCapture as Shared
- private import semmle.code.csharp.controlflow.BasicBlocks as BasicBlocks
- private predicate closureFlowStep(ControlFlow::Nodes::ExprNode e1, ControlFlow::Nodes::ExprNode e2) {
+ private predicate closureFlowStep(ControlFlowNodes::ExprNode e1, ControlFlowNodes::ExprNode e2) {
e1.getExpr() = LocalFlow::getALastEvalNode(e2.getExpr())
or
exists(Ssa::Definition def, AssignableDefinition adef |
@@ -273,21 +257,19 @@ module VariableCapture {
)
}
- private module CaptureInput implements Shared::InputSig {
+ private module CaptureInput implements Shared::InputSig {
private import csharp as Cs
private import semmle.code.csharp.controlflow.ControlFlowGraph as Cfg
private import TaintTrackingPrivate as TaintTrackingPrivate
- Callable basicBlockGetEnclosingCallable(BasicBlocks::BasicBlock bb) {
- result = bb.getCallable()
- }
+ Callable basicBlockGetEnclosingCallable(BasicBlock bb) { result = bb.getEnclosingCallable() }
- private predicate thisAccess(ControlFlow::Node cfn, InstanceCallable c) {
- ThisFlow::thisAccessExpr(cfn.getAstNode()) and
+ private predicate thisAccess(ControlFlowNode cfn, InstanceCallable c) {
+ ThisFlow::thisAccessExpr(cfn.asExpr()) and
cfn.getEnclosingCallable().getEnclosingCallable*() = c
}
- private predicate capturedThisAccess(ControlFlow::Node cfn, InstanceCallable c) {
+ private predicate capturedThisAccess(ControlFlowNode cfn, InstanceCallable c) {
thisAccess(cfn, c) and
cfn.getEnclosingCallable() != c
}
@@ -347,8 +329,8 @@ module VariableCapture {
}
}
- class Expr extends ControlFlow::Node {
- predicate hasCfgNode(BasicBlocks::BasicBlock bb, int i) { this = bb.getNode(i) }
+ class Expr extends ControlFlowNode {
+ predicate hasCfgNode(BasicBlock bb, int i) { this = bb.getNode(i) }
}
class VariableWrite extends Expr {
@@ -360,7 +342,7 @@ module VariableCapture {
this = def.getExpr().getAControlFlowNode()
}
- ControlFlow::Node getRhs() { LocalFlow::defAssigns(def, this, _, result) }
+ ControlFlowNode getRhs() { LocalFlow::defAssigns(def, this, _, result) }
CapturedVariable getVariable() { result = v }
}
@@ -369,7 +351,7 @@ module VariableCapture {
CapturedVariable v;
VariableRead() {
- this.getAstNode().(AssignableRead).getTarget() = v.asLocalScopeVariable()
+ this.asExpr().(AssignableRead).getTarget() = v.asLocalScopeVariable()
or
thisAccess(this, v.asThis())
}
@@ -380,14 +362,16 @@ module VariableCapture {
class ClosureExpr extends Expr {
Callable c;
- ClosureExpr() { lambdaCreationExpr(this.getAstNode(), c) }
+ ClosureExpr() {
+ lambdaCreationExpr(any(ControlFlowElement e | e.getControlFlowNode() = this), c)
+ }
predicate hasBody(Callable body) { body = c }
predicate hasAliasedAccess(Expr f) {
closureFlowStep+(this, f) and not closureFlowStep(f, _)
or
- isLocalFunctionCallReceiver(_, f.getAstNode(), c)
+ isLocalFunctionCallReceiver(_, f.asExpr(), c)
}
}
@@ -400,7 +384,7 @@ module VariableCapture {
class ClosureExpr = CaptureInput::ClosureExpr;
- module Flow = Shared::Flow;
+ module Flow = Shared::Flow;
private Flow::ClosureNode asClosureNode(Node n) {
result = n.(CaptureNode).getSynthesizedCaptureNode()
@@ -512,7 +496,7 @@ module LocalFlow {
predicate localExprStep(Expr e1, Expr e2) {
e1 = e2.(ParenthesizedExpr).getExpr()
or
- e1 = e2.(NullCoalescingExpr).getAnOperand()
+ e1 = e2.(NullCoalescingOperation).getAnOperand()
or
e1 = e2.(SuppressNullableWarningExpr).getExpr()
or
@@ -564,7 +548,7 @@ module LocalFlow {
}
predicate defAssigns(
- AssignableDefinition def, ControlFlow::Node cfnDef, Expr value, ControlFlow::Node valueCfn
+ AssignableDefinition def, ControlFlowNode cfnDef, Expr value, ControlFlowNode valueCfn
) {
def.getSource() = value and
valueCfn = value.getControlFlowNode() and
@@ -572,7 +556,7 @@ module LocalFlow {
}
private predicate defAssigns(ExprNode value, AssignableDefinitionNode defNode) {
- exists(ControlFlow::Node cfn, AssignableDefinition def, ControlFlow::Node cfnDef |
+ exists(ControlFlowNode cfn, AssignableDefinition def, ControlFlowNode cfnDef |
defAssigns(def, cfnDef, value.getExpr(), _) and
cfn = value.getControlFlowNode() and
defNode = TAssignableDefinitionNode(def, cfnDef)
@@ -596,7 +580,7 @@ module LocalFlow {
or
ThisFlow::adjacentThisRefs(nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo)
or
- exists(AssignableDefinition def, ControlFlow::Node cfn, Ssa::ExplicitDefinition ssaDef |
+ exists(AssignableDefinition def, ControlFlowNode cfn, Ssa::ExplicitDefinition ssaDef |
ssaDef.getADefinition() = def and
ssaDef.getControlFlowNode() = cfn and
nodeFrom = TAssignableDefinitionNode(def, cfn) and
@@ -623,7 +607,7 @@ module LocalFlow {
(
e instanceof ConditionalExpr or
e instanceof Cast or
- e instanceof NullCoalescingExpr or
+ e instanceof NullCoalescingOperation or
e instanceof SwitchExpr or
e instanceof SuppressNullableWarningExpr or
e instanceof AssignExpr
@@ -1024,9 +1008,9 @@ private module Cached {
cached
newtype TNode =
- TExprNode(ControlFlow::Nodes::ElementNode cfn) { cfn.getAstNode() instanceof Expr } or
+ TExprNode(ControlFlowNodes::ElementNode cfn) { exists(cfn.asExpr()) } or
TSsaNode(SsaImpl::DataFlowIntegration::SsaNode node) or
- TAssignableDefinitionNode(AssignableDefinition def, ControlFlow::Node cfn) {
+ TAssignableDefinitionNode(AssignableDefinition def, ControlFlowNode cfn) {
cfn = def.getExpr().getAControlFlowNode()
} or
TExplicitParameterNode(Parameter p, DataFlowCallable c) {
@@ -1038,20 +1022,20 @@ private module Cached {
l = c.getARelevantLocation()
} or
TDelegateSelfReferenceNode(Callable c) { lambdaCreationExpr(_, c) } or
- TLocalFunctionCreationNode(ControlFlow::Nodes::ElementNode cfn, Boolean isPostUpdate) {
- cfn.getAstNode() instanceof LocalFunctionStmt
+ TLocalFunctionCreationNode(ControlFlowNodes::ElementNode cfn, Boolean isPostUpdate) {
+ cfn.asStmt() instanceof LocalFunctionStmt
} or
- TYieldReturnNode(ControlFlow::Nodes::ElementNode cfn) {
- any(Callable c).canYieldReturn(cfn.getAstNode())
+ TYieldReturnNode(ControlFlowNodes::ElementNode cfn) {
+ any(Callable c).canYieldReturn(cfn.asExpr())
} or
- TAsyncReturnNode(ControlFlow::Nodes::ElementNode cfn) {
- any(Callable c | c.(Modifiable).isAsync()).canReturn(cfn.getAstNode())
+ TAsyncReturnNode(ControlFlowNodes::ElementNode cfn) {
+ any(Callable c | c.(Modifiable).isAsync()).canReturn(cfn.asExpr())
} or
- TMallocNode(ControlFlow::Nodes::ElementNode cfn) { cfn.getAstNode() instanceof ObjectCreation } or
- TObjectInitializerNode(ControlFlow::Nodes::ElementNode cfn) {
- cfn.getAstNode().(ObjectCreation).hasInitializer()
+ TMallocNode(ControlFlowNodes::ElementNode cfn) { cfn.asExpr() instanceof ObjectCreation } or
+ TObjectInitializerNode(ControlFlowNodes::ElementNode cfn) {
+ cfn.asExpr().(ObjectCreation).hasInitializer()
} or
- TExprPostUpdateNode(ControlFlow::Nodes::ExprNode cfn) {
+ TExprPostUpdateNode(ControlFlowNodes::ExprNode cfn) {
(
cfn.getExpr() instanceof Argument
or
@@ -1085,12 +1069,12 @@ private module Cached {
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) {
sn.getSummarizedCallable() instanceof CallableUsedInSource
} or
- TParamsArgumentNode(ControlFlow::Node callCfn) {
+ TParamsArgumentNode(ControlFlowNode callCfn) {
callCfn = any(Call c | isParamsArg(c, _, _)).getAControlFlowNode()
} or
TFlowInsensitiveFieldNode(FieldOrPropertyUsedInSource f) { f.isFieldLike() } or
TFlowInsensitiveCapturedVariableNode(LocalScopeVariable v) { v.isCaptured() } or
- TInstanceParameterAccessNode(ControlFlow::Node cfn, Boolean isPostUpdate) {
+ TInstanceParameterAccessNode(ControlFlowNode cfn, Boolean isPostUpdate) {
cfn = getAPrimaryConstructorParameterCfn(_)
} or
TPrimaryConstructorThisAccessNode(Parameter p, Boolean isPostUpdate, DataFlowCallable c) {
@@ -1232,7 +1216,7 @@ class SsaNode extends NodeImpl, TSsaNode {
override Type getTypeImpl() { result = node.getSourceVariable().getType() }
- override ControlFlow::Node getControlFlowNodeImpl() { none() }
+ override ControlFlowNode getControlFlowNodeImpl() { none() }
override Location getLocationImpl() { result = node.getLocation() }
@@ -1245,7 +1229,7 @@ class SsaDefinitionNode extends SsaNode {
Ssa::Definition getDefinition() { result = node.getDefinition() }
- override ControlFlow::Node getControlFlowNodeImpl() {
+ override ControlFlowNode getControlFlowNodeImpl() {
result = this.getDefinition().getControlFlowNode()
}
}
@@ -1253,7 +1237,7 @@ class SsaDefinitionNode extends SsaNode {
/** A definition, viewed as a node in a data flow graph. */
class AssignableDefinitionNodeImpl extends NodeImpl, TAssignableDefinitionNode {
private AssignableDefinition def;
- private ControlFlow::Node cfn_;
+ private ControlFlowNode cfn_;
AssignableDefinitionNodeImpl() { this = TAssignableDefinitionNode(def, cfn_) }
@@ -1261,7 +1245,7 @@ class AssignableDefinitionNodeImpl extends NodeImpl, TAssignableDefinitionNode {
AssignableDefinition getDefinition() { result = def }
/** Gets the underlying definition, at control flow node `cfn`, if any. */
- AssignableDefinition getDefinitionAtNode(ControlFlow::Node cfn) {
+ AssignableDefinition getDefinitionAtNode(ControlFlowNode cfn) {
result = def and
cfn = cfn_
}
@@ -1270,7 +1254,7 @@ class AssignableDefinitionNodeImpl extends NodeImpl, TAssignableDefinitionNode {
override Type getTypeImpl() { result = def.getTarget().getType() }
- override ControlFlow::Node getControlFlowNodeImpl() { result = cfn_ }
+ override ControlFlowNode getControlFlowNodeImpl() { result = cfn_ }
override Location getLocationImpl() {
result = def.getTargetAccess().getLocation()
@@ -1373,7 +1357,7 @@ private module ParameterNodes {
override Type getTypeImpl() { result = parameter.getType() }
- override ControlFlow::Node getControlFlowNodeImpl() { none() }
+ override ControlFlowNode getControlFlowNodeImpl() { none() }
override Location getLocationImpl() { result = this.getParameterLocation(_) }
@@ -1398,7 +1382,7 @@ private module ParameterNodes {
override Type getTypeImpl() { result = callable.getDeclaringType() }
- override ControlFlow::Node getControlFlowNodeImpl() { none() }
+ override ControlFlowNode getControlFlowNodeImpl() { none() }
override Location getLocationImpl() { result = location }
@@ -1423,7 +1407,7 @@ private module ParameterNodes {
callable = c.asCallable(_) and pos.isDelegateSelf()
}
- override ControlFlow::Node getControlFlowNodeImpl() { none() }
+ override ControlFlowNode getControlFlowNodeImpl() { none() }
override DataFlowCallable getEnclosingCallableImpl() { result.asCallable(_) = callable }
@@ -1507,7 +1491,7 @@ private module ArgumentNodes {
* the constructor has run.
*/
class MallocNode extends ArgumentNodeImpl, NodeImpl, TMallocNode {
- private ControlFlow::Nodes::ElementNode cfn;
+ private ControlFlowNodes::ElementNode cfn;
MallocNode() { this = TMallocNode(cfn) }
@@ -1516,15 +1500,11 @@ private module ArgumentNodes {
pos.isQualifier()
}
- override ControlFlow::Node getControlFlowNodeImpl() { result = cfn }
+ override ControlFlowNode getControlFlowNodeImpl() { result = cfn }
- override DataFlowCallable getEnclosingCallableImpl() {
- result.getAControlFlowNode() = cfn
- or
- result = getEnclosingStaticFieldOrProperty(cfn.getAstNode())
- }
+ override DataFlowCallable getEnclosingCallableImpl() { result.getAControlFlowNode() = cfn }
- override Type getTypeImpl() { result = cfn.getAstNode().(Expr).getType() }
+ override Type getTypeImpl() { result = cfn.asExpr().getType() }
override Location getLocationImpl() { result = cfn.getLocation() }
@@ -1546,7 +1526,7 @@ private module ArgumentNodes {
* `Foo(new[] { "a", "b", "c" })`.
*/
class ParamsArgumentNode extends ArgumentNodeImpl, NodeImpl, TParamsArgumentNode {
- private ControlFlow::Node callCfn;
+ private ControlFlowNode callCfn;
ParamsArgumentNode() { this = TParamsArgumentNode(callCfn) }
@@ -1559,15 +1539,11 @@ private module ArgumentNodes {
pos.getPosition() = this.getParameter().getPosition()
}
- override DataFlowCallable getEnclosingCallableImpl() {
- result.getAControlFlowNode() = callCfn
- or
- result = getEnclosingStaticFieldOrProperty(callCfn.getAstNode())
- }
+ override DataFlowCallable getEnclosingCallableImpl() { result.getAControlFlowNode() = callCfn }
override Type getTypeImpl() { result = this.getParameter().getType() }
- override ControlFlow::Node getControlFlowNodeImpl() { none() }
+ override ControlFlowNode getControlFlowNodeImpl() { none() }
override Location getLocationImpl() { result = callCfn.getLocation() }
@@ -1638,7 +1614,7 @@ private module ReturnNodes {
* to `yield return e [e]`.
*/
class YieldReturnNode extends ReturnNode, NodeImpl, TYieldReturnNode {
- private ControlFlow::Nodes::ElementNode cfn;
+ private ControlFlowNodes::ElementNode cfn;
private YieldReturnStmt yrs;
YieldReturnNode() { this = TYieldReturnNode(cfn) and yrs.getExpr().getAControlFlowNode() = cfn }
@@ -1651,7 +1627,7 @@ private module ReturnNodes {
override Type getTypeImpl() { result = yrs.getEnclosingCallable().getReturnType() }
- override ControlFlow::Node getControlFlowNodeImpl() { result = cfn }
+ override ControlFlowNode getControlFlowNodeImpl() { result = cfn }
override Location getLocationImpl() { result = yrs.getLocation() }
@@ -1662,10 +1638,10 @@ private module ReturnNodes {
* A synthesized `return` node for returned expressions inside `async` methods.
*/
class AsyncReturnNode extends ReturnNode, NodeImpl, TAsyncReturnNode {
- private ControlFlow::Nodes::ElementNode cfn;
+ private ControlFlowNodes::ElementNode cfn;
private Expr expr;
- AsyncReturnNode() { this = TAsyncReturnNode(cfn) and expr = cfn.getAstNode() }
+ AsyncReturnNode() { this = TAsyncReturnNode(cfn) and expr = cfn.asExpr() }
Expr getExpr() { result = expr }
@@ -1675,7 +1651,7 @@ private module ReturnNodes {
override Type getTypeImpl() { result = expr.getEnclosingCallable().getReturnType() }
- override ControlFlow::Node getControlFlowNodeImpl() { result = cfn }
+ override ControlFlowNode getControlFlowNodeImpl() { result = cfn }
override Location getLocationImpl() { result = expr.getLocation() }
@@ -1727,7 +1703,7 @@ private module OutNodes {
private import semmle.code.csharp.frameworks.system.Collections
private import semmle.code.csharp.frameworks.system.collections.Generic
- private DataFlowCall csharpCall(Expr e, ControlFlow::Node cfn) {
+ private DataFlowCall csharpCall(Expr e, ControlFlowNode cfn) {
e = any(DispatchCall dc | result = TNonDelegateCall(cfn, dc)).getCall() or
result = TExplicitDelegateLikeCall(cfn, e)
}
@@ -1757,7 +1733,7 @@ private module OutNodes {
*/
class ParamOutNode extends OutNode, AssignableDefinitionNode {
private AssignableDefinitions::OutRefDefinition outRefDef;
- private ControlFlow::Node cfn;
+ private ControlFlowNode cfn;
ParamOutNode() { outRefDef = this.getDefinitionAtNode(cfn) }
@@ -1802,7 +1778,7 @@ class FlowSummaryNode extends NodeImpl, TFlowSummaryNode {
override Type getTypeImpl() { none() }
- override ControlFlow::Node getControlFlowNodeImpl() { none() }
+ override ControlFlowNode getControlFlowNodeImpl() { none() }
override Location getLocationImpl() { result = this.getSummarizedCallable().getLocation() }
@@ -1826,7 +1802,7 @@ class FlowSummaryNode extends NodeImpl, TFlowSummaryNode {
* all of which are represented by an `InstanceParameterAccessNode` node.
*/
abstract private class InstanceParameterAccessNode extends NodeImpl, TInstanceParameterAccessNode {
- ControlFlow::Node cfn;
+ ControlFlowNode cfn;
boolean isPostUpdate;
Parameter p;
@@ -1839,14 +1815,14 @@ abstract private class InstanceParameterAccessNode extends NodeImpl, TInstancePa
override Type getTypeImpl() { result = cfn.getEnclosingCallable().getDeclaringType() }
- override ControlFlow::Nodes::ElementNode getControlFlowNodeImpl() { none() }
+ override ControlFlowNodes::ElementNode getControlFlowNodeImpl() { none() }
override Location getLocationImpl() { result = cfn.getLocation() }
/**
* Gets the underlying control flow node.
*/
- ControlFlow::Node getUnderlyingControlFlowNode() { result = cfn }
+ ControlFlowNode getUnderlyingControlFlowNode() { result = cfn }
/**
* Gets the primary constructor parameter that this is a this access to.
@@ -1888,7 +1864,7 @@ abstract private class PrimaryConstructorThisAccessNode extends NodeImpl,
override Type getTypeImpl() { result = p.getCallable().getDeclaringType() }
- override ControlFlow::Nodes::ElementNode getControlFlowNodeImpl() { none() }
+ override ControlFlowNodes::ElementNode getControlFlowNodeImpl() { none() }
override Location getLocationImpl() {
NearestLocation::nearestLocation(p,
@@ -1939,7 +1915,7 @@ class CaptureNode extends NodeImpl, TCaptureNode {
else result = super.getDataFlowType()
}
- override ControlFlow::Node getControlFlowNodeImpl() { none() }
+ override ControlFlowNode getControlFlowNodeImpl() { none() }
override Location getLocationImpl() { result = cn.getLocation() }
@@ -2050,7 +2026,7 @@ class FlowInsensitiveFieldNode extends NodeImpl, TFlowInsensitiveFieldNode {
override Type getTypeImpl() { result = f.getType() }
- override ControlFlow::Node getControlFlowNodeImpl() { none() }
+ override ControlFlowNode getControlFlowNodeImpl() { none() }
override Location getLocationImpl() { result = f.getLocation() }
@@ -2074,7 +2050,7 @@ class FlowInsensitiveCapturedVariableNode extends NodeImpl, TFlowInsensitiveCapt
override Type getTypeImpl() { result = v.getType() }
- override ControlFlow::Node getControlFlowNodeImpl() { none() }
+ override ControlFlowNode getControlFlowNodeImpl() { none() }
override Location getLocationImpl() { result = v.getLocation() }
@@ -2117,7 +2093,7 @@ private ContentSet getResultContent() {
private predicate primaryConstructorParameterStore(
AssignableDefinitionNode node1, PrimaryConstructorParameterContent c, Node node2
) {
- exists(AssignableDefinition def, ControlFlow::Node cfn, Parameter p |
+ exists(AssignableDefinition def, ControlFlowNode cfn, Parameter p |
node1 = TAssignableDefinitionNode(def, cfn) and
p = def.getTarget() and
node2 = TInstanceParameterAccessNode(cfn, true) and
@@ -2360,7 +2336,7 @@ predicate expectsContent(Node n, ContentSet c) {
n.asExpr() instanceof SpreadElementExpr and c.isElement()
}
-class NodeRegion instanceof ControlFlow::BasicBlock {
+class NodeRegion instanceof BasicBlock {
string toString() { result = "NodeRegion" }
predicate contains(Node n) { this = n.getControlFlowNode().getBasicBlock() }
@@ -2421,10 +2397,10 @@ DataFlowType getNodeType(Node n) {
not lambdaCreation(n, _, _) and
not isLocalFunctionCallReceiver(_, n.asExpr(), _)
or
- [
- n.asExpr().(ControlFlowElement),
- n.(LocalFunctionCreationPreNode).getUnderlyingControlFlowNode().getAstNode()
- ] = result.getADelegateCreation()
+ n.asExpr() = result.getADelegateCreation()
+ or
+ n.(LocalFunctionCreationPreNode).getUnderlyingControlFlowNode() =
+ result.getADelegateCreation().getControlFlowNode()
}
private class DataFlowNullType extends Gvn::GvnType {
@@ -2543,7 +2519,7 @@ module PostUpdateNodes {
ObjectCreationNode() { this = TExprNode(oc.getAControlFlowNode()) }
override Node getPreUpdateSourceNode() {
- exists(ControlFlow::Nodes::ElementNode cfn | this = TExprNode(cfn) |
+ exists(ControlFlowNodes::ElementNode cfn | this = TExprNode(cfn) |
result = TObjectInitializerNode(cfn)
or
not oc.hasInitializer() and
@@ -2563,7 +2539,7 @@ module PostUpdateNodes {
TObjectInitializerNode
{
private ObjectCreation oc;
- private ControlFlow::Nodes::ElementNode cfn;
+ private ControlFlowNodes::ElementNode cfn;
ObjectInitializerNode() {
this = TObjectInitializerNode(cfn) and
@@ -2586,15 +2562,11 @@ module PostUpdateNodes {
)
}
- override DataFlowCallable getEnclosingCallableImpl() {
- result.getAControlFlowNode() = cfn
- or
- result = getEnclosingStaticFieldOrProperty(oc)
- }
+ override DataFlowCallable getEnclosingCallableImpl() { result.getAControlFlowNode() = cfn }
override Type getTypeImpl() { result = oc.getType() }
- override ControlFlow::Nodes::ElementNode getControlFlowNodeImpl() { result = cfn }
+ override ControlFlowNodes::ElementNode getControlFlowNodeImpl() { result = cfn }
override Location getLocationImpl() { result = cfn.getLocation() }
@@ -2602,21 +2574,17 @@ module PostUpdateNodes {
}
class ExprPostUpdateNode extends SourcePostUpdateNode, NodeImpl, TExprPostUpdateNode {
- private ControlFlow::Nodes::ElementNode cfn;
+ private ControlFlowNodes::ElementNode cfn;
ExprPostUpdateNode() { this = TExprPostUpdateNode(cfn) }
override ExprNode getPreUpdateSourceNode() { result = TExprNode(cfn) }
- override DataFlowCallable getEnclosingCallableImpl() {
- result.getAControlFlowNode() = cfn
- or
- result = getEnclosingStaticFieldOrProperty(cfn.getAstNode())
- }
+ override DataFlowCallable getEnclosingCallableImpl() { result.getAControlFlowNode() = cfn }
- override Type getTypeImpl() { result = cfn.getAstNode().(Expr).getType() }
+ override Type getTypeImpl() { result = cfn.asExpr().getType() }
- override ControlFlow::Node getControlFlowNodeImpl() { none() }
+ override ControlFlowNode getControlFlowNodeImpl() { none() }
override Location getLocationImpl() { result = cfn.getLocation() }
@@ -2745,7 +2713,7 @@ private predicate isLocalFunctionCallReceiver(
f = receiver.getTarget().getUnboundDeclaration()
}
-private predicate lambdaCallExpr(DataFlowCall call, Expr receiver, ControlFlow::Node receiverCfn) {
+private predicate lambdaCallExpr(DataFlowCall call, Expr receiver, ControlFlowNode receiverCfn) {
exists(DelegateLikeCall dc |
call.(ExplicitDelegateLikeDataFlowCall).getCall() = dc and
receiver = dc.getExpr() and
@@ -2766,7 +2734,7 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
(
lambdaCallExpr(call, receiver.asExpr(), _) and
// local function calls can be resolved directly without a flow analysis
- not call.getControlFlowNode().getAstNode() instanceof LocalFunctionCall
+ not call.getControlFlowNode().asExpr() instanceof LocalFunctionCall
or
receiver.(FlowSummaryNode).getSummaryNode() = call.(SummaryCall).getReceiver()
) and
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll
index f4d24fdb5101..5c49809ed406 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll
@@ -17,7 +17,7 @@ class Node extends TNode {
* Gets the expression corresponding to this node, at control flow node `cfn`,
* if any.
*/
- Expr asExprAtNode(ControlFlow::Nodes::ElementNode cfn) {
+ Expr asExprAtNode(ControlFlowNodes::ElementNode cfn) {
result = this.(ExprNode).getExprAtNode(cfn)
}
@@ -31,7 +31,7 @@ class Node extends TNode {
* Gets the definition corresponding to this node, at control flow node `cfn`,
* if any.
*/
- AssignableDefinition asDefinitionAtNode(ControlFlow::Node cfn) {
+ AssignableDefinition asDefinitionAtNode(ControlFlowNode cfn) {
result = this.(AssignableDefinitionNode).getDefinitionAtNode(cfn)
}
@@ -44,7 +44,7 @@ class Node extends TNode {
}
/** Gets the control flow node corresponding to this node, if any. */
- final ControlFlow::Node getControlFlowNode() { result = this.(NodeImpl).getControlFlowNodeImpl() }
+ final ControlFlowNode getControlFlowNode() { result = this.(NodeImpl).getControlFlowNodeImpl() }
/** Gets a textual representation of this node. */
final string toString() { result = this.(NodeImpl).toStringImpl() }
@@ -71,7 +71,7 @@ class Node extends TNode {
*
* Note that because of control-flow splitting, one `Expr` may correspond
* to multiple `ExprNode`s, just like it may correspond to multiple
- * `ControlFlow::Node`s.
+ * `ControlFlowNode`s.
*/
class ExprNode extends Node, TExprNode {
/** Gets the expression corresponding to this node. */
@@ -81,9 +81,9 @@ class ExprNode extends Node, TExprNode {
* Gets the expression corresponding to this node, at control flow node `cfn`,
* if any.
*/
- Expr getExprAtNode(ControlFlow::Nodes::ElementNode cfn) {
+ Expr getExprAtNode(ControlFlowNodes::ElementNode cfn) {
this = TExprNode(cfn) and
- result = cfn.getAstNode()
+ result = cfn.asExpr()
}
}
@@ -113,7 +113,7 @@ class AssignableDefinitionNode extends Node instanceof AssignableDefinitionNodeI
AssignableDefinition getDefinition() { result = super.getDefinition() }
/** Gets the underlying definition, at control flow node `cfn`, if any. */
- AssignableDefinition getDefinitionAtNode(ControlFlow::Node cfn) {
+ AssignableDefinition getDefinitionAtNode(ControlFlowNode cfn) {
result = super.getDefinitionAtNode(cfn)
}
}
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll
index 7a592bebff0f..fb38305bfca0 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll
@@ -5,11 +5,10 @@
import csharp
private import codeql.ssa.Ssa as SsaImplCommon
private import AssignableDefinitions
-private import semmle.code.csharp.controlflow.BasicBlocks as BasicBlocks
private import semmle.code.csharp.controlflow.Guards as Guards
private import semmle.code.csharp.dataflow.internal.BaseSSA
-private module SsaInput implements SsaImplCommon::InputSig {
+private module SsaInput implements SsaImplCommon::InputSig {
class SourceVariable = Ssa::SourceVariable;
/**
@@ -18,7 +17,7 @@ private module SsaInput implements SsaImplCommon::InputSig as Impl
+import SsaImplCommon::Make as Impl
class Definition = Impl::Definition;
@@ -56,8 +55,8 @@ module Consistency = Impl::Consistency;
/**
* Holds if the `i`th node of basic block `bb` reads source variable `v`.
*/
-private predicate variableReadActual(ControlFlow::BasicBlock bb, int i, Ssa::SourceVariable v) {
- v.getAnAccess().(AssignableRead) = bb.getNode(i).getAstNode()
+private predicate variableReadActual(BasicBlock bb, int i, Ssa::SourceVariable v) {
+ v.getAnAccess().(AssignableRead) = bb.getNode(i).asExpr()
}
private module SourceVariableImpl {
@@ -125,9 +124,7 @@ private module SourceVariableImpl {
* Holds if the `i`th node of basic block `bb` is assignable definition `ad`
* targeting source variable `v`.
*/
- predicate variableDefinition(
- ControlFlow::BasicBlock bb, int i, Ssa::SourceVariable v, AssignableDefinition ad
- ) {
+ predicate variableDefinition(BasicBlock bb, int i, Ssa::SourceVariable v, AssignableDefinition ad) {
ad = v.getADefinition() and
ad.getExpr().getAControlFlowNode() = bb.getNode(i) and
// In cases like `(x, x) = (0, 1)`, we discard the first (dead) definition of `x`
@@ -159,9 +156,7 @@ private module SourceVariableImpl {
*
* This excludes implicit writes via calls.
*/
- predicate variableWriteDirect(
- ControlFlow::BasicBlock bb, int i, Ssa::SourceVariable v, boolean certain
- ) {
+ predicate variableWriteDirect(BasicBlock bb, int i, Ssa::SourceVariable v, boolean certain) {
exists(AssignableDefinition ad | variableDefinition(bb, i, v, ad) |
if any(AssignableDefinition ad0 | ad0 = ad or ad0 = getASameOutRefDefAfter(v, ad)).isCertain()
then certain = true
@@ -186,13 +181,12 @@ private module SourceVariableImpl {
* }
* ```
*/
- predicate outRefExitRead(ControlFlow::BasicBlock bb, int i, LocalScopeSourceVariable v) {
- exists(ControlFlow::Nodes::AnnotatedExitNode exit |
- exit.isNormal() and
+ predicate outRefExitRead(BasicBlock bb, int i, LocalScopeSourceVariable v) {
+ exists(ControlFlow::NormalExitNode exit |
exists(LocalScopeVariable lsv |
lsv = v.getAssignable() and
bb.getNode(i) = exit and
- exit.getCallable() = lsv.getCallable()
+ exit.getEnclosingCallable() = lsv.getCallable()
|
lsv.(Parameter).isOutOrRef()
or
@@ -218,7 +212,7 @@ private module SourceVariableImpl {
* The pseudo read is inserted at the CFG node `i` on the left-hand side of the
* assignment on line 3.
*/
- predicate refReadBeforeWrite(ControlFlow::BasicBlock bb, int i, LocalScopeSourceVariable v) {
+ predicate refReadBeforeWrite(BasicBlock bb, int i, LocalScopeSourceVariable v) {
exists(AssignableDefinitions::AssignmentDefinition def, LocalVariable lv |
def.getTarget() = lv and
lv.isRef() and
@@ -276,15 +270,17 @@ private module CallGraph {
*
* the constructor call `new Lazy(M2)` includes `M2` as a target.
*/
- Callable getARuntimeTarget(Call c, boolean libraryDelegateCall) {
+ Callable getARuntimeTarget(Call c, ControlFlowNode n, boolean libraryDelegateCall) {
// Non-delegate call: use dispatch library
exists(DispatchCall dc | dc.getCall() = c |
+ n = dc.getControlFlowNode() and
result = dc.getADynamicTarget().getUnboundDeclaration() and
libraryDelegateCall = false
)
or
// Delegate call: use simple analysis
- result = SimpleDelegateAnalysis::getARuntimeDelegateTarget(c, libraryDelegateCall)
+ result = SimpleDelegateAnalysis::getARuntimeDelegateTarget(c, libraryDelegateCall) and
+ n = c.getControlFlowNode()
}
private module SimpleDelegateAnalysis {
@@ -471,7 +467,7 @@ private module CallGraph {
/** Holds if `(c1,c2)` is an edge in the call graph. */
predicate callEdge(Callable c1, Callable c2) {
- exists(Call c | c.getEnclosingCallable() = c1 and c2 = getARuntimeTarget(c, _))
+ exists(Call c | c.getEnclosingCallable() = c1 and c2 = getARuntimeTarget(c, _, _))
}
}
@@ -605,7 +601,7 @@ private module FieldOrPropsImpl {
private predicate intraInstanceCallEdge(Callable c1, InstanceCallable c2) {
exists(Call c |
c.getEnclosingCallable() = c1 and
- c2 = getARuntimeTarget(c, _) and
+ c2 = getARuntimeTarget(c, _, _) and
c.(QualifiableExpr).targetIsLocalInstance()
)
}
@@ -620,9 +616,8 @@ private module FieldOrPropsImpl {
}
pragma[noinline]
- predicate callAt(ControlFlow::BasicBlock bb, int i, Call call) {
- bb.getNode(i) = call.getAControlFlowNode() and
- getARuntimeTarget(call, _).hasBody()
+ predicate callAt(BasicBlock bb, int i, Call call) {
+ getARuntimeTarget(call, bb.getNode(i), _).hasBody()
}
/**
@@ -630,9 +625,7 @@ private module FieldOrPropsImpl {
* an update somewhere, and `fp` is likely to be live in `bb` at index
* `i`.
*/
- predicate updateCandidate(
- ControlFlow::BasicBlock bb, int i, FieldOrPropSourceVariable fp, Call call
- ) {
+ predicate updateCandidate(BasicBlock bb, int i, FieldOrPropSourceVariable fp, Call call) {
callAt(bb, i, call) and
call.getEnclosingCallable() = fp.getEnclosingCallable() and
relevantDefinition(_, fp.getAssignable(), _) and
@@ -643,7 +636,7 @@ private module FieldOrPropsImpl {
Call call, FieldOrPropSourceVariable fps, FieldOrProp fp, Callable c, boolean fresh
) {
updateCandidate(_, _, fps, call) and
- c = getARuntimeTarget(call, _) and
+ c = getARuntimeTarget(call, _, _) and
fp = fps.getAssignable() and
if c instanceof Constructor then fresh = true else fresh = false
}
@@ -714,7 +707,7 @@ private module FieldOrPropsImpl {
}
}
-private predicate variableReadPseudo(ControlFlow::BasicBlock bb, int i, Ssa::SourceVariable v) {
+private predicate variableReadPseudo(BasicBlock bb, int i, Ssa::SourceVariable v) {
outRefExitRead(bb, i, v)
or
refReadBeforeWrite(bb, i, v)
@@ -722,16 +715,14 @@ private predicate variableReadPseudo(ControlFlow::BasicBlock bb, int i, Ssa::Sou
pragma[noinline]
deprecated private predicate adjacentDefRead(
- Definition def, ControlFlow::BasicBlock bb1, int i1, ControlFlow::BasicBlock bb2, int i2,
- SsaInput::SourceVariable v
+ Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2, SsaInput::SourceVariable v
) {
Impl::adjacentDefRead(def, bb1, i1, bb2, i2) and
v = def.getSourceVariable()
}
deprecated private predicate adjacentDefReachesRead(
- Definition def, SsaInput::SourceVariable v, ControlFlow::BasicBlock bb1, int i1,
- ControlFlow::BasicBlock bb2, int i2
+ Definition def, SsaInput::SourceVariable v, BasicBlock bb1, int i1, BasicBlock bb2, int i2
) {
adjacentDefRead(def, bb1, i1, bb2, i2, v) and
(
@@ -740,7 +731,7 @@ deprecated private predicate adjacentDefReachesRead(
SsaInput::variableRead(bb1, i1, v, true)
)
or
- exists(ControlFlow::BasicBlock bb3, int i3 |
+ exists(BasicBlock bb3, int i3 |
adjacentDefReachesRead(def, v, bb1, i1, bb3, i3) and
SsaInput::variableRead(bb3, i3, _, false) and
Impl::adjacentDefRead(def, bb3, i3, bb2, i2)
@@ -748,7 +739,7 @@ deprecated private predicate adjacentDefReachesRead(
}
deprecated private predicate adjacentDefReachesUncertainRead(
- Definition def, ControlFlow::BasicBlock bb1, int i1, ControlFlow::BasicBlock bb2, int i2
+ Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2
) {
exists(SsaInput::SourceVariable v |
adjacentDefReachesRead(def, v, bb1, i1, bb2, i2) and
@@ -758,21 +749,19 @@ deprecated private predicate adjacentDefReachesUncertainRead(
/** Same as `lastRefRedef`, but skips uncertain reads. */
pragma[nomagic]
-deprecated private predicate lastRefSkipUncertainReads(
- Definition def, ControlFlow::BasicBlock bb, int i
-) {
+deprecated private predicate lastRefSkipUncertainReads(Definition def, BasicBlock bb, int i) {
Impl::lastRef(def, bb, i) and
not SsaInput::variableRead(bb, i, def.getSourceVariable(), false)
or
- exists(ControlFlow::BasicBlock bb0, int i0 |
+ exists(BasicBlock bb0, int i0 |
Impl::lastRef(def, bb0, i0) and
adjacentDefReachesUncertainRead(def, bb, i, bb0, i0)
)
}
pragma[nomagic]
-deprecated predicate lastReadSameVar(Definition def, ControlFlow::Node cfn) {
- exists(ControlFlow::BasicBlock bb, int i |
+deprecated predicate lastReadSameVar(Definition def, ControlFlowNode cfn) {
+ exists(BasicBlock bb, int i |
lastRefSkipUncertainReads(def, bb, i) and
variableReadActual(bb, i, _) and
cfn = bb.getNode(i)
@@ -818,9 +807,9 @@ private module Cached {
}
cached
- predicate implicitEntryDefinition(ControlFlow::BasicBlock bb, Ssa::SourceVariable v) {
- exists(ControlFlow::BasicBlocks::EntryBlock entry, Callable c |
- c = entry.getCallable() and
+ predicate implicitEntryDefinition(BasicBlock bb, Ssa::SourceVariable v) {
+ exists(EntryBasicBlock entry, Callable c |
+ c = entry.getEnclosingCallable() and
// In case `c` has multiple bodies, we want each body to get its own implicit
// entry definition. In case `c` doesn't have multiple bodies, the line below
// is simply the same as `bb = entry`, because `entry.getFirstNode().getASuccessor()`
@@ -856,7 +845,7 @@ private module Cached {
*/
cached
predicate updatesNamedFieldOrProp(
- ControlFlow::BasicBlock bb, int i, Call c, FieldOrPropSourceVariable fp, Callable setter
+ BasicBlock bb, int i, Call c, FieldOrPropSourceVariable fp, Callable setter
) {
FieldOrPropsImpl::updateCandidate(bb, i, fp, c) and
FieldOrPropsImpl::updatesNamedFieldOrProp(fp, c, setter)
@@ -864,7 +853,7 @@ private module Cached {
cached
predicate variableWriteQualifier(
- ControlFlow::BasicBlock bb, int i, QualifiedFieldOrPropSourceVariable v, boolean certain
+ BasicBlock bb, int i, QualifiedFieldOrPropSourceVariable v, boolean certain
) {
SsaInput::variableWrite(bb, i, v.getQualifier(), certain) and
// Eliminate corner case where a call definition can overlap with a
@@ -877,25 +866,25 @@ private module Cached {
cached
predicate explicitDefinition(WriteDefinition def, Ssa::SourceVariable v, AssignableDefinition ad) {
- exists(ControlFlow::BasicBlock bb, int i |
+ exists(BasicBlock bb, int i |
def.definesAt(v, bb, i) and
variableDefinition(bb, i, v, ad)
)
}
cached
- predicate isLiveAtEndOfBlock(Definition def, ControlFlow::BasicBlock bb) {
+ predicate isLiveAtEndOfBlock(Definition def, BasicBlock bb) {
Impl::ssaDefReachesEndOfBlock(bb, def, _)
}
cached
- Definition phiHasInputFromBlock(Ssa::PhiNode phi, ControlFlow::BasicBlock bb) {
+ Definition phiHasInputFromBlock(Ssa::PhiNode phi, BasicBlock bb) {
Impl::phiHasInputFromBlock(phi, result, bb)
}
cached
- AssignableRead getAReadAtNode(Definition def, ControlFlow::Node cfn) {
- exists(Ssa::SourceVariable v, ControlFlow::BasicBlock bb, int i |
+ AssignableRead getAReadAtNode(Definition def, ControlFlowNode cfn) {
+ exists(Ssa::SourceVariable v, BasicBlock bb, int i |
Impl::ssaDefReachesRead(v, def, bb, i) and
variableReadActual(bb, i, v) and
cfn = bb.getNode(i) and
@@ -908,10 +897,8 @@ private module Cached {
* without passing through any other read.
*/
cached
- predicate firstReadSameVar(Definition def, ControlFlow::Node cfn) {
- exists(ControlFlow::BasicBlock bb, int i |
- Impl::firstUse(def, bb, i, true) and cfn = bb.getNode(i)
- )
+ predicate firstReadSameVar(Definition def, ControlFlowNode cfn) {
+ exists(BasicBlock bb, int i | Impl::firstUse(def, bb, i, true) and cfn = bb.getNode(i))
}
/**
@@ -920,11 +907,8 @@ private module Cached {
* passing through another read.
*/
cached
- predicate adjacentReadPairSameVar(Definition def, ControlFlow::Node cfn1, ControlFlow::Node cfn2) {
- exists(
- ControlFlow::BasicBlock bb1, int i1, ControlFlow::BasicBlock bb2, int i2,
- Ssa::SourceVariable v
- |
+ predicate adjacentReadPairSameVar(Definition def, ControlFlowNode cfn1, ControlFlowNode cfn2) {
+ exists(BasicBlock bb1, int i1, BasicBlock bb2, int i2, Ssa::SourceVariable v |
Impl::ssaDefReachesRead(v, def, bb1, i1) and
Impl::adjacentUseUse(bb1, i1, bb2, i2, v, true) and
cfn1 = bb1.getNode(i1) and
@@ -940,7 +924,7 @@ private module Cached {
cached
predicate isLiveOutRefParameterDefinition(Ssa::Definition def, Parameter p) {
p.isOutOrRef() and
- exists(Ssa::SourceVariable v, Ssa::Definition def0, ControlFlow::BasicBlock bb, int i |
+ exists(Ssa::SourceVariable v, Ssa::Definition def0, BasicBlock bb, int i |
v = def.getSourceVariable() and
p = v.getAssignable() and
def = def0.getAnUltimateDefinition() and
@@ -1022,32 +1006,12 @@ private module Cached {
import Cached
-private string getSplitString(Definition def) {
- exists(ControlFlow::BasicBlock bb, int i, ControlFlow::Node cfn |
- def.definesAt(_, bb, i) and
- result = cfn.(ControlFlow::Nodes::ElementNode).getSplitsString()
- |
- cfn = bb.getNode(i)
- or
- not exists(bb.getNode(i)) and
- cfn = bb.getFirstNode()
- )
-}
-
-string getToStringPrefix(Definition def) {
- result = "[" + getSplitString(def) + "] "
- or
- not exists(getSplitString(def)) and
- result = ""
-}
-
private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInputSig {
private import csharp as Cs
- private import semmle.code.csharp.controlflow.BasicBlocks
private import codeql.util.Boolean
- class Expr extends ControlFlow::Node {
- predicate hasCfgNode(ControlFlow::BasicBlock bb, int i) { this = bb.getNode(i) }
+ class Expr extends ControlFlowNode {
+ predicate hasCfgNode(BasicBlock bb, int i) { this = bb.getNode(i) }
}
Expr getARead(Definition def) { exists(getAReadAtNode(def, result)) }
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll
index 99a50b36873e..d3ae19c6d18b 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll
@@ -47,7 +47,7 @@ predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::ContentSet c)
private predicate localTaintExprStep(Expr e1, Expr e2) {
e1 = e2.(ElementAccess).getQualifier()
or
- e1 = e2.(AddExpr).getAnOperand()
+ e1 = e2.(AddOperation).getAnOperand()
or
// A comparison expression where taint can flow from one of the
// operands if the other operand is a constant value.
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/BoundSpecific.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/BoundSpecific.qll
index 3885c11afd14..037422684306 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/BoundSpecific.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/BoundSpecific.qll
@@ -10,7 +10,7 @@ private import semmle.code.csharp.dataflow.internal.rangeanalysis.SsaUtils as SU
class SsaVariable = SU::SsaVariable;
-class Expr = CS::ControlFlow::Nodes::ExprNode;
+class Expr = CS::ControlFlowNodes::ExprNode;
class Location = CS::Location;
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll
index e3f5deb98989..602f9f0022d0 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll
@@ -7,7 +7,7 @@ private import Ssa
private import SsaUtils
private import RangeUtils
-private class ExprNode = ControlFlow::Nodes::ExprNode;
+private class ExprNode = ControlFlowNodes::ExprNode;
/**
* Holds if `pa` is an access to the `Length` property of an array.
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ModulusAnalysisSpecific.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ModulusAnalysisSpecific.qll
index c9c3a937ef9e..fbc09e7ec52d 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ModulusAnalysisSpecific.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ModulusAnalysisSpecific.qll
@@ -4,15 +4,14 @@ module Private {
private import semmle.code.csharp.dataflow.internal.rangeanalysis.RangeUtils as RU
private import SsaUtils as SU
private import SsaReadPositionCommon
- private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl as CfgImpl
- class BasicBlock = CS::ControlFlow::BasicBlock;
+ class BasicBlock = CS::BasicBlock;
class SsaVariable = SU::SsaVariable;
class SsaPhiNode = CS::Ssa::PhiNode;
- class Expr = CS::ControlFlow::Nodes::ExprNode;
+ class Expr = CS::ControlFlowNodes::ExprNode;
class Guard = RU::Guard;
@@ -20,17 +19,17 @@ module Private {
class ConditionalExpr = RU::ExprNode::ConditionalExpr;
- class AddExpr = RU::ExprNode::AddExpr;
+ class AddExpr = RU::ExprNode::AddOperation;
- class SubExpr = RU::ExprNode::SubExpr;
+ class SubExpr = RU::ExprNode::SubOperation;
- class RemExpr = RU::ExprNode::RemExpr;
+ class RemExpr = RU::ExprNode::RemOperation;
- class BitwiseAndExpr = RU::ExprNode::BitwiseAndExpr;
+ class BitwiseAndExpr = RU::ExprNode::BitwiseAndOperation;
- class MulExpr = RU::ExprNode::MulExpr;
+ class MulExpr = RU::ExprNode::MulOperation;
- class LeftShiftExpr = RU::ExprNode::LeftShiftExpr;
+ class LeftShiftExpr = RU::ExprNode::LeftShiftOperation;
predicate guardControlsSsaRead = RU::guardControlsSsaRead/3;
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/RangeUtils.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/RangeUtils.qll
index 656bf9aae211..e3bd4f465de4 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/RangeUtils.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/RangeUtils.qll
@@ -9,7 +9,7 @@ private module Impl {
private import SsaReadPositionCommon
private import semmle.code.csharp.controlflow.Guards as G
- private class ExprNode = ControlFlow::Nodes::ExprNode;
+ private class ExprNode = ControlFlowNodes::ExprNode;
/** Holds if `parent` having child `child` implies `parentNode` having child `childNode`. */
predicate hasChild(Expr parent, Expr child, ExprNode parentNode, ExprNode childNode) {
@@ -20,8 +20,12 @@ private module Impl {
/** Holds if SSA definition `def` equals `e + delta`. */
predicate ssaUpdateStep(ExplicitDefinition def, ExprNode e, int delta) {
- exists(ControlFlow::Node cfn | cfn = def.getControlFlowNode() |
- e = cfn.(ExprNode::Assignment).getRValue() and delta = 0
+ exists(ControlFlowNode cfn | cfn = def.getControlFlowNode() |
+ e = cfn.(ExprNode::Assignment).getRValue() and
+ delta = 0 and
+ not cfn instanceof ExprNode::AssignOperation
+ or
+ e = cfn.(ExprNode::AssignOperation) and delta = 0
or
e = cfn.(ExprNode::PostIncrExpr).getOperand() and delta = 1
or
@@ -48,15 +52,15 @@ private module Impl {
e2.(ExprNode::PreDecrExpr).getOperand() = e1 and delta = -1
or
exists(ConstantIntegerExpr x |
- e2.(ExprNode::AddExpr).getAnOperand() = e1 and
- e2.(ExprNode::AddExpr).getAnOperand() = x and
+ e2.(ExprNode::AddOperation).getAnOperand() = e1 and
+ e2.(ExprNode::AddOperation).getAnOperand() = x and
e1 != x and
x.getIntValue() = delta
)
or
exists(ConstantIntegerExpr x |
- e2.(ExprNode::SubExpr).getLeftOperand() = e1 and
- e2.(ExprNode::SubExpr).getRightOperand() = x and
+ e2.(ExprNode::SubOperation).getLeftOperand() = e1 and
+ e2.(ExprNode::SubOperation).getRightOperand() = x and
x.getIntValue() = -delta
)
or
@@ -79,9 +83,7 @@ private module Impl {
/**
* Holds if basic block `bb` is guarded by this guard having value `v`.
*/
- predicate controlsBasicBlock(ControlFlow::BasicBlock bb, G::GuardValue v) {
- super.controlsBasicBlock(bb, v)
- }
+ predicate controlsBasicBlock(BasicBlock bb, G::GuardValue v) { super.controlsBasicBlock(bb, v) }
/**
* Holds if this guard is an equality test between `e1` and `e2`. If the test is
@@ -156,7 +158,7 @@ import Impl
module ExprNode {
private import csharp as CS
- private class ExprNode = CS::ControlFlow::Nodes::ExprNode;
+ private class ExprNode = CS::ControlFlowNodes::ExprNode;
private import Sign
@@ -218,6 +220,11 @@ module ExprNode {
override CS::AssignExpr e;
}
+ /** A compound assignment operation. */
+ class AssignOperation extends Assignment, BinaryOperation {
+ override CS::AssignOperation e;
+ }
+
/** A unary operation. */
class UnaryOperation extends ExprNode {
override CS::UnaryOperation e;
@@ -309,78 +316,78 @@ module ExprNode {
}
/** An addition operation. */
- class AddExpr extends BinaryOperation {
- override CS::AddExpr e;
+ class AddOperation extends BinaryOperation {
+ override CS::AddOperation e;
override TAddOp getOp() { any() }
}
/** A subtraction operation. */
- class SubExpr extends BinaryOperation {
- override CS::SubExpr e;
+ class SubOperation extends BinaryOperation {
+ override CS::SubOperation e;
override TSubOp getOp() { any() }
}
/** A multiplication operation. */
- class MulExpr extends BinaryOperation {
- override CS::MulExpr e;
+ class MulOperation extends BinaryOperation {
+ override CS::MulOperation e;
override TMulOp getOp() { any() }
}
/** A division operation. */
- class DivExpr extends BinaryOperation {
- override CS::DivExpr e;
+ class DivOperation extends BinaryOperation {
+ override CS::DivOperation e;
override TDivOp getOp() { any() }
}
/** A remainder operation. */
- class RemExpr extends BinaryOperation {
- override CS::RemExpr e;
+ class RemOperation extends BinaryOperation {
+ override CS::RemOperation e;
override TRemOp getOp() { any() }
}
/** A bitwise-and operation. */
- class BitwiseAndExpr extends BinaryOperation {
- override CS::BitwiseAndExpr e;
+ class BitwiseAndOperation extends BinaryOperation {
+ override CS::BitwiseAndOperation e;
override TBitAndOp getOp() { any() }
}
/** A bitwise-or operation. */
- class BitwiseOrExpr extends BinaryOperation {
- override CS::BitwiseOrExpr e;
+ class BitwiseOrOperation extends BinaryOperation {
+ override CS::BitwiseOrOperation e;
override TBitOrOp getOp() { any() }
}
/** A bitwise-xor operation. */
- class BitwiseXorExpr extends BinaryOperation {
- override CS::BitwiseXorExpr e;
+ class BitwiseXorOperation extends BinaryOperation {
+ override CS::BitwiseXorOperation e;
override TBitXorOp getOp() { any() }
}
/** A left-shift operation. */
- class LeftShiftExpr extends BinaryOperation {
- override CS::LeftShiftExpr e;
+ class LeftShiftOperation extends BinaryOperation {
+ override CS::LeftShiftOperation e;
override TLeftShiftOp getOp() { any() }
}
/** A right-shift operation. */
- class RightShiftExpr extends BinaryOperation {
- override CS::RightShiftExpr e;
+ class RightShiftOperation extends BinaryOperation {
+ override CS::RightShiftOperation e;
override TRightShiftOp getOp() { any() }
}
/** An unsigned right-shift operation. */
- class UnsignedRightShiftExpr extends BinaryOperation {
- override CS::UnsignedRightShiftExpr e;
+ class UnsignedRightShiftOperation extends BinaryOperation {
+ override CS::UnsignedRightShiftOperation e;
override TUnsignedRightShiftOp getOp() { any() }
}
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll
index 1bd86e19f34d..076ee8f553ed 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll
@@ -33,7 +33,7 @@ module Private {
class Type = CS::Type;
- class Expr = CS::ControlFlow::Nodes::ExprNode;
+ class Expr = CS::ControlFlowNodes::ExprNode;
class VariableUpdate = CS::Ssa::ExplicitDefinition;
@@ -41,7 +41,7 @@ module Private {
class RealLiteral = RU::ExprNode::RealLiteral;
- class DivExpr = RU::ExprNode::DivExpr;
+ class DivExpr = RU::ExprNode::DivOperation;
class UnaryOperation = RU::ExprNode::UnaryOperation;
@@ -63,7 +63,7 @@ private module Impl {
private import SsaReadPositionCommon
private import semmle.code.csharp.commons.ComparisonTest
- private class ExprNode = ControlFlow::Nodes::ExprNode;
+ private class ExprNode = ControlFlowNodes::ExprNode;
/** Gets the character value of expression `e`. */
string getCharValue(ExprNode e) { result = e.getValue() and e.getType() instanceof CharType }
@@ -130,6 +130,11 @@ private module Impl {
adef = def.getADefinition() and
hasChild(adef.getExpr(), adef.getSource(), def.getControlFlowNode(), result)
)
+ or
+ exists(AssignableDefinitions::AssignOperationDefinition adef |
+ adef = def.getADefinition() and
+ result.getExpr() = adef.getSource()
+ )
}
/** Holds if `def` can have any sign. */
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll
index cbf4a1d57393..c55109528049 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll
@@ -2,15 +2,14 @@
* Provides C#-specific definitions for use in the `SsaReadPosition`.
*/
-private import csharp
+private import csharp as CS
private import SsaReadPositionCommon
-private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl as CfgImpl
-class SsaVariable = Ssa::Definition;
+class SsaVariable = CS::Ssa::Definition;
-class SsaPhiNode = Ssa::PhiNode;
+class SsaPhiNode = CS::Ssa::PhiNode;
-class BasicBlock = ControlFlow::BasicBlock;
+class BasicBlock = CS::BasicBlock;
/** Gets a basic block in which SSA variable `v` is read. */
BasicBlock getAReadBasicBlock(SsaVariable v) {
@@ -21,20 +20,18 @@ private class PhiInputEdgeBlock extends BasicBlock {
PhiInputEdgeBlock() { this = any(SsaReadPositionPhiInputEdge edge).getOrigBlock() }
}
-private int getId(PhiInputEdgeBlock bb) {
- exists(CfgImpl::AstNode n | result = n.getId() |
- n = bb.getFirstNode().getAstNode()
- or
- n = bb.(ControlFlow::BasicBlocks::EntryBlock).getCallable()
- )
+private predicate id(CS::ControlFlowElementOrCallable x, CS::ControlFlowElementOrCallable y) {
+ x = y
}
-private string getSplitString(PhiInputEdgeBlock bb) {
- result = bb.getFirstNode().(ControlFlow::Nodes::ElementNode).getSplitsString()
- or
- not exists(bb.getFirstNode().(ControlFlow::Nodes::ElementNode).getSplitsString()) and
- result = ""
-}
+private predicate idOfAst(CS::ControlFlowElementOrCallable x, int y) =
+ equivalenceRelation(id/2)(x, y)
+
+private predicate idOf(PhiInputEdgeBlock x, int y) { idOfAst(x.getFirstNode().getAstNode(), y) }
+
+private int getId1(PhiInputEdgeBlock bb) { idOf(bb, result) }
+
+private string getId2(PhiInputEdgeBlock bb) { bb.getFirstNode().getIdTag() = result }
/**
* Declarations to be exposed to users of SsaReadPositionCommon.
@@ -50,7 +47,7 @@ module Public {
rank[r](SsaReadPositionPhiInputEdge e |
e.phiInput(phi, _)
|
- e order by getId(e.getOrigBlock()), getSplitString(e.getOrigBlock())
+ e order by getId1(e.getOrigBlock()), getId2(e.getOrigBlock())
)
}
}
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaUtils.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaUtils.qll
index b26082b6250a..682801ce121f 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaUtils.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaUtils.qll
@@ -7,7 +7,7 @@ private import Ssa
private import RangeUtils
private import ConstantUtils
-private class ExprNode = ControlFlow::Nodes::ExprNode;
+private class ExprNode = ControlFlowNodes::ExprNode;
/** An SSA variable. */
class SsaVariable extends Definition {
@@ -29,7 +29,7 @@ ExprNode getAnExplicitDefinitionRead(ExprNode src) {
ExprNode ssaRead(Definition v, int delta) {
exists(v.getAReadAtNode(result)) and delta = 0
or
- exists(ExprNode::AddExpr add, int d1, ConstantIntegerExpr c |
+ exists(ExprNode::AddOperation add, int d1, ConstantIntegerExpr c |
result = add and
delta = d1 - c.getIntValue()
|
@@ -38,7 +38,7 @@ ExprNode ssaRead(Definition v, int delta) {
add.getRightOperand() = ssaRead(v, d1) and add.getLeftOperand() = c
)
or
- exists(ExprNode::SubExpr sub, int d1, ConstantIntegerExpr c |
+ exists(ExprNode::SubOperation sub, int d1, ConstantIntegerExpr c |
result = sub and
sub.getLeftOperand() = ssaRead(v, d1) and
sub.getRightOperand() = c and
diff --git a/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll b/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll
index a83967441d74..11cf60cc7881 100644
--- a/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll
@@ -20,6 +20,9 @@ class DispatchCall extends Internal::TDispatchCall {
/** Gets the underlying expression of this call. */
Expr getCall() { result = Internal::getCall(this) }
+ /** Gets the control flow node of this call. */
+ ControlFlowNode getControlFlowNode() { result = Internal::getControlFlowNode(this) }
+
/** Gets the `i`th argument of this call. */
Expr getArgument(int i) { result = Internal::getArgument(this, i) }
@@ -90,7 +93,12 @@ private module Internal {
not mc.isLateBound() and
not isExtensionAccessorCall(mc)
} or
- TDispatchAccessorCall(AccessorCall ac) or
+ TDispatchAccessorCall(AccessorCall ac, boolean isRead) {
+ // For compound assignments an AccessorCall can be both a read and a write
+ ac instanceof AssignableRead and isRead = true
+ or
+ ac instanceof AssignableWrite and isRead = false
+ } or
TDispatchOperatorCall(OperatorCall oc) { not oc.isLateBound() } or
TDispatchReflectionCall(MethodCall mc, string name, Expr object, Expr qualifier, int args) {
isReflectionCall(mc, name, object, qualifier, args)
@@ -117,6 +125,11 @@ private module Internal {
cached
Expr getCall(DispatchCall dc) { result = dc.(DispatchCallImpl).getCall() }
+ cached
+ ControlFlowNode getControlFlowNode(DispatchCall dc) {
+ result = dc.(DispatchCallImpl).getControlFlowNode()
+ }
+
cached
Expr getArgument(DispatchCall dc, int i) { result = dc.(DispatchCallImpl).getArgument(i) }
@@ -202,11 +215,9 @@ private module Internal {
private predicate isPotentialEventCall(
AssignArithmeticOperation aao, DynamicMemberAccess dma, string name
) {
- exists(DynamicOperatorCall doc, AssignExpr ae |
- ae = aao.getExpandedAssignment() and
- dma = ae.getLValue() and
- doc = ae.getRValue()
- |
+ aao instanceof DynamicOperatorCall and
+ dma = aao.getLeftOperand() and
+ (
aao instanceof AssignAddExpr and
name = "add_" + dma.getLateBoundTargetName()
or
@@ -226,6 +237,9 @@ private module Internal {
/** Gets the underlying expression of this call. */
abstract Expr getCall();
+ /** Gets the control flow node of this call. */
+ ControlFlowNode getControlFlowNode() { result = this.getCall().getControlFlowNode() }
+
/** Gets the `i`th argument of this call. */
abstract Expr getArgument(int i);
@@ -879,13 +893,28 @@ private module Internal {
* into account.
*/
private class DispatchAccessorCall extends DispatchOverridableCall, TDispatchAccessorCall {
- override AccessorCall getCall() { this = TDispatchAccessorCall(result) }
+ private predicate isRead() { this = TDispatchAccessorCall(_, true) }
+
+ override ControlFlowNode getControlFlowNode() {
+ if this.isRead()
+ then result = this.getCall().getControlFlowNode()
+ else
+ exists(AssignableDefinition def |
+ def.getTargetAccess() = this.getCall() and result = def.getExpr().getControlFlowNode()
+ )
+ }
+
+ override AccessorCall getCall() { this = TDispatchAccessorCall(result, _) }
override Expr getArgument(int i) { result = this.getCall().getArgument(i) }
override Expr getQualifier() { result = this.getCall().(MemberAccess).getQualifier() }
- override Accessor getAStaticTarget() { result = this.getCall().getTarget() }
+ override Accessor getAStaticTarget() {
+ if this.isRead()
+ then result = this.getCall().getReadTarget()
+ else result = this.getCall().getWriteTarget()
+ }
override RuntimeAccessor getADynamicTarget() {
result = DispatchOverridableCall.super.getADynamicTarget() and
diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/ArithmeticOperation.qll b/csharp/ql/lib/semmle/code/csharp/exprs/ArithmeticOperation.qll
index f20bfba1589a..193c48ed3a2b 100644
--- a/csharp/ql/lib/semmle/code/csharp/exprs/ArithmeticOperation.qll
+++ b/csharp/ql/lib/semmle/code/csharp/exprs/ArithmeticOperation.qll
@@ -107,7 +107,7 @@ class BinaryArithmeticOperation extends ArithmeticOperation, BinaryOperation, @b
/**
* An addition operation, for example `x + y`.
*/
-class AddExpr extends BinaryArithmeticOperation, @add_expr {
+class AddExpr extends BinaryArithmeticOperation, AddOperation, @add_expr {
override string getOperator() { result = "+" }
override string getAPrimaryQlClass() { result = "AddExpr" }
@@ -116,7 +116,7 @@ class AddExpr extends BinaryArithmeticOperation, @add_expr {
/**
* A subtraction operation, for example `x - y`.
*/
-class SubExpr extends BinaryArithmeticOperation, @sub_expr {
+class SubExpr extends BinaryArithmeticOperation, SubOperation, @sub_expr {
override string getOperator() { result = "-" }
override string getAPrimaryQlClass() { result = "SubExpr" }
@@ -125,7 +125,7 @@ class SubExpr extends BinaryArithmeticOperation, @sub_expr {
/**
* A multiplication operation, for example `x * y`.
*/
-class MulExpr extends BinaryArithmeticOperation, @mul_expr {
+class MulExpr extends BinaryArithmeticOperation, MulOperation, @mul_expr {
override string getOperator() { result = "*" }
override string getAPrimaryQlClass() { result = "MulExpr" }
@@ -134,22 +134,16 @@ class MulExpr extends BinaryArithmeticOperation, @mul_expr {
/**
* A division operation, for example `x / y`.
*/
-class DivExpr extends BinaryArithmeticOperation, @div_expr {
+class DivExpr extends BinaryArithmeticOperation, DivOperation, @div_expr {
override string getOperator() { result = "/" }
- /** Gets the numerator of this division operation. */
- Expr getNumerator() { result = this.getLeftOperand() }
-
- /** Gets the denominator of this division operation. */
- Expr getDenominator() { result = this.getRightOperand() }
-
override string getAPrimaryQlClass() { result = "DivExpr" }
}
/**
* A remainder operation, for example `x % y`.
*/
-class RemExpr extends BinaryArithmeticOperation, @rem_expr {
+class RemExpr extends BinaryArithmeticOperation, RemOperation, @rem_expr {
override string getOperator() { result = "%" }
override string getAPrimaryQlClass() { result = "RemExpr" }
diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Assignment.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Assignment.qll
index 9fa2a93724d4..467149011d2c 100644
--- a/csharp/ql/lib/semmle/code/csharp/exprs/Assignment.qll
+++ b/csharp/ql/lib/semmle/code/csharp/exprs/Assignment.qll
@@ -17,18 +17,14 @@ class Assignment extends BinaryOperation, @assign_expr {
implies
// Same as `this.(LocalVariableDeclExpr).hasInitializer()` but avoids
// negative recursion
- expr_parent(_, 0, this)
+ expr_parent(_, 1, this)
}
- override Expr getLeftOperand() { result = this.getChild(1) }
-
- override Expr getRightOperand() { result = this.getChild(0) }
-
/** Gets the left operand of this assignment. */
- Expr getLValue() { result = this.getChild(1) }
+ Expr getLValue() { result = this.getLeftOperand() }
/** Gets the right operand of this assignment. */
- Expr getRValue() { result = this.getChild(0) }
+ Expr getRValue() { result = this.getRightOperand() }
/** Gets the variable being assigned to, if any. */
Variable getTargetVariable() { result.getAnAccess() = this.getLValue() }
@@ -65,36 +61,36 @@ class AssignExpr extends Assignment, @simple_assign_expr {
/**
* An assignment operation. Either an arithmetic assignment operation
* (`AssignArithmeticOperation`), a bitwise assignment operation
- * (`AssignBitwiseOperation`), or an event assignment (`AddOrRemoveEventExpr`).
+ * (`AssignBitwiseOperation`), an event assignment (`AddOrRemoveEventExpr`), or
+ * a null-coalescing assignment (`AssignCoalesceExpr`).
*/
class AssignOperation extends Assignment, @assign_op_expr {
override string getOperator() { none() }
/**
- * Gets the expanded version of this assignment operation, if any.
- *
- * For example, if this assignment operation is `x += y` then
- * the expanded assignment is `x = x + y`.
- *
- * If an expanded version exists, then it is used in the control
- * flow graph.
+ * Expanded versions of compound assignments are no longer extracted.
*/
- AssignExpr getExpandedAssignment() { expr_parent(result, 2, this) }
+ deprecated AssignExpr getExpandedAssignment() { none() }
/**
- * Holds if this assignment operation has an expanded version.
- *
- * For example, if this assignment operation is `x += y` then
- * it has the expanded version `x = x + y`.
- *
- * If an expanded version exists, then it is used in the control
- * flow graph.
+ * Expanded versions of compound assignments are no longer extracted.
*/
- predicate hasExpandedAssignment() { exists(this.getExpandedAssignment()) }
+ deprecated predicate hasExpandedAssignment() { none() }
override string toString() { result = "... " + this.getOperator() + " ..." }
}
+/**
+ * A compound assignment operation that implicitly invokes an operator.
+ * For example, `x += y` assigns the result of `x + y` to `x`.
+ *
+ * Either an arithmetic assignment operation (`AssignArithmeticOperation`) or a bitwise
+ * assignment operation (`AssignBitwiseOperation`).
+ */
+class AssignCallOperation extends AssignOperation, OperatorCall, @assign_op_call_expr {
+ override string toString() { result = AssignOperation.super.toString() }
+}
+
/**
* An arithmetic assignment operation. Either an addition assignment operation
* (`AssignAddExpr`), a subtraction assignment operation (`AssignSubExpr`), a
@@ -102,12 +98,12 @@ class AssignOperation extends Assignment, @assign_op_expr {
* operation (`AssignDivExpr`), or a remainder assignment operation
* (`AssignRemExpr`).
*/
-class AssignArithmeticOperation extends AssignOperation, @assign_arith_expr { }
+class AssignArithmeticOperation extends AssignCallOperation, @assign_arith_expr { }
/**
* An addition assignment operation, for example `x += y`.
*/
-class AssignAddExpr extends AssignArithmeticOperation, @assign_add_expr {
+class AssignAddExpr extends AssignArithmeticOperation, AddOperation, @assign_add_expr {
override string getOperator() { result = "+=" }
override string getAPrimaryQlClass() { result = "AssignAddExpr" }
@@ -116,7 +112,7 @@ class AssignAddExpr extends AssignArithmeticOperation, @assign_add_expr {
/**
* A subtraction assignment operation, for example `x -= y`.
*/
-class AssignSubExpr extends AssignArithmeticOperation, @assign_sub_expr {
+class AssignSubExpr extends AssignArithmeticOperation, SubOperation, @assign_sub_expr {
override string getOperator() { result = "-=" }
override string getAPrimaryQlClass() { result = "AssignSubExpr" }
@@ -125,7 +121,7 @@ class AssignSubExpr extends AssignArithmeticOperation, @assign_sub_expr {
/**
* An multiplication assignment operation, for example `x *= y`.
*/
-class AssignMulExpr extends AssignArithmeticOperation, @assign_mul_expr {
+class AssignMulExpr extends AssignArithmeticOperation, MulOperation, @assign_mul_expr {
override string getOperator() { result = "*=" }
override string getAPrimaryQlClass() { result = "AssignMulExpr" }
@@ -134,7 +130,7 @@ class AssignMulExpr extends AssignArithmeticOperation, @assign_mul_expr {
/**
* An division assignment operation, for example `x /= y`.
*/
-class AssignDivExpr extends AssignArithmeticOperation, @assign_div_expr {
+class AssignDivExpr extends AssignArithmeticOperation, DivOperation, @assign_div_expr {
override string getOperator() { result = "/=" }
override string getAPrimaryQlClass() { result = "AssignDivExpr" }
@@ -143,7 +139,7 @@ class AssignDivExpr extends AssignArithmeticOperation, @assign_div_expr {
/**
* A remainder assignment operation, for example `x %= y`.
*/
-class AssignRemExpr extends AssignArithmeticOperation, @assign_rem_expr {
+class AssignRemExpr extends AssignArithmeticOperation, RemOperation, @assign_rem_expr {
override string getOperator() { result = "%=" }
override string getAPrimaryQlClass() { result = "AssignRemExpr" }
@@ -158,12 +154,12 @@ class AssignRemExpr extends AssignArithmeticOperation, @assign_rem_expr {
* operation (`AssignRightShiftExpr`), or an unsigned right-shift assignment
* operation (`AssignUnsignedRightShiftExpr`).
*/
-class AssignBitwiseOperation extends AssignOperation, @assign_bitwise_expr { }
+class AssignBitwiseOperation extends AssignCallOperation, @assign_bitwise_expr { }
/**
* A bitwise-and assignment operation, for example `x &= y`.
*/
-class AssignAndExpr extends AssignBitwiseOperation, @assign_and_expr {
+class AssignAndExpr extends AssignBitwiseOperation, BitwiseAndOperation, @assign_and_expr {
override string getOperator() { result = "&=" }
override string getAPrimaryQlClass() { result = "AssignAndExpr" }
@@ -172,7 +168,7 @@ class AssignAndExpr extends AssignBitwiseOperation, @assign_and_expr {
/**
* A bitwise-or assignment operation, for example `x |= y`.
*/
-class AssignOrExpr extends AssignBitwiseOperation, @assign_or_expr {
+class AssignOrExpr extends AssignBitwiseOperation, BitwiseOrOperation, @assign_or_expr {
override string getOperator() { result = "|=" }
override string getAPrimaryQlClass() { result = "AssignOrExpr" }
@@ -181,7 +177,7 @@ class AssignOrExpr extends AssignBitwiseOperation, @assign_or_expr {
/**
* A bitwise exclusive-or assignment operation, for example `x ^= y`.
*/
-class AssignXorExpr extends AssignBitwiseOperation, @assign_xor_expr {
+class AssignXorExpr extends AssignBitwiseOperation, BitwiseXorOperation, @assign_xor_expr {
override string getOperator() { result = "^=" }
override string getAPrimaryQlClass() { result = "AssignXorExpr" }
@@ -190,7 +186,7 @@ class AssignXorExpr extends AssignBitwiseOperation, @assign_xor_expr {
/**
* A left-shift assignment operation, for example `x <<= y`.
*/
-class AssignLeftShiftExpr extends AssignBitwiseOperation, @assign_lshift_expr {
+class AssignLeftShiftExpr extends AssignBitwiseOperation, LeftShiftOperation, @assign_lshift_expr {
override string getOperator() { result = "<<=" }
override string getAPrimaryQlClass() { result = "AssignLeftShiftExpr" }
@@ -199,7 +195,7 @@ class AssignLeftShiftExpr extends AssignBitwiseOperation, @assign_lshift_expr {
/**
* A right-shift assignment operation, for example `x >>= y`.
*/
-class AssignRightShiftExpr extends AssignBitwiseOperation, @assign_rshift_expr {
+class AssignRightShiftExpr extends AssignBitwiseOperation, RightShiftOperation, @assign_rshift_expr {
override string getOperator() { result = ">>=" }
override string getAPrimaryQlClass() { result = "AssignRightShiftExpr" }
@@ -208,12 +204,19 @@ class AssignRightShiftExpr extends AssignBitwiseOperation, @assign_rshift_expr {
/**
* An unsigned right-shift assignment operation, for example `x >>>= y`.
*/
-class AssignUnsighedRightShiftExpr extends AssignBitwiseOperation, @assign_urshift_expr {
+class AssignUnsignedRightShiftExpr extends AssignBitwiseOperation, UnsignedRightShiftOperation,
+ @assign_urshift_expr
+{
override string getOperator() { result = ">>>=" }
- override string getAPrimaryQlClass() { result = "AssignUnsighedRightShiftExpr" }
+ override string getAPrimaryQlClass() { result = "AssignUnsignedRightShiftExpr" }
}
+/**
+ * DEPRECATED: Use `AssignUnsignedRightShiftExpr` instead.
+ */
+deprecated class AssignUnsighedRightShiftExpr = AssignUnsignedRightShiftExpr;
+
/**
* An event assignment. Either an event addition (`AddEventExpr`) or an event
* removal (`RemoveEventExpr`).
@@ -222,9 +225,9 @@ class AddOrRemoveEventExpr extends AssignOperation, @assign_event_expr {
/** Gets the event targeted by this event assignment. */
Event getTarget() { result = this.getLValue().getTarget() }
- override EventAccess getLValue() { result = this.getChild(1) }
+ override EventAccess getLValue() { result = this.getChild(0) }
- override Expr getRValue() { result = this.getChild(0) }
+ override EventAccess getLeftOperand() { result = this.getChild(0) }
}
/**
@@ -276,7 +279,7 @@ class RemoveEventExpr extends AddOrRemoveEventExpr, @remove_event_expr {
/**
* A null-coalescing assignment operation, for example `x ??= y`.
*/
-class AssignCoalesceExpr extends AssignOperation, @assign_coalesce_expr {
+class AssignCoalesceExpr extends AssignOperation, NullCoalescingOperation, @assign_coalesce_expr {
override string toString() { result = "... ??= ..." }
override string getAPrimaryQlClass() { result = "AssignCoalesceExpr" }
diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/BitwiseOperation.qll b/csharp/ql/lib/semmle/code/csharp/exprs/BitwiseOperation.qll
index d818a1d08f87..14bb3d74e2b2 100644
--- a/csharp/ql/lib/semmle/code/csharp/exprs/BitwiseOperation.qll
+++ b/csharp/ql/lib/semmle/code/csharp/exprs/BitwiseOperation.qll
@@ -41,7 +41,7 @@ class BinaryBitwiseOperation extends BitwiseOperation, BinaryOperation, @bin_bit
/**
* A left-shift operation, for example `x << y`.
*/
-class LeftShiftExpr extends BinaryBitwiseOperation, @lshift_expr {
+class LeftShiftExpr extends BinaryBitwiseOperation, LeftShiftOperation, @lshift_expr {
override string getOperator() { result = "<<" }
override string getAPrimaryQlClass() { result = "LeftShiftExpr" }
@@ -50,7 +50,7 @@ class LeftShiftExpr extends BinaryBitwiseOperation, @lshift_expr {
/**
* A right-shift operation, for example `x >> y`.
*/
-class RightShiftExpr extends BinaryBitwiseOperation, @rshift_expr {
+class RightShiftExpr extends BinaryBitwiseOperation, RightShiftOperation, @rshift_expr {
override string getOperator() { result = ">>" }
override string getAPrimaryQlClass() { result = "RightShiftExpr" }
@@ -59,7 +59,9 @@ class RightShiftExpr extends BinaryBitwiseOperation, @rshift_expr {
/**
* An unsigned right-shift operation, for example `x >>> y`.
*/
-class UnsignedRightShiftExpr extends BinaryBitwiseOperation, @urshift_expr {
+class UnsignedRightShiftExpr extends BinaryBitwiseOperation, UnsignedRightShiftOperation,
+ @urshift_expr
+{
override string getOperator() { result = ">>>" }
override string getAPrimaryQlClass() { result = "UnsignedRightShiftExpr" }
@@ -68,7 +70,7 @@ class UnsignedRightShiftExpr extends BinaryBitwiseOperation, @urshift_expr {
/**
* A bitwise-and operation, for example `x & y`.
*/
-class BitwiseAndExpr extends BinaryBitwiseOperation, @bit_and_expr {
+class BitwiseAndExpr extends BinaryBitwiseOperation, BitwiseAndOperation, @bit_and_expr {
override string getOperator() { result = "&" }
override string getAPrimaryQlClass() { result = "BitwiseAndExpr" }
@@ -77,7 +79,7 @@ class BitwiseAndExpr extends BinaryBitwiseOperation, @bit_and_expr {
/**
* A bitwise-or operation, for example `x | y`.
*/
-class BitwiseOrExpr extends BinaryBitwiseOperation, @bit_or_expr {
+class BitwiseOrExpr extends BinaryBitwiseOperation, BitwiseOrOperation, @bit_or_expr {
override string getOperator() { result = "|" }
override string getAPrimaryQlClass() { result = "BitwiseOrExpr" }
@@ -86,7 +88,7 @@ class BitwiseOrExpr extends BinaryBitwiseOperation, @bit_or_expr {
/**
* A bitwise exclusive-or operation, for example `x ^ y`.
*/
-class BitwiseXorExpr extends BinaryBitwiseOperation, @bit_xor_expr {
+class BitwiseXorExpr extends BinaryBitwiseOperation, BitwiseXorOperation, @bit_xor_expr {
override string getOperator() { result = "^" }
override string getAPrimaryQlClass() { result = "BitwiseXorExpr" }
diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll
index f8b51a990ed1..2a0d0fc44720 100644
--- a/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll
+++ b/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll
@@ -478,7 +478,7 @@ class ConstructorInitializer extends Call, @constructor_init_expr {
}
/**
- * A call to a user-defined operator, for example `this + other`
+ * A call to an operator, for example `this + other`
* on line 7 in
*
* ```csharp
@@ -493,12 +493,16 @@ class ConstructorInitializer extends Call, @constructor_init_expr {
* }
* ```
*/
-class OperatorCall extends Call, LateBindableExpr, @operator_invocation_expr {
+class OperatorCall extends Call, LateBindableExpr, @op_invoke_expr {
override Operator getTarget() { expr_call(this, result) }
override Operator getARuntimeTarget() { result = Call.super.getARuntimeTarget() }
- override string toString() { result = "call to operator " + this.getTarget().getName() }
+ override string toString() {
+ if this instanceof DynamicOperatorCall
+ then result = "dynamic call to operator " + this.getLateBoundTargetName()
+ else result = "call to operator " + this.getTarget().getName()
+ }
override string getAPrimaryQlClass() { result = "OperatorCall" }
}
@@ -629,7 +633,25 @@ class FunctionPointerCall extends DelegateLikeCall, @function_pointer_invocation
* (`EventCall`).
*/
class AccessorCall extends Call, QualifiableExpr, @call_access_expr {
- override Accessor getTarget() { none() }
+ override Accessor getTarget() { result = this.getReadTarget() or result = this.getWriteTarget() }
+
+ /**
+ * Gets the static (compile-time) target of this call, assuming that this is
+ * an `AssignableRead`.
+ *
+ * Note that left-hand sides of compound assignments are both
+ * `AssignableRead`s and `AssignableWrite`s.
+ */
+ Accessor getReadTarget() { none() }
+
+ /**
+ * Gets the static (compile-time) target of this call, assuming that this is
+ * an `AssignableWrite`.
+ *
+ * Note that left-hand sides of compound assignments are both
+ * `AssignableRead`s and `AssignableWrite`s.
+ */
+ Accessor getWriteTarget() { none() }
override Expr getArgument(int i) { none() }
@@ -651,12 +673,12 @@ class AccessorCall extends Call, QualifiableExpr, @call_access_expr {
* ```
*/
class PropertyCall extends AccessorCall, PropertyAccessExpr {
- override Accessor getTarget() {
- exists(PropertyAccess pa, Property p | pa = this and p = this.getProperty() |
- pa instanceof AssignableRead and result = p.getGetter()
- or
- pa instanceof AssignableWrite and result = p.getSetter()
- )
+ override Accessor getReadTarget() {
+ this instanceof AssignableRead and result = this.getProperty().getGetter()
+ }
+
+ override Accessor getWriteTarget() {
+ this instanceof AssignableWrite and result = this.getProperty().getSetter()
}
override Expr getArgument(int i) {
@@ -686,12 +708,12 @@ class PropertyCall extends AccessorCall, PropertyAccessExpr {
* ```
*/
class IndexerCall extends AccessorCall, IndexerAccessExpr {
- override Accessor getTarget() {
- exists(IndexerAccess ia, Indexer i | ia = this and i = this.getIndexer() |
- ia instanceof AssignableRead and result = i.getGetter()
- or
- ia instanceof AssignableWrite and result = i.getSetter()
- )
+ override Accessor getReadTarget() {
+ this instanceof AssignableRead and result = this.getIndexer().getGetter()
+ }
+
+ override Accessor getWriteTarget() {
+ this instanceof AssignableWrite and result = this.getIndexer().getSetter()
}
override Expr getArgument(int i) {
@@ -766,7 +788,7 @@ class ExtensionPropertyCall extends PropertyCall {
* ```
*/
class EventCall extends AccessorCall, EventAccessExpr {
- override EventAccessor getTarget() {
+ override EventAccessor getWriteTarget() {
exists(Event e, AddOrRemoveEventExpr aoree |
e = this.getEvent() and
aoree.getLValue() = this
diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Dynamic.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Dynamic.qll
index 04ea9f062a50..bfc5c36ff37c 100644
--- a/csharp/ql/lib/semmle/code/csharp/exprs/Dynamic.qll
+++ b/csharp/ql/lib/semmle/code/csharp/exprs/Dynamic.qll
@@ -96,13 +96,7 @@ class DynamicMethodCall extends DynamicExpr, MethodCall {
* Unlike an ordinary call to a user-defined operator (`OperatorCall`), the
* target operator may not be known at compile-time (as in the example above).
*/
-class DynamicOperatorCall extends DynamicExpr, OperatorCall {
- override string toString() {
- result = "dynamic call to operator " + this.getLateBoundTargetName()
- }
-
- override string getAPrimaryQlClass() { result = "DynamicOperatorCall" }
-}
+class DynamicOperatorCall extends DynamicExpr, OperatorCall { }
/**
* A call to a user-defined mutator operator where the operand is a `dynamic`
diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll
index c3b9bcc363b3..66764d06479d 100644
--- a/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll
+++ b/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll
@@ -14,6 +14,7 @@ import Creation
import Dynamic
import Literal
import LogicalOperation
+import Operation
import semmle.code.csharp.controlflow.ControlFlowElement
import semmle.code.csharp.Location
import semmle.code.csharp.Stmt
@@ -65,25 +66,11 @@ 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.isExpandedAssignmentRValueDescendant()
- }
+ final predicate isImplicit() { compiler_generated(this) }
/**
* Gets an expression that is the result of stripping (recursively) all
@@ -168,7 +155,7 @@ class LocalVariableDeclExpr extends Expr, @local_var_decl_expr {
string getName() { result = this.getVariable().getName() }
/** Gets the initializer expression of this local variable declaration, if any. */
- Expr getInitializer() { result = this.getChild(0) }
+ Expr getInitializer() { result = this.getChild(1) }
/** Holds if this local variable declaration has an initializer. */
predicate hasInitializer() { exists(this.getInitializer()) }
@@ -188,7 +175,7 @@ class LocalVariableDeclExpr extends Expr, @local_var_decl_expr {
/** Gets the variable access used in this declaration, if any. */
LocalVariableAccess getAccess() {
- result = this.getChild(1) or
+ result = this.getChild(0) or
result = this // `out` argument
}
diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/LogicalOperation.qll b/csharp/ql/lib/semmle/code/csharp/exprs/LogicalOperation.qll
index e94d8ff93e7a..4161f734c9b7 100644
--- a/csharp/ql/lib/semmle/code/csharp/exprs/LogicalOperation.qll
+++ b/csharp/ql/lib/semmle/code/csharp/exprs/LogicalOperation.qll
@@ -65,7 +65,9 @@ class LogicalOrExpr extends BinaryLogicalOperation, @log_or_expr {
* }
* ```
*/
-class NullCoalescingExpr extends BinaryLogicalOperation, @null_coalescing_expr {
+class NullCoalescingExpr extends BinaryLogicalOperation, NullCoalescingOperation,
+ @null_coalescing_expr
+{
override string getOperator() { result = "??" }
override string getAPrimaryQlClass() { result = "NullCoalescingExpr" }
diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Operation.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Operation.qll
new file mode 100644
index 000000000000..1f816baea868
--- /dev/null
+++ b/csharp/ql/lib/semmle/code/csharp/exprs/Operation.qll
@@ -0,0 +1,71 @@
+/**
+ * Provides classes for operations that also have compound assignment forms.
+ */
+
+import Expr
+
+/**
+ * An addition operation, either `x + y` or `x += y`.
+ */
+class AddOperation extends BinaryOperation, @add_operation { }
+
+/**
+ * A subtraction operation, either `x - y` or `x -= y`.
+ */
+class SubOperation extends BinaryOperation, @sub_operation { }
+
+/**
+ * A multiplication operation, either `x * y` or `x *= y`.
+ */
+class MulOperation extends BinaryOperation, @mul_operation { }
+
+/**
+ * A division operation, either `x / y` or `x /= y`.
+ */
+class DivOperation extends BinaryOperation, @div_operation {
+ /** Gets the numerator of this division operation. */
+ Expr getNumerator() { result = this.getLeftOperand() }
+
+ /** Gets the denominator of this division operation. */
+ Expr getDenominator() { result = this.getRightOperand() }
+}
+
+/**
+ * A remainder operation, either `x % y` or `x %= y`.
+ */
+class RemOperation extends BinaryOperation, @rem_operation { }
+
+/**
+ * A bitwise-and operation, either `x & y` or `x &= y`.
+ */
+class BitwiseAndOperation extends BinaryOperation, @and_operation { }
+
+/**
+ * A bitwise-or operation, either `x | y` or `x |= y`.
+ */
+class BitwiseOrOperation extends BinaryOperation, @or_operation { }
+
+/**
+ * A bitwise exclusive-or operation, either `x ^ y` or `x ^= y`.
+ */
+class BitwiseXorOperation extends BinaryOperation, @xor_operation { }
+
+/**
+ * A left-shift operation, either `x << y` or `x <<= y`.
+ */
+class LeftShiftOperation extends BinaryOperation, @lshift_operation { }
+
+/**
+ * A right-shift operation, either `x >> y` or `x >>= y`.
+ */
+class RightShiftOperation extends BinaryOperation, @rshift_operation { }
+
+/**
+ * An unsigned right-shift operation, either `x >>> y` or `x >>>= y`.
+ */
+class UnsignedRightShiftOperation extends BinaryOperation, @urshift_operation { }
+
+/**
+ * A null-coalescing operation, either `x ?? y` or `x ??= y`.
+ */
+class NullCoalescingOperation extends BinaryOperation, @null_coalescing_operation { }
diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/system/Xml.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/system/Xml.qll
index c0edf9e110e1..798a8ed38222 100644
--- a/csharp/ql/lib/semmle/code/csharp/frameworks/system/Xml.qll
+++ b/csharp/ql/lib/semmle/code/csharp/frameworks/system/Xml.qll
@@ -131,7 +131,7 @@ class SystemXmlSchemaXmlSchemaValidationFlags extends EnumConstant {
}
}
-private Expr getBitwiseOrOperand(Expr e) { result = e.(BitwiseOrExpr).getAnOperand() }
+private Expr getBitwiseOrOperand(Expr e) { result = e.(BitwiseOrOperation).getAnOperand() }
/** A creation of an instance of `System.Xml.XmlReaderSettings`. */
class XmlReaderSettingsCreation extends ObjectCreation {
diff --git a/csharp/ql/lib/semmle/code/csharp/metrics/Complexity.qll b/csharp/ql/lib/semmle/code/csharp/metrics/Complexity.qll
index 2f37d23fe49f..392f13d118ee 100644
--- a/csharp/ql/lib/semmle/code/csharp/metrics/Complexity.qll
+++ b/csharp/ql/lib/semmle/code/csharp/metrics/Complexity.qll
@@ -32,7 +32,7 @@ private predicate branchingExpr(Expr expr) {
expr instanceof ConditionalExpr or
expr instanceof LogicalAndExpr or
expr instanceof LogicalOrExpr or
- expr instanceof NullCoalescingExpr
+ expr instanceof NullCoalescingOperation
}
/**
diff --git a/csharp/ql/lib/semmle/code/csharp/security/dataflow/ConditionalBypassQuery.qll b/csharp/ql/lib/semmle/code/csharp/security/dataflow/ConditionalBypassQuery.qll
index 53b44f873a64..a9ad31dc804c 100644
--- a/csharp/ql/lib/semmle/code/csharp/security/dataflow/ConditionalBypassQuery.qll
+++ b/csharp/ql/lib/semmle/code/csharp/security/dataflow/ConditionalBypassQuery.qll
@@ -5,7 +5,6 @@
import csharp
private import semmle.code.csharp.controlflow.Guards
-private import semmle.code.csharp.controlflow.BasicBlocks
private import semmle.code.csharp.security.dataflow.flowsinks.FlowSinks
private import semmle.code.csharp.security.dataflow.flowsources.FlowSources
private import semmle.code.csharp.frameworks.System
diff --git a/csharp/ql/lib/semmlecode.csharp.dbscheme b/csharp/ql/lib/semmlecode.csharp.dbscheme
index e73ca2c93df8..19b8cc3e2dc7 100644
--- a/csharp/ql/lib/semmlecode.csharp.dbscheme
+++ b/csharp/ql/lib/semmlecode.csharp.dbscheme
@@ -1216,9 +1216,23 @@ case @expr.kind of
| @string_literal_expr | @null_literal_expr;
@assign_expr = @simple_assign_expr | @assign_op_expr | @local_var_decl_expr;
-@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr | @assign_event_expr | @assign_coalesce_expr;
+@assign_op_call_expr = @assign_arith_expr | @assign_bitwise_expr
+@assign_op_expr = @assign_op_call_expr | @assign_event_expr | @assign_coalesce_expr;
@assign_event_expr = @add_event_expr | @remove_event_expr;
+@add_operation = @add_expr | @assign_add_expr;
+@sub_operation = @sub_expr | @assign_sub_expr;
+@mul_operation = @mul_expr | @assign_mul_expr;
+@div_operation = @div_expr | @assign_div_expr;
+@rem_operation = @rem_expr | @assign_rem_expr;
+@and_operation = @bit_and_expr | @assign_and_expr;
+@xor_operation = @bit_xor_expr | @assign_xor_expr;
+@or_operation = @bit_or_expr | @assign_or_expr;
+@lshift_operation = @lshift_expr | @assign_lshift_expr;
+@rshift_operation = @rshift_expr | @assign_rshift_expr;
+@urshift_operation = @urshift_expr | @assign_urshift_expr;
+@null_coalescing_operation = @null_coalescing_expr | @assign_coalesce_expr;
+
@assign_arith_expr = @assign_add_expr | @assign_sub_expr | @assign_mul_expr | @assign_div_expr
| @assign_rem_expr
@assign_bitwise_expr = @assign_and_expr | @assign_or_expr | @assign_xor_expr
@@ -1270,14 +1284,15 @@ case @expr.kind of
@anonymous_function_expr = @lambda_expr | @anonymous_method_expr;
-@call = @method_invocation_expr | @constructor_init_expr | @operator_invocation_expr
+@op_invoke_expr = @operator_invocation_expr | @assign_op_call_expr
+@call = @method_invocation_expr | @constructor_init_expr | @op_invoke_expr
| @delegate_invocation_expr | @object_creation_expr | @call_access_expr
| @local_function_invocation_expr | @function_pointer_invocation_expr;
@call_access_expr = @property_access_expr | @event_access_expr | @indexer_access_expr;
@late_bindable_expr = @dynamic_element_access_expr | @dynamic_member_access_expr
- | @object_creation_expr | @method_invocation_expr | @operator_invocation_expr;
+ | @object_creation_expr | @method_invocation_expr | @op_invoke_expr;
@throw_element = @throw_expr | @throw_stmt;
diff --git a/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql b/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql
index 386b238e049c..7799513c327e 100644
--- a/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql
+++ b/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql
@@ -16,11 +16,10 @@
import csharp
import semmle.code.csharp.commons.Assertions
import semmle.code.csharp.commons.Constants
-import semmle.code.csharp.controlflow.BasicBlocks
import semmle.code.csharp.controlflow.Guards as Guards
import codeql.controlflow.queries.ConstantCondition as ConstCond
-module ConstCondInput implements ConstCond::InputSig {
+module ConstCondInput implements ConstCond::InputSig {
class SsaDefinition = Ssa::Definition;
class GuardValue = Guards::GuardValue;
@@ -43,7 +42,7 @@ module ConstCondImpl = ConstCond::Make;
predicate nullCheck(Expr e, boolean direct) {
exists(QualifiableExpr qe | qe.isConditional() and qe.getQualifier() = e and direct = true)
or
- exists(NullCoalescingExpr nce | nce.getLeftOperand() = e and direct = true)
+ exists(NullCoalescingOperation nce | nce.getLeftOperand() = e and direct = true)
or
exists(ConditionalExpr ce | ce.getThen() = e or ce.getElse() = e |
nullCheck(ce, _) and direct = false
@@ -114,7 +113,7 @@ class ConstantBooleanCondition extends ConstantCondition {
override predicate isWhiteListed() {
// E.g. `x ?? false`
- this.(BoolLiteral) = any(NullCoalescingExpr nce).getRightOperand() or
+ this.(BoolLiteral) = any(NullCoalescingOperation nce).getRightOperand() or
// No need to flag logical operations when the operands are constant
isConstantCondition(this.(LogicalNotExpr).getOperand(), _) or
this =
@@ -166,14 +165,12 @@ class ConstantNullnessCondition extends ConstantCondition {
boolean b;
ConstantNullnessCondition() {
- forex(ControlFlow::Node cfn | cfn = this.getAControlFlowNode() |
- exists(ControlFlow::NullnessSuccessor t, ControlFlow::Node s |
- s = cfn.getASuccessorByType(t)
- |
+ exists(ControlFlowNode cfn | cfn = this.getAControlFlowNode() |
+ exists(ControlFlow::NullnessSuccessor t, ControlFlowNode s | s = cfn.getASuccessor(t) |
b = t.getValue() and
- not s.isJoin()
+ exists(unique(ControlFlowNode pred | s.getAPredecessor() = pred))
) and
- strictcount(ControlFlow::SuccessorType t | exists(cfn.getASuccessorByType(t))) = 1
+ strictcount(ControlFlow::SuccessorType t | exists(cfn.getASuccessor(t))) = 1
)
}
@@ -190,11 +187,9 @@ class ConstantMatchingCondition extends ConstantCondition {
ConstantMatchingCondition() {
this instanceof Expr and
- forex(ControlFlow::Node cfn | cfn = this.getAControlFlowNode() |
- exists(ControlFlow::MatchingSuccessor t | exists(cfn.getASuccessorByType(t)) |
- b = t.getValue()
- ) and
- strictcount(ControlFlow::SuccessorType t | exists(cfn.getASuccessorByType(t))) = 1
+ forex(ControlFlowNode cfn | cfn = this.getAControlFlowNode() |
+ exists(ControlFlow::MatchingSuccessor t | exists(cfn.getASuccessor(t)) | b = t.getValue()) and
+ strictcount(ControlFlow::SuccessorType t | exists(cfn.getASuccessor(t))) = 1
)
}
diff --git a/csharp/ql/src/Complexity/ComplexCondition.ql b/csharp/ql/src/Complexity/ComplexCondition.ql
index 0afb27e2a945..0a4d37705a37 100644
--- a/csharp/ql/src/Complexity/ComplexCondition.ql
+++ b/csharp/ql/src/Complexity/ComplexCondition.ql
@@ -12,19 +12,38 @@
import csharp
-predicate nontrivialLogicalOperator(BinaryLogicalOperation e) {
- not exists(BinaryLogicalOperation parent |
+abstract class RelevantBinaryOperations extends Operation { }
+
+private class AddBinaryLogicalOperationRelevantBinaryOperations extends RelevantBinaryOperations,
+ BinaryLogicalOperation
+{ }
+
+private class AddAssignCoalesceExprRelevantBinaryOperations extends RelevantBinaryOperations,
+ AssignCoalesceExpr
+{ }
+
+abstract class RelevantOperations extends Operation { }
+
+private class AddLogicalOperationRelevantOperations extends RelevantOperations, LogicalOperation { }
+
+private class AddAssignCoalesceExprRelevantOperations extends RelevantOperations, AssignCoalesceExpr
+{ }
+
+predicate nontrivialLogicalOperator(RelevantBinaryOperations e) {
+ not exists(RelevantBinaryOperations parent |
parent = e.getParent() and
parent.getOperator() = e.getOperator()
)
}
-predicate logicalParent(LogicalOperation op, LogicalOperation parent) { parent = op.getParent() }
+predicate logicalParent(RelevantOperations op, RelevantOperations parent) {
+ parent = op.getParent()
+}
from Expr e, int operators
where
- not e.getParent() instanceof LogicalOperation and
+ not e.getParent() instanceof RelevantOperations and
operators =
- count(BinaryLogicalOperation op | logicalParent*(op, e) and nontrivialLogicalOperator(op)) and
+ count(RelevantBinaryOperations op | logicalParent*(op, e) and nontrivialLogicalOperator(op)) and
operators > 3
select e, "Complex condition: too many logical operations in this expression."
diff --git a/csharp/ql/src/Concurrency/UnsynchronizedStaticAccess.ql b/csharp/ql/src/Concurrency/UnsynchronizedStaticAccess.ql
index 150ae78ae090..ca41f94cf566 100644
--- a/csharp/ql/src/Concurrency/UnsynchronizedStaticAccess.ql
+++ b/csharp/ql/src/Concurrency/UnsynchronizedStaticAccess.ql
@@ -26,11 +26,11 @@ predicate correctlySynchronized(CollectionMember c, Expr access) {
)
}
-ControlFlow::Node unlockedReachable(Callable a) {
+ControlFlowNode unlockedReachable(Callable a) {
result = a.getEntryPoint()
or
- exists(ControlFlow::Node mid | mid = unlockedReachable(a) |
- not mid.getAstNode() instanceof LockingCall and
+ exists(ControlFlowNode mid | mid = unlockedReachable(a) |
+ not mid.asExpr() instanceof LockingCall and
result = mid.getASuccessor()
)
}
diff --git a/csharp/ql/src/Dead Code/DeadStoreOfLocal.ql b/csharp/ql/src/Dead Code/DeadStoreOfLocal.ql
index 0f6e6d11fb2c..59816a18b3fb 100644
--- a/csharp/ql/src/Dead Code/DeadStoreOfLocal.ql
+++ b/csharp/ql/src/Dead Code/DeadStoreOfLocal.ql
@@ -84,6 +84,8 @@ class RelevantDefinition extends AssignableDefinition {
)
or
this instanceof AssignableDefinitions::PatternDefinition
+ or
+ this instanceof AssignableDefinitions::AssignOperationDefinition
}
/** Holds if this assignment may be live. */
diff --git a/csharp/ql/src/Language Abuse/UselessNullCoalescingExpression.ql b/csharp/ql/src/Language Abuse/UselessNullCoalescingExpression.ql
index 7790fc5ba4ab..78e1ea365a5c 100644
--- a/csharp/ql/src/Language Abuse/UselessNullCoalescingExpression.ql
+++ b/csharp/ql/src/Language Abuse/UselessNullCoalescingExpression.ql
@@ -17,20 +17,20 @@ import semmle.code.csharp.commons.StructuralComparison
pragma[noinline]
private predicate same(AssignableAccess x, AssignableAccess y) {
- exists(NullCoalescingExpr nce |
- x = nce.getLeftOperand() and
- y = nce.getRightOperand().getAChildExpr*()
+ exists(NullCoalescingOperation nc |
+ x = nc.getLeftOperand() and
+ y = nc.getRightOperand().getAChildExpr*()
) and
sameGvn(x, y)
}
-private predicate uselessNullCoalescingExpr(NullCoalescingExpr nce) {
+private predicate uselessNullCoalescingOperation(NullCoalescingOperation nce) {
exists(AssignableAccess x |
nce.getLeftOperand() = x and
forex(AssignableAccess y | same(x, y) | y instanceof AssignableRead and not y.isRefArgument())
)
}
-from NullCoalescingExpr nce
-where uselessNullCoalescingExpr(nce)
+from NullCoalescingOperation nce
+where uselessNullCoalescingOperation(nce)
select nce, "Both operands of this null-coalescing expression access the same variable or property."
diff --git a/csharp/ql/src/Likely Bugs/Collections/WriteOnlyContainer.ql b/csharp/ql/src/Likely Bugs/Collections/WriteOnlyContainer.ql
index 5a24a1f5f519..5fcb140e6791 100644
--- a/csharp/ql/src/Likely Bugs/Collections/WriteOnlyContainer.ql
+++ b/csharp/ql/src/Likely Bugs/Collections/WriteOnlyContainer.ql
@@ -23,7 +23,9 @@ where
) and
forex(Access a | a = v.getAnAccess() |
a = any(ModifierMethodCall m).getQualifier() or
- a = any(Assignment ass | ass.getRValue() instanceof ObjectCreation).getLValue()
+ a = any(AssignExpr ass | ass.getRValue() instanceof ObjectCreation).getLValue() or
+ a =
+ any(LocalVariableDeclAndInitExpr ass | ass.getRValue() instanceof ObjectCreation).getLValue()
) and
not v = any(ForeachStmt fs).getVariable() and
not v = any(BindingPatternExpr vpe).getVariableDeclExpr().getVariable() and
diff --git a/csharp/ql/src/Likely Bugs/DangerousNonShortCircuitLogic.ql b/csharp/ql/src/Likely Bugs/DangerousNonShortCircuitLogic.ql
index b980bfba1aea..8c77a6376d6e 100644
--- a/csharp/ql/src/Likely Bugs/DangerousNonShortCircuitLogic.ql
+++ b/csharp/ql/src/Likely Bugs/DangerousNonShortCircuitLogic.ql
@@ -23,7 +23,6 @@ class NonShortCircuit extends BinaryBitwiseOperation {
or
this instanceof BitwiseOrExpr
) and
- not exists(AssignBitwiseOperation abo | abo.getExpandedAssignment().getRValue() = this) and
this.getLeftOperand().getType() instanceof BoolType and
this.getRightOperand().getType() instanceof BoolType
}
diff --git a/csharp/ql/src/Likely Bugs/NestedLoopsSameVariable.ql b/csharp/ql/src/Likely Bugs/NestedLoopsSameVariable.ql
index 0831eb561997..d616c2377c3f 100644
--- a/csharp/ql/src/Likely Bugs/NestedLoopsSameVariable.ql
+++ b/csharp/ql/src/Likely Bugs/NestedLoopsSameVariable.ql
@@ -80,21 +80,20 @@ class NestedForLoopSameVariable extends ForStmt {
}
/** Finds elements inside the outer loop that are no longer guarded by the loop invariant. */
- private ControlFlow::Node getAnUnguardedNode() {
+ private ControlFlowNode getAnUnguardedNode() {
hasChild(this.getOuterForStmt().getBody(), result.getAstNode()) and
(
- result =
- this.getCondition().(ControlFlowElement).getAControlFlowExitNode().getAFalseSuccessor()
+ result.isAfterFalse(this.getCondition())
or
- exists(ControlFlow::Node mid | mid = this.getAnUnguardedNode() |
+ exists(ControlFlowNode mid | mid = this.getAnUnguardedNode() |
mid.getASuccessor() = result and
- not exists(this.getAComparisonTest(result.getAstNode()))
+ not exists(this.getAComparisonTest(result.asExpr()))
)
)
}
private VariableAccess getAnUnguardedAccess() {
- result = this.getAnUnguardedNode().getAstNode() and
+ result = this.getAnUnguardedNode().asExpr() and
result.getTarget() = iteration
}
}
diff --git a/csharp/ql/src/Likely Bugs/PossibleLossOfPrecision.ql b/csharp/ql/src/Likely Bugs/PossibleLossOfPrecision.ql
index c66bbbeedbdd..8b719bb92a57 100644
--- a/csharp/ql/src/Likely Bugs/PossibleLossOfPrecision.ql
+++ b/csharp/ql/src/Likely Bugs/PossibleLossOfPrecision.ql
@@ -27,13 +27,13 @@ predicate convertedToFloatOrDecimal(Expr e, Type t) {
t instanceof DecimalType
)
or
- exists(BinaryArithmeticOperation op |
+ exists(BinaryOperation op |
op.getAnOperand() = e and
convertedToFloatOrDecimal(op, t)
|
- op instanceof AddExpr or
- op instanceof SubExpr or
- op instanceof MulExpr
+ op instanceof AddOperation or
+ op instanceof SubOperation or
+ op instanceof MulOperation
)
}
diff --git a/csharp/ql/src/Likely Bugs/Statements/UseBraces.ql b/csharp/ql/src/Likely Bugs/Statements/UseBraces.ql
index f639b060ac17..bef3fabd296d 100644
--- a/csharp/ql/src/Likely Bugs/Statements/UseBraces.ql
+++ b/csharp/ql/src/Likely Bugs/Statements/UseBraces.ql
@@ -15,10 +15,10 @@
import csharp
// Iterate the control flow until we reach a Stmt
-Stmt findSuccessorStmt(ControlFlow::Node n) {
- result = n.getAstNode()
+Stmt findSuccessorStmt(ControlFlowNode n) {
+ result = n.asStmt()
or
- not n.getAstNode() instanceof Stmt and result = findSuccessorStmt(n.getASuccessor())
+ not exists(n.asStmt()) and result = findSuccessorStmt(n.getASuccessor())
}
// Return a successor statement to s
diff --git a/csharp/ql/src/Likely Bugs/UncheckedCastInEquals.ql b/csharp/ql/src/Likely Bugs/UncheckedCastInEquals.ql
index 5c11a77f30df..f86b97ef3023 100644
--- a/csharp/ql/src/Likely Bugs/UncheckedCastInEquals.ql
+++ b/csharp/ql/src/Likely Bugs/UncheckedCastInEquals.ql
@@ -13,20 +13,14 @@
import csharp
import semmle.code.csharp.frameworks.System
-private predicate equalsMethodChild(EqualsMethod equals, Element child) {
- child = equals
+predicate nodeBeforeParameterAccess(ControlFlowNode node) {
+ exists(EqualsMethod equals | equals.getBody().getControlFlowNode() = node)
or
- equalsMethodChild(equals, child.getParent())
-}
-
-predicate nodeBeforeParameterAccess(ControlFlow::Node node) {
- exists(EqualsMethod equals | equals.getBody() = node.getAstNode())
- or
- exists(EqualsMethod equals, Parameter param, ControlFlow::Node mid |
+ exists(EqualsMethod equals, Parameter param, ControlFlowNode mid |
equals.getParameter(0) = param and
- equalsMethodChild(equals, mid.getAstNode()) and
+ equals = mid.getEnclosingCallable() and
nodeBeforeParameterAccess(mid) and
- not param.getAnAccess() = mid.getAstNode() and
+ not param.getAnAccess().getControlFlowNode() = mid and
mid.getASuccessor() = node
)
}
diff --git a/csharp/ql/src/Performance/StringBuilderInLoop.ql b/csharp/ql/src/Performance/StringBuilderInLoop.ql
index f1f23ebf5e0d..f2a13923478c 100644
--- a/csharp/ql/src/Performance/StringBuilderInLoop.ql
+++ b/csharp/ql/src/Performance/StringBuilderInLoop.ql
@@ -13,10 +13,10 @@
import csharp
import semmle.code.csharp.frameworks.system.Text
-from ObjectCreation creation, LoopStmt loop, ControlFlow::Node loopEntryNode
+from ObjectCreation creation, LoopStmt loop, ControlFlowNode loopEntryNode
where
creation.getType() instanceof SystemTextStringBuilderClass and
- loopEntryNode = loop.getBody().getAControlFlowEntryNode() and
+ loopEntryNode.isBefore(loop.getBody()) and
loop.getBody().getAChild*() = creation and
creation.getAControlFlowNode().postDominates(loopEntryNode)
select creation, "Creating a 'StringBuilder' in a loop."
diff --git a/csharp/ql/src/Performance/StringConcatenationInLoop.ql b/csharp/ql/src/Performance/StringConcatenationInLoop.ql
index a015771610d5..3b3228588a4b 100644
--- a/csharp/ql/src/Performance/StringConcatenationInLoop.ql
+++ b/csharp/ql/src/Performance/StringConcatenationInLoop.ql
@@ -23,7 +23,6 @@ class StringCat extends AddExpr {
* where `v` is a simple variable (and not, for example, a property).
*/
predicate isSelfConcatAssignExpr(AssignExpr e, Variable v) {
- not e = any(AssignAddExpr a).getExpandedAssignment() and
exists(VariableAccess use |
stringCatContains(e.getRValue(), use) and
use.getTarget() = e.getTargetVariable() and
diff --git a/csharp/ql/src/Security Features/CWE-119/LocalUnvalidatedArithmetic.ql b/csharp/ql/src/Security Features/CWE-119/LocalUnvalidatedArithmetic.ql
index 57d6e500134a..d5efb1304aff 100644
--- a/csharp/ql/src/Security Features/CWE-119/LocalUnvalidatedArithmetic.ql
+++ b/csharp/ql/src/Security Features/CWE-119/LocalUnvalidatedArithmetic.ql
@@ -18,7 +18,7 @@
import csharp
import semmle.code.csharp.controlflow.Guards
-from AddExpr add, VirtualMethodCall taintSrc
+from AddOperation add, VirtualMethodCall taintSrc
where
// `add` is performing pointer arithmetic
add.getType() instanceof PointerType and
diff --git a/csharp/ql/src/Security Features/CWE-384/AbandonSession.ql b/csharp/ql/src/Security Features/CWE-384/AbandonSession.ql
index c350c8f37554..2654b48c233f 100644
--- a/csharp/ql/src/Security Features/CWE-384/AbandonSession.ql
+++ b/csharp/ql/src/Security Features/CWE-384/AbandonSession.ql
@@ -56,18 +56,18 @@ predicate sessionUse(MemberAccess ma) {
}
/** A control flow step that is not sanitised by a call to clear the session. */
-predicate controlStep(ControlFlow::Node s1, ControlFlow::Node s2) {
+predicate controlStep(ControlFlowNode s1, ControlFlowNode s2) {
s2 = s1.getASuccessor() and
- not sessionEndMethod(s2.getAstNode().(MethodCall).getTarget())
+ not sessionEndMethod(s2.asExpr().(MethodCall).getTarget())
}
from
- ControlFlow::Node loginCall, Method loginMethod, ControlFlow::Node sessionUse,
+ ControlFlowNode loginCall, Method loginMethod, ControlFlowNode sessionUse,
ControlFlow::SuccessorType fromLoginFlow
where
- loginMethod = loginCall.getAstNode().(MethodCall).getTarget() and
+ loginMethod = loginCall.asExpr().(MethodCall).getTarget() and
loginMethod(loginMethod, fromLoginFlow) and
- sessionUse(sessionUse.getAstNode()) and
- controlStep+(loginCall.getASuccessorByType(fromLoginFlow), sessionUse)
+ sessionUse(sessionUse.asExpr()) and
+ controlStep+(loginCall.getASuccessor(fromLoginFlow), sessionUse)
select sessionUse, "This session has not been invalidated following the call to $@.", loginCall,
loginMethod.getName()
diff --git a/csharp/ql/src/Security Features/InsecureRandomness.ql b/csharp/ql/src/Security Features/InsecureRandomness.ql
index 2c2df7010c67..8237afdff130 100644
--- a/csharp/ql/src/Security Features/InsecureRandomness.ql
+++ b/csharp/ql/src/Security Features/InsecureRandomness.ql
@@ -89,11 +89,7 @@ module Random {
e = any(SensitiveLibraryParameter v).getAnAssignedArgument()
or
// Assignment operation, e.g. += or similar
- exists(AssignOperation ao |
- ao.getRValue() = e and
- // "expanded" assignments will be covered by simple assignment
- not ao.hasExpandedAssignment()
- |
+ exists(AssignOperation ao | ao.getRValue() = e |
ao.getLValue() = any(SensitiveVariable v).getAnAccess() or
ao.getLValue() = any(SensitiveProperty v).getAnAccess() or
ao.getLValue() = any(SensitiveLibraryParameter v).getAnAccess()
diff --git a/csharp/ql/src/experimental/CWE-918/RequestForgery.qll b/csharp/ql/src/experimental/CWE-918/RequestForgery.qll
index 84ea534a50fa..12af7fd7d333 100644
--- a/csharp/ql/src/experimental/CWE-918/RequestForgery.qll
+++ b/csharp/ql/src/experimental/CWE-918/RequestForgery.qll
@@ -211,7 +211,7 @@ module RequestForgery {
}
private predicate stringConcatStep(DataFlow::Node prev, DataFlow::Node succ) {
- exists(AddExpr a |
+ exists(AddOperation a |
a.getLeftOperand() = prev.asExpr()
or
a.getRightOperand() = prev.asExpr() and
diff --git a/csharp/ql/src/experimental/Security Features/CWE-759/HashWithoutSalt.ql b/csharp/ql/src/experimental/Security Features/CWE-759/HashWithoutSalt.ql
index f175723c0997..d43050c2deb4 100644
--- a/csharp/ql/src/experimental/Security Features/CWE-759/HashWithoutSalt.ql
+++ b/csharp/ql/src/experimental/Security Features/CWE-759/HashWithoutSalt.ql
@@ -174,7 +174,7 @@ module HashWithoutSaltConfig implements DataFlow::ConfigSig {
mc.getAnArgument() = node.asExpr()
)
or
- exists(AddExpr e | node.asExpr() = e.getAnOperand()) // password+salt
+ exists(AddOperation e | node.asExpr() = e.getAnOperand()) // password+salt
or
exists(InterpolatedStringExpr e | node.asExpr() = e.getAnInsert())
or
diff --git a/csharp/ql/test/library-tests/arguments/argumentByName.expected b/csharp/ql/test/library-tests/arguments/argumentByName.expected
index 6fcb9021d17d..065d70703120 100644
--- a/csharp/ql/test/library-tests/arguments/argumentByName.expected
+++ b/csharp/ql/test/library-tests/arguments/argumentByName.expected
@@ -33,14 +33,16 @@
| arguments.cs:59:16:59:25 | access to indexer | arguments.cs:59:21:59:21 | 3 | a |
| arguments.cs:59:16:59:25 | access to indexer | arguments.cs:59:24:59:24 | 4 | b |
| arguments.cs:59:16:59:25 | access to indexer | arguments.cs:59:34:59:34 | 6 | value |
-| arguments.cs:61:9:61:12 | access to property Prop | arguments.cs:61:9:61:17 | ... + ... | value |
+| arguments.cs:61:9:61:12 | access to property Prop | arguments.cs:61:9:61:17 | ... += ... | value |
+| arguments.cs:61:9:61:17 | ... += ... | arguments.cs:61:9:61:12 | access to property Prop | left |
+| arguments.cs:61:9:61:17 | ... += ... | arguments.cs:61:17:61:17 | 7 | right |
| arguments.cs:62:9:62:18 | access to indexer | arguments.cs:62:14:62:14 | 8 | a |
| arguments.cs:62:9:62:18 | access to indexer | arguments.cs:62:17:62:17 | 9 | b |
-| arguments.cs:63:9:63:20 | access to indexer | arguments.cs:63:9:63:26 | ... + ... | value |
+| arguments.cs:63:9:63:20 | access to indexer | arguments.cs:63:9:63:26 | ... += ... | value |
| arguments.cs:63:9:63:20 | access to indexer | arguments.cs:63:14:63:15 | 10 | a |
-| arguments.cs:63:9:63:20 | access to indexer | arguments.cs:63:14:63:15 | 10 | a |
-| arguments.cs:63:9:63:20 | access to indexer | arguments.cs:63:18:63:19 | 11 | b |
| arguments.cs:63:9:63:20 | access to indexer | arguments.cs:63:18:63:19 | 11 | b |
+| arguments.cs:63:9:63:26 | ... += ... | arguments.cs:63:9:63:20 | access to indexer | left |
+| arguments.cs:63:9:63:26 | ... += ... | arguments.cs:63:25:63:26 | 12 | right |
| arguments.cs:65:16:65:27 | access to indexer | arguments.cs:65:21:65:22 | 15 | a |
| arguments.cs:65:16:65:27 | access to indexer | arguments.cs:65:25:65:26 | 16 | b |
| arguments.cs:76:9:76:31 | call to method f8`1 | arguments.cs:76:12:76:12 | 0 | o |
diff --git a/csharp/ql/test/library-tests/arguments/argumentByParameter.expected b/csharp/ql/test/library-tests/arguments/argumentByParameter.expected
index ac354d31e28e..c04658592532 100644
--- a/csharp/ql/test/library-tests/arguments/argumentByParameter.expected
+++ b/csharp/ql/test/library-tests/arguments/argumentByParameter.expected
@@ -33,12 +33,12 @@
| arguments.cs:59:16:59:25 | access to indexer | arguments.cs:59:21:59:21 | 3 | arguments.cs:53:18:53:18 | a |
| arguments.cs:59:16:59:25 | access to indexer | arguments.cs:59:24:59:24 | 4 | arguments.cs:53:25:53:25 | b |
| arguments.cs:59:16:59:25 | access to indexer | arguments.cs:59:34:59:34 | 6 | arguments.cs:53:44:53:46 | value |
-| arguments.cs:61:9:61:12 | access to property Prop | arguments.cs:61:9:61:17 | ... + ... | arguments.cs:51:21:51:23 | value |
+| arguments.cs:61:9:61:12 | access to property Prop | arguments.cs:61:9:61:17 | ... += ... | arguments.cs:51:21:51:23 | value |
| arguments.cs:62:9:62:18 | access to indexer | arguments.cs:62:14:62:14 | 8 | arguments.cs:53:18:53:18 | a |
| arguments.cs:62:9:62:18 | access to indexer | arguments.cs:62:14:62:14 | 8 | arguments.cs:53:18:53:18 | a |
| arguments.cs:62:9:62:18 | access to indexer | arguments.cs:62:17:62:17 | 9 | arguments.cs:53:25:53:25 | b |
| arguments.cs:62:9:62:18 | access to indexer | arguments.cs:62:17:62:17 | 9 | arguments.cs:53:25:53:25 | b |
-| arguments.cs:63:9:63:20 | access to indexer | arguments.cs:63:9:63:26 | ... + ... | arguments.cs:53:44:53:46 | value |
+| arguments.cs:63:9:63:20 | access to indexer | arguments.cs:63:9:63:26 | ... += ... | arguments.cs:53:44:53:46 | value |
| arguments.cs:63:9:63:20 | access to indexer | arguments.cs:63:14:63:15 | 10 | arguments.cs:53:18:53:18 | a |
| arguments.cs:63:9:63:20 | access to indexer | arguments.cs:63:14:63:15 | 10 | arguments.cs:53:18:53:18 | a |
| arguments.cs:63:9:63:20 | access to indexer | arguments.cs:63:18:63:19 | 11 | arguments.cs:53:25:53:25 | b |
diff --git a/csharp/ql/test/library-tests/arguments/argumentType.expected b/csharp/ql/test/library-tests/arguments/argumentType.expected
index 0dac70193453..fa148e657b44 100644
--- a/csharp/ql/test/library-tests/arguments/argumentType.expected
+++ b/csharp/ql/test/library-tests/arguments/argumentType.expected
@@ -36,8 +36,6 @@
| arguments.cs:62:14:62:14 | 8 | 0 |
| arguments.cs:62:17:62:17 | 9 | 0 |
| arguments.cs:63:14:63:15 | 10 | 0 |
-| arguments.cs:63:14:63:15 | 10 | 0 |
-| arguments.cs:63:18:63:19 | 11 | 0 |
| arguments.cs:63:18:63:19 | 11 | 0 |
| arguments.cs:64:22:64:23 | 13 | 0 |
| arguments.cs:64:26:64:27 | 14 | 0 |
diff --git a/csharp/ql/test/library-tests/arguments/parameterGetArguments.expected b/csharp/ql/test/library-tests/arguments/parameterGetArguments.expected
index 6f25b07e2e5a..9f830954efb8 100644
--- a/csharp/ql/test/library-tests/arguments/parameterGetArguments.expected
+++ b/csharp/ql/test/library-tests/arguments/parameterGetArguments.expected
@@ -28,7 +28,7 @@
| arguments.cs:51:21:51:23 | value | arguments.cs:57:16:57:16 | 0 |
| arguments.cs:51:21:51:23 | value | arguments.cs:58:16:58:25 | access to indexer |
| arguments.cs:51:21:51:23 | value | arguments.cs:59:31:59:31 | 5 |
-| arguments.cs:51:21:51:23 | value | arguments.cs:61:9:61:17 | ... + ... |
+| arguments.cs:51:21:51:23 | value | arguments.cs:61:9:61:17 | ... += ... |
| arguments.cs:53:18:53:18 | a | arguments.cs:58:21:58:21 | 1 |
| arguments.cs:53:18:53:18 | a | arguments.cs:59:21:59:21 | 3 |
| arguments.cs:53:18:53:18 | a | arguments.cs:62:14:62:14 | 8 |
@@ -44,7 +44,7 @@
| arguments.cs:53:25:53:25 | b | arguments.cs:63:18:63:19 | 11 |
| arguments.cs:53:25:53:25 | b | arguments.cs:65:25:65:26 | 16 |
| arguments.cs:53:44:53:46 | value | arguments.cs:59:34:59:34 | 6 |
-| arguments.cs:53:44:53:46 | value | arguments.cs:63:9:63:26 | ... + ... |
+| arguments.cs:53:44:53:46 | value | arguments.cs:63:9:63:26 | ... += ... |
| arguments.cs:74:20:74:20 | o | arguments.cs:76:12:76:12 | 0 |
| arguments.cs:74:20:74:20 | o | arguments.cs:77:12:77:12 | 0 |
| arguments.cs:74:20:74:20 | o | arguments.cs:78:12:78:12 | 0 |
diff --git a/csharp/ql/test/library-tests/assignments/AssignOperationExpanded.expected b/csharp/ql/test/library-tests/assignments/AssignOperationExpanded.expected
deleted file mode 100644
index bcc9d11c42f5..000000000000
--- a/csharp/ql/test/library-tests/assignments/AssignOperationExpanded.expected
+++ /dev/null
@@ -1,3 +0,0 @@
-| Assignments.cs:6:9:6:14 | ... += ... | Assignments.cs:6:9:6:14 | ... = ... | Assignments.cs:6:9:6:9 | access to local variable x | Assignments.cs:6:9:6:14 | ... + ... | Assignments.cs:6:9:6:9 | access to local variable x | Assignments.cs:6:14:6:14 | 1 |
-| Assignments.cs:9:9:9:14 | ... -= ... | Assignments.cs:9:9:9:14 | ... = ... | Assignments.cs:9:9:9:9 | access to local variable d | Assignments.cs:9:9:9:14 | dynamic call to operator - | Assignments.cs:9:9:9:9 | access to local variable d | Assignments.cs:9:14:9:14 | 2 |
-| Assignments.cs:12:9:12:17 | ... += ... | Assignments.cs:12:9:12:17 | ... = ... | Assignments.cs:12:9:12:9 | access to local variable a | Assignments.cs:12:9:12:17 | call to operator + | Assignments.cs:12:9:12:9 | access to local variable a | Assignments.cs:12:14:12:17 | this access |
diff --git a/csharp/ql/test/library-tests/assignments/AssignOperationExpanded.ql b/csharp/ql/test/library-tests/assignments/AssignOperationExpanded.ql
deleted file mode 100644
index bd67776520fb..000000000000
--- a/csharp/ql/test/library-tests/assignments/AssignOperationExpanded.ql
+++ /dev/null
@@ -1,22 +0,0 @@
-import csharp
-
-predicate getExpandedOperatorArgs(Expr e, Expr left, Expr right) {
- e =
- any(BinaryArithmeticOperation bo |
- bo.getLeftOperand() = left and
- bo.getRightOperand() = right
- )
- or
- e =
- any(OperatorCall oc |
- oc.getArgument(0) = left and
- oc.getArgument(1) = right
- )
-}
-
-from AssignOperation ao, AssignExpr ae, Expr op, Expr left, Expr right
-where
- ae = ao.getExpandedAssignment() and
- op = ae.getRValue() and
- getExpandedOperatorArgs(op, left, right)
-select ao, ae, ae.getLValue(), op, left, right
diff --git a/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected b/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected
index 30f2b1051551..9802f2b01955 100644
--- a/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected
+++ b/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected
@@ -7,11 +7,11 @@
| AccessorCalls.cs:19:10:19:11 | enter M2 | AccessorCalls.cs:19:10:19:11 | exit M2 | 42 |
| AccessorCalls.cs:28:10:28:11 | enter M3 | AccessorCalls.cs:28:10:28:11 | exit M3 | 17 |
| AccessorCalls.cs:35:10:35:11 | enter M4 | AccessorCalls.cs:35:10:35:11 | exit M4 | 20 |
-| AccessorCalls.cs:42:10:42:11 | enter M5 | AccessorCalls.cs:42:10:42:11 | exit M5 | 34 |
-| AccessorCalls.cs:49:10:49:11 | enter M6 | AccessorCalls.cs:49:10:49:11 | exit M6 | 43 |
+| AccessorCalls.cs:42:10:42:11 | enter M5 | AccessorCalls.cs:42:10:42:11 | exit M5 | 24 |
+| AccessorCalls.cs:49:10:49:11 | enter M6 | AccessorCalls.cs:49:10:49:11 | exit M6 | 30 |
| AccessorCalls.cs:56:10:56:11 | enter M7 | AccessorCalls.cs:56:10:56:11 | exit M7 | 25 |
| AccessorCalls.cs:61:10:61:11 | enter M8 | AccessorCalls.cs:61:10:61:11 | exit M8 | 31 |
-| AccessorCalls.cs:66:10:66:11 | enter M9 | AccessorCalls.cs:66:10:66:11 | exit M9 | 58 |
+| AccessorCalls.cs:66:10:66:11 | enter M9 | AccessorCalls.cs:66:10:66:11 | exit M9 | 51 |
| ArrayCreation.cs:1:7:1:19 | enter ArrayCreation | ArrayCreation.cs:1:7:1:19 | exit ArrayCreation | 7 |
| ArrayCreation.cs:3:11:3:12 | enter M1 | ArrayCreation.cs:3:11:3:12 | exit M1 | 5 |
| ArrayCreation.cs:5:12:5:13 | enter M2 | ArrayCreation.cs:5:12:5:13 | exit M2 | 6 |
@@ -164,7 +164,7 @@
| Assert.cs:138:10:138:12 | exit M13 (abnormal) | Assert.cs:138:10:138:12 | exit M13 (abnormal) | 1 |
| Assert.cs:141:9:141:15 | return ...; | Assert.cs:138:10:138:12 | exit M13 (normal) | 2 |
| Assignments.cs:1:7:1:17 | enter Assignments | Assignments.cs:1:7:1:17 | exit Assignments | 7 |
-| Assignments.cs:3:10:3:10 | enter M | Assignments.cs:3:10:3:10 | exit M | 34 |
+| Assignments.cs:3:10:3:10 | enter M | Assignments.cs:3:10:3:10 | exit M | 31 |
| Assignments.cs:14:18:14:35 | enter (...) => ... | Assignments.cs:14:18:14:35 | exit (...) => ... | 4 |
| Assignments.cs:17:40:17:40 | enter + | Assignments.cs:17:40:17:40 | exit + | 6 |
| Assignments.cs:27:10:27:23 | enter SetParamSingle | Assignments.cs:27:10:27:23 | exit SetParamSingle | 7 |
@@ -250,7 +250,6 @@
| ConditionalAccess.cs:42:9:42:11 | enter get_Item | ConditionalAccess.cs:42:9:42:11 | exit get_Item | 6 |
| ConditionalAccess.cs:43:9:43:11 | enter set_Item | ConditionalAccess.cs:43:9:43:11 | exit set_Item | 4 |
| ConditionalAccess.cs:46:10:46:11 | enter M9 | ConditionalAccess.cs:48:9:48:10 | access to parameter ca | 4 |
-| ConditionalAccess.cs:46:10:46:11 | exit M9 (normal) | ConditionalAccess.cs:46:10:46:11 | exit M9 | 2 |
| ConditionalAccess.cs:48:24:48:25 | 42 | ConditionalAccess.cs:48:12:48:25 | ... = ... | 3 |
| ConditionalAccess.cs:49:9:49:33 | ...; | ConditionalAccess.cs:49:9:49:10 | access to parameter ca | 2 |
| ConditionalAccess.cs:49:26:49:32 | "Hello" | ConditionalAccess.cs:49:12:49:32 | ... = ... | 3 |
@@ -262,14 +261,11 @@
| ConditionalAccess.cs:52:9:52:16 | access to property Prop | ConditionalAccess.cs:52:9:52:16 | access to property Prop | 1 |
| ConditionalAccess.cs:52:9:52:39 | ...; | ConditionalAccess.cs:52:9:52:10 | access to parameter ca | 2 |
| ConditionalAccess.cs:52:32:52:38 | "World" | ConditionalAccess.cs:52:18:52:38 | ... = ... | 3 |
-| ConditionalAccess.cs:53:9:53:10 | access to parameter ca | ConditionalAccess.cs:53:9:53:10 | access to parameter ca | 1 |
| ConditionalAccess.cs:53:9:53:20 | access to field IntField | ConditionalAccess.cs:53:9:53:20 | access to field IntField | 1 |
| ConditionalAccess.cs:53:9:53:26 | ...; | ConditionalAccess.cs:53:9:53:10 | access to parameter ca | 2 |
-| ConditionalAccess.cs:53:25:53:25 | 1 | ConditionalAccess.cs:53:12:53:25 | ... = ... | 4 |
-| ConditionalAccess.cs:54:9:54:10 | access to parameter ca | ConditionalAccess.cs:54:9:54:10 | access to parameter ca | 1 |
+| ConditionalAccess.cs:53:25:53:25 | 1 | ConditionalAccess.cs:54:9:54:10 | access to parameter ca | 4 |
| ConditionalAccess.cs:54:9:54:22 | access to property StringProp | ConditionalAccess.cs:54:9:54:22 | access to property StringProp | 1 |
-| ConditionalAccess.cs:54:9:54:30 | ...; | ConditionalAccess.cs:54:9:54:10 | access to parameter ca | 2 |
-| ConditionalAccess.cs:54:27:54:29 | "!" | ConditionalAccess.cs:54:12:54:29 | ... = ... | 4 |
+| ConditionalAccess.cs:54:27:54:29 | "!" | ConditionalAccess.cs:46:10:46:11 | exit M9 | 4 |
| ConditionalAccess.cs:60:26:60:38 | enter CommaJoinWith | ConditionalAccess.cs:60:26:60:38 | exit CommaJoinWith | 8 |
| Conditions.cs:1:7:1:16 | enter Conditions | Conditions.cs:1:7:1:16 | exit Conditions | 7 |
| Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:5:13:5:15 | access to parameter inc | 4 |
@@ -331,12 +327,12 @@
| Conditions.cs:97:17:97:20 | ...; | Conditions.cs:97:17:97:19 | ...++ | 3 |
| Conditions.cs:99:16:99:16 | access to local variable x | Conditions.cs:86:9:86:10 | exit M7 | 4 |
| Conditions.cs:102:12:102:13 | enter M8 | Conditions.cs:105:13:105:13 | access to parameter b | 8 |
-| Conditions.cs:106:13:106:20 | ...; | Conditions.cs:106:13:106:19 | ... = ... | 5 |
+| Conditions.cs:106:13:106:20 | ...; | Conditions.cs:106:13:106:19 | ... += ... | 4 |
| Conditions.cs:107:9:109:24 | if (...) ... | Conditions.cs:107:13:107:24 | ... > ... | 5 |
| Conditions.cs:108:13:109:24 | if (...) ... | Conditions.cs:108:18:108:18 | access to parameter b | 2 |
| Conditions.cs:108:17:108:18 | [false] !... | Conditions.cs:108:17:108:18 | [false] !... | 1 |
| Conditions.cs:108:17:108:18 | [true] !... | Conditions.cs:108:17:108:18 | [true] !... | 1 |
-| Conditions.cs:109:17:109:24 | ...; | Conditions.cs:109:17:109:23 | ... = ... | 5 |
+| Conditions.cs:109:17:109:24 | ...; | Conditions.cs:109:17:109:23 | ... += ... | 4 |
| Conditions.cs:110:16:110:16 | access to local variable x | Conditions.cs:102:12:102:13 | exit M8 | 4 |
| Conditions.cs:113:10:113:11 | enter M9 | Conditions.cs:116:18:116:22 | Int32 i = ... | 8 |
| Conditions.cs:113:10:113:11 | exit M9 (normal) | Conditions.cs:113:10:113:11 | exit M9 | 2 |
@@ -533,7 +529,7 @@
| Finally.cs:254:13:254:45 | ...; | Finally.cs:254:13:254:44 | call to method WriteLine | 3 |
| Finally.cs:257:9:259:9 | {...} | Finally.cs:258:13:258:46 | call to method WriteLine | 4 |
| Finally.cs:260:9:260:34 | ...; | Finally.cs:233:10:233:12 | exit M12 (normal) | 4 |
-| Finally.cs:263:10:263:12 | enter M13 | Finally.cs:272:13:272:18 | ... = ... | 16 |
+| Finally.cs:263:10:263:12 | enter M13 | Finally.cs:272:13:272:18 | ... += ... | 15 |
| Finally.cs:263:10:263:12 | exit M13 | Finally.cs:263:10:263:12 | exit M13 | 1 |
| Finally.cs:263:10:263:12 | exit M13 (abnormal) | Finally.cs:263:10:263:12 | exit M13 (abnormal) | 1 |
| Finally.cs:263:10:263:12 | exit M13 (normal) | Finally.cs:263:10:263:12 | exit M13 (normal) | 1 |
@@ -1130,7 +1126,7 @@
| cflow.cs:201:9:205:9 | {...} | cflow.cs:193:10:193:17 | exit Booleans (abnormal) | 5 |
| cflow.cs:208:10:208:11 | enter Do | cflow.cs:210:9:221:36 | do ... while (...); | 3 |
| cflow.cs:208:10:208:11 | exit Do (normal) | cflow.cs:208:10:208:11 | exit Do | 2 |
-| cflow.cs:211:9:221:9 | {...} | cflow.cs:213:17:213:32 | ... > ... | 15 |
+| cflow.cs:211:9:221:9 | {...} | cflow.cs:213:17:213:32 | ... > ... | 12 |
| cflow.cs:214:13:216:13 | {...} | cflow.cs:215:17:215:25 | continue; | 2 |
| cflow.cs:217:13:220:13 | if (...) ... | cflow.cs:217:17:217:32 | ... < ... | 6 |
| cflow.cs:218:13:220:13 | {...} | cflow.cs:219:17:219:22 | break; | 2 |
@@ -1138,7 +1134,7 @@
| cflow.cs:224:10:224:16 | enter Foreach | cflow.cs:226:27:226:64 | call to method Repeat | 5 |
| cflow.cs:224:10:224:16 | exit Foreach (normal) | cflow.cs:224:10:224:16 | exit Foreach | 2 |
| cflow.cs:226:9:237:9 | foreach (... ... in ...) ... | cflow.cs:226:9:237:9 | foreach (... ... in ...) ... | 1 |
-| cflow.cs:226:22:226:22 | String x | cflow.cs:229:17:229:32 | ... > ... | 16 |
+| cflow.cs:226:22:226:22 | String x | cflow.cs:229:17:229:32 | ... > ... | 13 |
| cflow.cs:230:13:232:13 | {...} | cflow.cs:231:17:231:25 | continue; | 2 |
| cflow.cs:233:13:236:13 | if (...) ... | cflow.cs:233:17:233:32 | ... < ... | 6 |
| cflow.cs:234:13:236:13 | {...} | cflow.cs:235:17:235:22 | break; | 2 |
diff --git a/csharp/ql/test/library-tests/controlflow/graph/Common.qll b/csharp/ql/test/library-tests/controlflow/graph/Common.qll
index f6f9b26f1cdd..0df87ac46701 100644
--- a/csharp/ql/test/library-tests/controlflow/graph/Common.qll
+++ b/csharp/ql/test/library-tests/controlflow/graph/Common.qll
@@ -10,14 +10,14 @@ class SourceControlFlowElement extends ControlFlowElement {
}
}
-class SourceControlFlowNode extends ControlFlow::Node {
+class SourceControlFlowNode extends ControlFlowNode {
SourceControlFlowNode() {
not this.getLocation().getFile() instanceof StubFile and
not this.getLocation().getFile().fromLibrary()
}
}
-class SourceBasicBlock extends ControlFlow::BasicBlock {
+class SourceBasicBlock extends BasicBlock {
SourceBasicBlock() {
not this.getLocation().getFile() instanceof StubFile and
not this.getLocation().getFile().fromLibrary()
diff --git a/csharp/ql/test/library-tests/controlflow/graph/Condition.expected b/csharp/ql/test/library-tests/controlflow/graph/Condition.expected
index 3ef1d481abe8..cda25b6abd12 100644
--- a/csharp/ql/test/library-tests/controlflow/graph/Condition.expected
+++ b/csharp/ql/test/library-tests/controlflow/graph/Condition.expected
@@ -100,14 +100,8 @@ conditionBlock
| ConditionalAccess.cs:52:9:52:16 | access to property Prop | ConditionalAccess.cs:52:32:52:38 | "World" | false |
| ConditionalAccess.cs:52:9:52:39 | ...; | ConditionalAccess.cs:52:9:52:16 | access to property Prop | false |
| ConditionalAccess.cs:52:9:52:39 | ...; | ConditionalAccess.cs:52:32:52:38 | "World" | false |
-| ConditionalAccess.cs:53:9:53:10 | access to parameter ca | ConditionalAccess.cs:53:9:53:20 | access to field IntField | false |
-| ConditionalAccess.cs:53:9:53:26 | ...; | ConditionalAccess.cs:53:9:53:10 | access to parameter ca | false |
| ConditionalAccess.cs:53:9:53:26 | ...; | ConditionalAccess.cs:53:9:53:20 | access to field IntField | false |
-| ConditionalAccess.cs:53:9:53:26 | ...; | ConditionalAccess.cs:53:25:53:25 | 1 | false |
-| ConditionalAccess.cs:54:9:54:10 | access to parameter ca | ConditionalAccess.cs:54:9:54:22 | access to property StringProp | false |
-| ConditionalAccess.cs:54:9:54:30 | ...; | ConditionalAccess.cs:54:9:54:10 | access to parameter ca | false |
-| ConditionalAccess.cs:54:9:54:30 | ...; | ConditionalAccess.cs:54:9:54:22 | access to property StringProp | false |
-| ConditionalAccess.cs:54:9:54:30 | ...; | ConditionalAccess.cs:54:27:54:29 | "!" | false |
+| ConditionalAccess.cs:53:25:53:25 | 1 | ConditionalAccess.cs:54:9:54:22 | access to property StringProp | false |
| Conditions.cs:3:10:3:19 | enter IncrOrDecr | Conditions.cs:6:13:6:16 | ...; | true |
| Conditions.cs:7:9:8:16 | if (...) ... | Conditions.cs:7:13:7:16 | [false] !... | true |
| Conditions.cs:7:9:8:16 | if (...) ... | Conditions.cs:7:13:7:16 | [true] !... | false |
diff --git a/csharp/ql/test/library-tests/controlflow/graph/Condition.ql b/csharp/ql/test/library-tests/controlflow/graph/Condition.ql
index 61c801819246..616c4d1b95f4 100644
--- a/csharp/ql/test/library-tests/controlflow/graph/Condition.ql
+++ b/csharp/ql/test/library-tests/controlflow/graph/Condition.ql
@@ -1,18 +1,14 @@
import csharp
import ControlFlow
-query predicate conditionBlock(
- BasicBlocks::ConditionBlock cb, BasicBlock controlled, boolean testIsTrue
-) {
+query predicate conditionBlock(BasicBlock cb, BasicBlock controlled, boolean testIsTrue) {
cb.edgeDominates(controlled, any(ConditionalSuccessor s | testIsTrue = s.getValue()))
}
-ControlFlow::Node successor(ControlFlow::Node node, boolean kind) {
- kind = true and result = node.getATrueSuccessor()
- or
- kind = false and result = node.getAFalseSuccessor()
+ControlFlowNode successor(ControlFlowNode node, boolean kind) {
+ result = node.getASuccessor(any(BooleanSuccessor s | s.getValue() = kind))
}
-query predicate conditionFlow(ControlFlow::Node node, ControlFlow::Node successor, boolean kind) {
+query predicate conditionFlow(ControlFlowNode node, ControlFlowNode successor, boolean kind) {
successor = successor(node, kind)
}
diff --git a/csharp/ql/test/library-tests/controlflow/graph/ConditionalAccess.cs b/csharp/ql/test/library-tests/controlflow/graph/ConditionalAccess.cs
index b99e940f88b5..f40cfd8c8414 100644
--- a/csharp/ql/test/library-tests/controlflow/graph/ConditionalAccess.cs
+++ b/csharp/ql/test/library-tests/controlflow/graph/ConditionalAccess.cs
@@ -52,6 +52,7 @@ void M9(ConditionalAccess ca)
ca?.Prop?.StringProp = "World";
ca?.IntField -= 1;
ca?.StringProp += "!";
+ ca?[1] ??= "Set1";
}
}
diff --git a/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected b/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected
index 204092c6df23..072d874d8d4f 100644
--- a/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected
+++ b/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected
@@ -129,77 +129,54 @@ dominance
| AccessorCalls.cs:42:10:42:11 | enter M5 | AccessorCalls.cs:43:5:47:5 | {...} |
| AccessorCalls.cs:42:10:42:11 | exit M5 (normal) | AccessorCalls.cs:42:10:42:11 | exit M5 |
| AccessorCalls.cs:43:5:47:5 | {...} | AccessorCalls.cs:44:9:44:33 | ...; |
-| AccessorCalls.cs:44:9:44:12 | this access | AccessorCalls.cs:44:9:44:12 | this access |
| AccessorCalls.cs:44:9:44:12 | this access | AccessorCalls.cs:44:9:44:18 | access to field Field |
-| AccessorCalls.cs:44:9:44:18 | access to field Field | AccessorCalls.cs:44:9:44:32 | ... = ... |
| AccessorCalls.cs:44:9:44:18 | access to field Field | AccessorCalls.cs:44:23:44:26 | this access |
-| AccessorCalls.cs:44:9:44:32 | ... + ... | AccessorCalls.cs:44:9:44:18 | access to field Field |
-| AccessorCalls.cs:44:9:44:32 | ... = ... | AccessorCalls.cs:45:9:45:31 | ...; |
+| AccessorCalls.cs:44:9:44:32 | ... += ... | AccessorCalls.cs:45:9:45:31 | ...; |
| AccessorCalls.cs:44:9:44:33 | ...; | AccessorCalls.cs:44:9:44:12 | this access |
| AccessorCalls.cs:44:23:44:26 | this access | AccessorCalls.cs:44:23:44:32 | access to field Field |
-| AccessorCalls.cs:44:23:44:32 | access to field Field | AccessorCalls.cs:44:9:44:32 | ... + ... |
-| AccessorCalls.cs:45:9:45:12 | this access | AccessorCalls.cs:45:9:45:12 | this access |
+| AccessorCalls.cs:44:23:44:32 | access to field Field | AccessorCalls.cs:44:9:44:32 | ... += ... |
| AccessorCalls.cs:45:9:45:12 | this access | AccessorCalls.cs:45:9:45:17 | access to property Prop |
-| AccessorCalls.cs:45:9:45:17 | access to property Prop | AccessorCalls.cs:45:9:45:30 | ... = ... |
| AccessorCalls.cs:45:9:45:17 | access to property Prop | AccessorCalls.cs:45:22:45:25 | this access |
-| AccessorCalls.cs:45:9:45:30 | ... + ... | AccessorCalls.cs:45:9:45:17 | access to property Prop |
-| AccessorCalls.cs:45:9:45:30 | ... = ... | AccessorCalls.cs:46:9:46:27 | ...; |
+| AccessorCalls.cs:45:9:45:30 | ... += ... | AccessorCalls.cs:46:9:46:27 | ...; |
| AccessorCalls.cs:45:9:45:31 | ...; | AccessorCalls.cs:45:9:45:12 | this access |
| AccessorCalls.cs:45:22:45:25 | this access | AccessorCalls.cs:45:22:45:30 | access to property Prop |
-| AccessorCalls.cs:45:22:45:30 | access to property Prop | AccessorCalls.cs:45:9:45:30 | ... + ... |
+| AccessorCalls.cs:45:22:45:30 | access to property Prop | AccessorCalls.cs:45:9:45:30 | ... += ... |
| AccessorCalls.cs:46:9:46:12 | this access | AccessorCalls.cs:46:14:46:14 | 0 |
-| AccessorCalls.cs:46:9:46:12 | this access | AccessorCalls.cs:46:14:46:14 | 0 |
-| AccessorCalls.cs:46:9:46:15 | access to indexer | AccessorCalls.cs:46:9:46:26 | ... = ... |
| AccessorCalls.cs:46:9:46:15 | access to indexer | AccessorCalls.cs:46:20:46:23 | this access |
-| AccessorCalls.cs:46:9:46:26 | ... + ... | AccessorCalls.cs:46:9:46:15 | access to indexer |
-| AccessorCalls.cs:46:9:46:26 | ... = ... | AccessorCalls.cs:42:10:42:11 | exit M5 (normal) |
+| AccessorCalls.cs:46:9:46:26 | ... += ... | AccessorCalls.cs:42:10:42:11 | exit M5 (normal) |
| AccessorCalls.cs:46:9:46:27 | ...; | AccessorCalls.cs:46:9:46:12 | this access |
-| AccessorCalls.cs:46:14:46:14 | 0 | AccessorCalls.cs:46:9:46:12 | this access |
| AccessorCalls.cs:46:14:46:14 | 0 | AccessorCalls.cs:46:9:46:15 | access to indexer |
| AccessorCalls.cs:46:20:46:23 | this access | AccessorCalls.cs:46:25:46:25 | 0 |
-| AccessorCalls.cs:46:20:46:26 | access to indexer | AccessorCalls.cs:46:9:46:26 | ... + ... |
+| AccessorCalls.cs:46:20:46:26 | access to indexer | AccessorCalls.cs:46:9:46:26 | ... += ... |
| AccessorCalls.cs:46:25:46:25 | 0 | AccessorCalls.cs:46:20:46:26 | access to indexer |
| AccessorCalls.cs:49:10:49:11 | enter M6 | AccessorCalls.cs:50:5:54:5 | {...} |
| AccessorCalls.cs:49:10:49:11 | exit M6 (normal) | AccessorCalls.cs:49:10:49:11 | exit M6 |
| AccessorCalls.cs:50:5:54:5 | {...} | AccessorCalls.cs:51:9:51:37 | ...; |
| AccessorCalls.cs:51:9:51:12 | this access | AccessorCalls.cs:51:9:51:14 | access to field x |
-| AccessorCalls.cs:51:9:51:12 | this access | AccessorCalls.cs:51:9:51:14 | access to field x |
-| AccessorCalls.cs:51:9:51:14 | access to field x | AccessorCalls.cs:51:9:51:12 | this access |
| AccessorCalls.cs:51:9:51:14 | access to field x | AccessorCalls.cs:51:9:51:20 | access to field Field |
-| AccessorCalls.cs:51:9:51:20 | access to field Field | AccessorCalls.cs:51:9:51:36 | ... = ... |
| AccessorCalls.cs:51:9:51:20 | access to field Field | AccessorCalls.cs:51:25:51:28 | this access |
-| AccessorCalls.cs:51:9:51:36 | ... + ... | AccessorCalls.cs:51:9:51:20 | access to field Field |
-| AccessorCalls.cs:51:9:51:36 | ... = ... | AccessorCalls.cs:52:9:52:35 | ...; |
+| AccessorCalls.cs:51:9:51:36 | ... += ... | AccessorCalls.cs:52:9:52:35 | ...; |
| AccessorCalls.cs:51:9:51:37 | ...; | AccessorCalls.cs:51:9:51:12 | this access |
| AccessorCalls.cs:51:25:51:28 | this access | AccessorCalls.cs:51:25:51:30 | access to field x |
| AccessorCalls.cs:51:25:51:30 | access to field x | AccessorCalls.cs:51:25:51:36 | access to field Field |
-| AccessorCalls.cs:51:25:51:36 | access to field Field | AccessorCalls.cs:51:9:51:36 | ... + ... |
+| AccessorCalls.cs:51:25:51:36 | access to field Field | AccessorCalls.cs:51:9:51:36 | ... += ... |
| AccessorCalls.cs:52:9:52:12 | this access | AccessorCalls.cs:52:9:52:14 | access to field x |
-| AccessorCalls.cs:52:9:52:12 | this access | AccessorCalls.cs:52:9:52:14 | access to field x |
-| AccessorCalls.cs:52:9:52:14 | access to field x | AccessorCalls.cs:52:9:52:12 | this access |
| AccessorCalls.cs:52:9:52:14 | access to field x | AccessorCalls.cs:52:9:52:19 | access to property Prop |
-| AccessorCalls.cs:52:9:52:19 | access to property Prop | AccessorCalls.cs:52:9:52:34 | ... = ... |
| AccessorCalls.cs:52:9:52:19 | access to property Prop | AccessorCalls.cs:52:24:52:27 | this access |
-| AccessorCalls.cs:52:9:52:34 | ... + ... | AccessorCalls.cs:52:9:52:19 | access to property Prop |
-| AccessorCalls.cs:52:9:52:34 | ... = ... | AccessorCalls.cs:53:9:53:31 | ...; |
+| AccessorCalls.cs:52:9:52:34 | ... += ... | AccessorCalls.cs:53:9:53:31 | ...; |
| AccessorCalls.cs:52:9:52:35 | ...; | AccessorCalls.cs:52:9:52:12 | this access |
| AccessorCalls.cs:52:24:52:27 | this access | AccessorCalls.cs:52:24:52:29 | access to field x |
| AccessorCalls.cs:52:24:52:29 | access to field x | AccessorCalls.cs:52:24:52:34 | access to property Prop |
-| AccessorCalls.cs:52:24:52:34 | access to property Prop | AccessorCalls.cs:52:9:52:34 | ... + ... |
-| AccessorCalls.cs:53:9:53:12 | this access | AccessorCalls.cs:53:9:53:14 | access to field x |
+| AccessorCalls.cs:52:24:52:34 | access to property Prop | AccessorCalls.cs:52:9:52:34 | ... += ... |
| AccessorCalls.cs:53:9:53:12 | this access | AccessorCalls.cs:53:9:53:14 | access to field x |
| AccessorCalls.cs:53:9:53:14 | access to field x | AccessorCalls.cs:53:16:53:16 | 0 |
-| AccessorCalls.cs:53:9:53:14 | access to field x | AccessorCalls.cs:53:16:53:16 | 0 |
-| AccessorCalls.cs:53:9:53:17 | access to indexer | AccessorCalls.cs:53:9:53:30 | ... = ... |
| AccessorCalls.cs:53:9:53:17 | access to indexer | AccessorCalls.cs:53:22:53:25 | this access |
-| AccessorCalls.cs:53:9:53:30 | ... + ... | AccessorCalls.cs:53:9:53:17 | access to indexer |
-| AccessorCalls.cs:53:9:53:30 | ... = ... | AccessorCalls.cs:49:10:49:11 | exit M6 (normal) |
+| AccessorCalls.cs:53:9:53:30 | ... += ... | AccessorCalls.cs:49:10:49:11 | exit M6 (normal) |
| AccessorCalls.cs:53:9:53:31 | ...; | AccessorCalls.cs:53:9:53:12 | this access |
-| AccessorCalls.cs:53:16:53:16 | 0 | AccessorCalls.cs:53:9:53:12 | this access |
| AccessorCalls.cs:53:16:53:16 | 0 | AccessorCalls.cs:53:9:53:17 | access to indexer |
| AccessorCalls.cs:53:22:53:25 | this access | AccessorCalls.cs:53:22:53:27 | access to field x |
| AccessorCalls.cs:53:22:53:27 | access to field x | AccessorCalls.cs:53:29:53:29 | 0 |
-| AccessorCalls.cs:53:22:53:30 | access to indexer | AccessorCalls.cs:53:9:53:30 | ... + ... |
+| AccessorCalls.cs:53:22:53:30 | access to indexer | AccessorCalls.cs:53:9:53:30 | ... += ... |
| AccessorCalls.cs:53:29:53:29 | 0 | AccessorCalls.cs:53:22:53:30 | access to indexer |
| AccessorCalls.cs:56:10:56:11 | enter M7 | AccessorCalls.cs:57:5:59:5 | {...} |
| AccessorCalls.cs:56:10:56:11 | exit M7 (normal) | AccessorCalls.cs:56:10:56:11 | exit M7 |
@@ -271,25 +248,18 @@ dominance
| AccessorCalls.cs:70:9:70:19 | dynamic access to member MaybeProp | AccessorCalls.cs:70:9:70:21 | dynamic call to operator ++ |
| AccessorCalls.cs:70:9:70:21 | dynamic call to operator ++ | AccessorCalls.cs:71:9:71:26 | ...; |
| AccessorCalls.cs:70:9:70:22 | ...; | AccessorCalls.cs:70:9:70:9 | access to local variable d |
-| AccessorCalls.cs:71:9:71:9 | access to local variable d | AccessorCalls.cs:71:9:71:9 | access to local variable d |
| AccessorCalls.cs:71:9:71:9 | access to local variable d | AccessorCalls.cs:71:9:71:20 | dynamic access to member MaybeEvent |
-| AccessorCalls.cs:71:9:71:20 | dynamic access to member MaybeEvent | AccessorCalls.cs:71:9:71:25 | ... = ... |
| AccessorCalls.cs:71:9:71:20 | dynamic access to member MaybeEvent | AccessorCalls.cs:71:25:71:25 | access to parameter e |
-| AccessorCalls.cs:71:9:71:25 | ... = ... | AccessorCalls.cs:72:9:72:21 | ...; |
-| AccessorCalls.cs:71:9:71:25 | dynamic call to operator + | AccessorCalls.cs:71:9:71:20 | dynamic access to member MaybeEvent |
+| AccessorCalls.cs:71:9:71:25 | ... += ... | AccessorCalls.cs:72:9:72:21 | ...; |
| AccessorCalls.cs:71:9:71:26 | ...; | AccessorCalls.cs:71:9:71:9 | access to local variable d |
-| AccessorCalls.cs:71:25:71:25 | access to parameter e | AccessorCalls.cs:71:9:71:25 | dynamic call to operator + |
-| AccessorCalls.cs:72:9:72:9 | access to local variable d | AccessorCalls.cs:72:11:72:11 | 0 |
+| AccessorCalls.cs:71:25:71:25 | access to parameter e | AccessorCalls.cs:71:9:71:25 | ... += ... |
| AccessorCalls.cs:72:9:72:9 | access to local variable d | AccessorCalls.cs:72:11:72:11 | 0 |
-| AccessorCalls.cs:72:9:72:12 | dynamic access to element | AccessorCalls.cs:72:9:72:20 | ... = ... |
| AccessorCalls.cs:72:9:72:12 | dynamic access to element | AccessorCalls.cs:72:17:72:17 | access to local variable d |
-| AccessorCalls.cs:72:9:72:20 | ... = ... | AccessorCalls.cs:73:9:73:84 | ...; |
-| AccessorCalls.cs:72:9:72:20 | dynamic call to operator + | AccessorCalls.cs:72:9:72:12 | dynamic access to element |
+| AccessorCalls.cs:72:9:72:20 | ... += ... | AccessorCalls.cs:73:9:73:84 | ...; |
| AccessorCalls.cs:72:9:72:21 | ...; | AccessorCalls.cs:72:9:72:9 | access to local variable d |
-| AccessorCalls.cs:72:11:72:11 | 0 | AccessorCalls.cs:72:9:72:9 | access to local variable d |
| AccessorCalls.cs:72:11:72:11 | 0 | AccessorCalls.cs:72:9:72:12 | dynamic access to element |
| AccessorCalls.cs:72:17:72:17 | access to local variable d | AccessorCalls.cs:72:19:72:19 | 1 |
-| AccessorCalls.cs:72:17:72:20 | dynamic access to element | AccessorCalls.cs:72:9:72:20 | dynamic call to operator + |
+| AccessorCalls.cs:72:17:72:20 | dynamic access to element | AccessorCalls.cs:72:9:72:20 | ... += ... |
| AccessorCalls.cs:72:19:72:19 | 1 | AccessorCalls.cs:72:17:72:20 | dynamic access to element |
| AccessorCalls.cs:73:9:73:44 | (..., ...) | AccessorCalls.cs:73:49:73:49 | access to local variable d |
| AccessorCalls.cs:73:9:73:83 | ... = ... | AccessorCalls.cs:66:10:66:11 | exit M9 (normal) |
@@ -732,27 +702,24 @@ dominance
| Assignments.cs:5:13:5:17 | Int32 x = ... | Assignments.cs:6:9:6:15 | ...; |
| Assignments.cs:5:17:5:17 | 0 | Assignments.cs:5:13:5:17 | Int32 x = ... |
| Assignments.cs:6:9:6:9 | access to local variable x | Assignments.cs:6:14:6:14 | 1 |
-| Assignments.cs:6:9:6:14 | ... + ... | Assignments.cs:6:9:6:14 | ... = ... |
-| Assignments.cs:6:9:6:14 | ... = ... | Assignments.cs:8:9:8:22 | ... ...; |
+| Assignments.cs:6:9:6:14 | ... += ... | Assignments.cs:8:9:8:22 | ... ...; |
| Assignments.cs:6:9:6:15 | ...; | Assignments.cs:6:9:6:9 | access to local variable x |
-| Assignments.cs:6:14:6:14 | 1 | Assignments.cs:6:9:6:14 | ... + ... |
+| Assignments.cs:6:14:6:14 | 1 | Assignments.cs:6:9:6:14 | ... += ... |
| Assignments.cs:8:9:8:22 | ... ...; | Assignments.cs:8:21:8:21 | 0 |
| Assignments.cs:8:17:8:21 | dynamic d = ... | Assignments.cs:9:9:9:15 | ...; |
| Assignments.cs:8:21:8:21 | 0 | Assignments.cs:8:21:8:21 | (...) ... |
| Assignments.cs:8:21:8:21 | (...) ... | Assignments.cs:8:17:8:21 | dynamic d = ... |
| Assignments.cs:9:9:9:9 | access to local variable d | Assignments.cs:9:14:9:14 | 2 |
-| Assignments.cs:9:9:9:14 | ... = ... | Assignments.cs:11:9:11:34 | ... ...; |
-| Assignments.cs:9:9:9:14 | dynamic call to operator - | Assignments.cs:9:9:9:14 | ... = ... |
+| Assignments.cs:9:9:9:14 | ... -= ... | Assignments.cs:11:9:11:34 | ... ...; |
| Assignments.cs:9:9:9:15 | ...; | Assignments.cs:9:9:9:9 | access to local variable d |
-| Assignments.cs:9:14:9:14 | 2 | Assignments.cs:9:9:9:14 | dynamic call to operator - |
+| Assignments.cs:9:14:9:14 | 2 | Assignments.cs:9:9:9:14 | ... -= ... |
| Assignments.cs:11:9:11:34 | ... ...; | Assignments.cs:11:17:11:33 | object creation of type Assignments |
| Assignments.cs:11:13:11:33 | Assignments a = ... | Assignments.cs:12:9:12:18 | ...; |
| Assignments.cs:11:17:11:33 | object creation of type Assignments | Assignments.cs:11:13:11:33 | Assignments a = ... |
| Assignments.cs:12:9:12:9 | access to local variable a | Assignments.cs:12:14:12:17 | this access |
-| Assignments.cs:12:9:12:17 | ... = ... | Assignments.cs:14:9:14:36 | ...; |
-| Assignments.cs:12:9:12:17 | call to operator + | Assignments.cs:12:9:12:17 | ... = ... |
+| Assignments.cs:12:9:12:17 | ... += ... | Assignments.cs:14:9:14:36 | ...; |
| Assignments.cs:12:9:12:18 | ...; | Assignments.cs:12:9:12:9 | access to local variable a |
-| Assignments.cs:12:14:12:17 | this access | Assignments.cs:12:9:12:17 | call to operator + |
+| Assignments.cs:12:14:12:17 | this access | Assignments.cs:12:9:12:17 | ... += ... |
| Assignments.cs:14:9:14:13 | access to event Event | Assignments.cs:14:9:14:35 | ... += ... |
| Assignments.cs:14:9:14:13 | this access | Assignments.cs:14:18:14:35 | (...) => ... |
| Assignments.cs:14:9:14:35 | ... += ... | Assignments.cs:3:10:3:10 | exit M (normal) |
@@ -1058,22 +1025,16 @@ dominance
| ConditionalAccess.cs:52:9:52:28 | access to property StringProp | ConditionalAccess.cs:52:18:52:38 | ... = ... |
| ConditionalAccess.cs:52:9:52:39 | ...; | ConditionalAccess.cs:52:9:52:10 | access to parameter ca |
| ConditionalAccess.cs:52:32:52:38 | "World" | ConditionalAccess.cs:52:9:52:28 | access to property StringProp |
-| ConditionalAccess.cs:53:9:53:10 | access to parameter ca | ConditionalAccess.cs:53:9:53:10 | access to parameter ca |
| ConditionalAccess.cs:53:9:53:10 | access to parameter ca | ConditionalAccess.cs:53:9:53:20 | access to field IntField |
| ConditionalAccess.cs:53:9:53:10 | access to parameter ca | ConditionalAccess.cs:53:25:53:25 | 1 |
-| ConditionalAccess.cs:53:9:53:10 | access to parameter ca | ConditionalAccess.cs:54:9:54:30 | ...; |
-| ConditionalAccess.cs:53:9:53:20 | access to field IntField | ConditionalAccess.cs:53:12:53:25 | ... = ... |
| ConditionalAccess.cs:53:9:53:26 | ...; | ConditionalAccess.cs:53:9:53:10 | access to parameter ca |
-| ConditionalAccess.cs:53:12:53:25 | ... - ... | ConditionalAccess.cs:53:9:53:20 | access to field IntField |
-| ConditionalAccess.cs:53:25:53:25 | 1 | ConditionalAccess.cs:53:12:53:25 | ... - ... |
-| ConditionalAccess.cs:54:9:54:10 | access to parameter ca | ConditionalAccess.cs:46:10:46:11 | exit M9 (normal) |
-| ConditionalAccess.cs:54:9:54:10 | access to parameter ca | ConditionalAccess.cs:54:9:54:10 | access to parameter ca |
+| ConditionalAccess.cs:53:12:53:25 | ... -= ... | ConditionalAccess.cs:54:9:54:30 | ...; |
+| ConditionalAccess.cs:53:25:53:25 | 1 | ConditionalAccess.cs:53:12:53:25 | ... -= ... |
| ConditionalAccess.cs:54:9:54:10 | access to parameter ca | ConditionalAccess.cs:54:9:54:22 | access to property StringProp |
| ConditionalAccess.cs:54:9:54:10 | access to parameter ca | ConditionalAccess.cs:54:27:54:29 | "!" |
-| ConditionalAccess.cs:54:9:54:22 | access to property StringProp | ConditionalAccess.cs:54:12:54:29 | ... = ... |
| ConditionalAccess.cs:54:9:54:30 | ...; | ConditionalAccess.cs:54:9:54:10 | access to parameter ca |
-| ConditionalAccess.cs:54:12:54:29 | ... + ... | ConditionalAccess.cs:54:9:54:22 | access to property StringProp |
-| ConditionalAccess.cs:54:27:54:29 | "!" | ConditionalAccess.cs:54:12:54:29 | ... + ... |
+| ConditionalAccess.cs:54:12:54:29 | ... += ... | ConditionalAccess.cs:46:10:46:11 | exit M9 (normal) |
+| ConditionalAccess.cs:54:27:54:29 | "!" | ConditionalAccess.cs:54:12:54:29 | ... += ... |
| ConditionalAccess.cs:60:26:60:38 | enter CommaJoinWith | ConditionalAccess.cs:60:70:60:71 | access to parameter s1 |
| ConditionalAccess.cs:60:26:60:38 | exit CommaJoinWith (normal) | ConditionalAccess.cs:60:26:60:38 | exit CommaJoinWith |
| ConditionalAccess.cs:60:70:60:71 | access to parameter s1 | ConditionalAccess.cs:60:75:60:78 | ", " |
@@ -1295,9 +1256,8 @@ dominance
| Conditions.cs:105:13:105:13 | access to parameter b | Conditions.cs:106:13:106:20 | ...; |
| Conditions.cs:105:13:105:13 | access to parameter b | Conditions.cs:107:9:109:24 | if (...) ... |
| Conditions.cs:106:13:106:13 | access to local variable x | Conditions.cs:106:18:106:19 | "" |
-| Conditions.cs:106:13:106:19 | ... + ... | Conditions.cs:106:13:106:19 | ... = ... |
| Conditions.cs:106:13:106:20 | ...; | Conditions.cs:106:13:106:13 | access to local variable x |
-| Conditions.cs:106:18:106:19 | "" | Conditions.cs:106:13:106:19 | ... + ... |
+| Conditions.cs:106:18:106:19 | "" | Conditions.cs:106:13:106:19 | ... += ... |
| Conditions.cs:107:9:109:24 | if (...) ... | Conditions.cs:107:13:107:13 | access to local variable x |
| Conditions.cs:107:13:107:13 | access to local variable x | Conditions.cs:107:13:107:20 | access to property Length |
| Conditions.cs:107:13:107:20 | access to property Length | Conditions.cs:107:24:107:24 | 0 |
@@ -1309,9 +1269,8 @@ dominance
| Conditions.cs:108:18:108:18 | access to parameter b | Conditions.cs:108:17:108:18 | [false] !... |
| Conditions.cs:108:18:108:18 | access to parameter b | Conditions.cs:108:17:108:18 | [true] !... |
| Conditions.cs:109:17:109:17 | access to local variable x | Conditions.cs:109:22:109:23 | "" |
-| Conditions.cs:109:17:109:23 | ... + ... | Conditions.cs:109:17:109:23 | ... = ... |
| Conditions.cs:109:17:109:24 | ...; | Conditions.cs:109:17:109:17 | access to local variable x |
-| Conditions.cs:109:22:109:23 | "" | Conditions.cs:109:17:109:23 | ... + ... |
+| Conditions.cs:109:22:109:23 | "" | Conditions.cs:109:17:109:23 | ... += ... |
| Conditions.cs:110:9:110:17 | return ...; | Conditions.cs:102:12:102:13 | exit M8 (normal) |
| Conditions.cs:110:16:110:16 | access to local variable x | Conditions.cs:110:9:110:17 | return ...; |
| Conditions.cs:113:10:113:11 | enter M9 | Conditions.cs:114:5:124:5 | {...} |
@@ -1939,11 +1898,10 @@ dominance
| Finally.cs:271:13:271:35 | ...; | Finally.cs:271:31:271:33 | "3" |
| Finally.cs:271:31:271:33 | "3" | Finally.cs:271:13:271:34 | call to method WriteLine |
| Finally.cs:272:13:272:13 | access to parameter i | Finally.cs:272:18:272:18 | 3 |
-| Finally.cs:272:13:272:18 | ... + ... | Finally.cs:272:13:272:18 | ... = ... |
-| Finally.cs:272:13:272:18 | ... = ... | Finally.cs:263:10:263:12 | exit M13 (abnormal) |
-| Finally.cs:272:13:272:18 | ... = ... | Finally.cs:263:10:263:12 | exit M13 (normal) |
+| Finally.cs:272:13:272:18 | ... += ... | Finally.cs:263:10:263:12 | exit M13 (abnormal) |
+| Finally.cs:272:13:272:18 | ... += ... | Finally.cs:263:10:263:12 | exit M13 (normal) |
| Finally.cs:272:13:272:19 | ...; | Finally.cs:272:13:272:13 | access to parameter i |
-| Finally.cs:272:18:272:18 | 3 | Finally.cs:272:13:272:18 | ... + ... |
+| Finally.cs:272:18:272:18 | 3 | Finally.cs:272:13:272:18 | ... += ... |
| Foreach.cs:4:7:4:13 | call to constructor Object | Foreach.cs:4:7:4:13 | {...} |
| Foreach.cs:4:7:4:13 | call to method