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..ca153bfb97db 100644
--- a/csharp/ql/campaigns/Solorigate/src/ModifiedFnvFunctionDetection.ql
+++ b/csharp/ql/campaigns/Solorigate/src/ModifiedFnvFunctionDetection.ql
@@ -16,16 +16,9 @@ import experimental.code.csharp.Cryptography.NonCryptographicHashes
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 |
+ loop.getAControlFlowExitNode().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/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/semmle/code/csharp/Assignable.qll b/csharp/ql/lib/semmle/code/csharp/Assignable.qll
index 3c7170a6f846..a0e575218adc 100644
--- a/csharp/ql/lib/semmle/code/csharp/Assignable.qll
+++ b/csharp/ql/lib/semmle/code/csharp/Assignable.qll
@@ -78,6 +78,8 @@ class AssignableRead extends AssignableAccess {
this.isRefArgument()
or
this = any(AssignableDefinitions::AddressOfDefinition def).getTargetAccess()
+ or
+ this = any(AssignableDefinitions::AssignOperationDefinition def).getTargetAccess()
) and
not nameOfChild(_, this)
}
@@ -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 {
/**
@@ -511,7 +528,7 @@ module AssignableDefinitions {
override Expr getSource() {
result = a.getRValue() and
- not a instanceof AssignOperation
+ not a instanceof AddOrRemoveEventExpr
}
override string toString() { result = a.toString() }
@@ -735,4 +752,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/ExprOrStmtParent.qll b/csharp/ql/lib/semmle/code/csharp/ExprOrStmtParent.qll
index 5afacf608a8c..8102b4a02880 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) {
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/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/Guards.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll
index 40ec3722edd2..03a5aa7e2b74 100644
--- a/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll
+++ b/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll
@@ -119,14 +119,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 +292,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
@@ -840,14 +840,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 +907,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 +922,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 +951,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 +973,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) {
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..e734e79402bf 100644
--- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Completion.qll
+++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Completion.qll
@@ -344,7 +344,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 +353,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
@@ -447,7 +447,7 @@ private predicate inBooleanContext(Expr e) {
e in [ce.getThen(), ce.getElse()]
)
or
- e = any(NullCoalescingExpr nce | inBooleanContext(nce)).getAnOperand()
+ e = any(NullCoalescingOperation nce | inBooleanContext(nce)).getAnOperand()
or
e = any(SwitchExpr se | inBooleanContext(se)).getACase()
or
@@ -467,13 +467,13 @@ private predicate mustHaveNullnessCompletion(Expr e) {
* that `e` evaluates to determines a `null`/non-`null` branch successor.
*/
private predicate inNullnessContext(Expr e) {
- e = any(NullCoalescingExpr nce).getLeftOperand()
+ e = any(NullCoalescingOperation 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())
+ exists(NullCoalescingOperation nce | inNullnessContext(nce) | e = nce.getRightOperand())
or
e = any(SwitchExpr se | inNullnessContext(se)).getACase()
or
diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll
index 0bdf1f795db2..e29e92d26eba 100644
--- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll
+++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll
@@ -62,18 +62,13 @@ class CfgScope extends Element, @top_level_exprorstmt_parent {
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)
+ e = parent.getAChild()
)
}
@@ -447,7 +442,6 @@ module Expressions {
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
@@ -458,14 +452,6 @@ module Expressions {
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) {
@@ -491,9 +477,8 @@ module Expressions {
not this instanceof LogicalNotExpr and
not this instanceof LogicalAndExpr and
not this instanceof LogicalOrExpr and
- not this instanceof NullCoalescingExpr and
+ not this instanceof NullCoalescingOperation 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
@@ -590,8 +575,7 @@ module Expressions {
QualifiedAccessorWrite() {
def.getExpr() = this and
def.getTargetAccess().(WriteAccess) instanceof QualifiableExpr and
- not def instanceof AssignableDefinitions::OutRefDefinition and
- not this instanceof AssignOperationWithExpandedAssignment
+ not def instanceof AssignableDefinitions::OutRefDefinition
}
/**
@@ -723,7 +707,8 @@ module Expressions {
}
}
- private class NullCoalescingExprTree extends PostOrderTree instanceof NullCoalescingExpr {
+ private class NullCoalescingOperationTree extends PostOrderTree instanceof NullCoalescingOperation
+ {
final override predicate propagatesAbnormal(AstNode child) {
child in [super.getLeftOperand(), super.getRightOperand()]
}
@@ -774,26 +759,6 @@ module Expressions {
}
}
- /**
- * 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;
@@ -1551,7 +1516,7 @@ module Statements {
/** Gets a child of `cfe` that is in CFG scope `scope`. */
pragma[noinline]
private ControlFlowElement getAChildInScope(AstNode cfe, Callable scope) {
- result = getAChild(cfe) and
+ result = cfe.getAChild() and
scope = result.getEnclosingCallable()
}
diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Splitting.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Splitting.qll
index 55b75ed31a71..173179216f3d 100644
--- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Splitting.qll
+++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Splitting.qll
@@ -95,7 +95,7 @@ module ConditionalCompletionSplitting {
child = parent.(SwitchCaseExpr).getBody()
or
parent =
- any(NullCoalescingExpr nce |
+ any(NullCoalescingOperation nce |
if childCompletion instanceof NullnessCompletion
then child = nce.getRightOperand()
else child = nce.getAnOperand()
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll
index c7ac34d3d01a..a82779eaa5d7 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 |
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..afb09f858354 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
@@ -512,7 +512,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
@@ -623,7 +623,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
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/ModulusAnalysisSpecific.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ModulusAnalysisSpecific.qll
index c9c3a937ef9e..ca0aa83f29fc 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
@@ -20,17 +20,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..46153f18dae2 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
@@ -21,7 +21,11 @@ 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
+ 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
@@ -218,6 +222,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 +318,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..d59d7958765a 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
@@ -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;
@@ -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/SsaUtils.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaUtils.qll
index b26082b6250a..89117d90ba79 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
@@ -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..f7f6c7a50be4 100644
--- a/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll
@@ -202,11 +202,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
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..0d9e414a5c44 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" }
}
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/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/lib/upgrades/e73ca2c93df8aae162f1704edc4817a5cb330529/old.dbscheme b/csharp/ql/lib/upgrades/e73ca2c93df8aae162f1704edc4817a5cb330529/old.dbscheme
new file mode 100644
index 000000000000..e73ca2c93df8
--- /dev/null
+++ b/csharp/ql/lib/upgrades/e73ca2c93df8aae162f1704edc4817a5cb330529/old.dbscheme
@@ -0,0 +1,1489 @@
+/* This is a dummy line to alter the dbscheme, so we can make a database upgrade
+ * without actually changing any of the dbscheme predicates. It contains a date
+ * to allow for such updates in the future as well.
+ *
+ * 2021-07-14
+ *
+ * DO NOT remove this comment carelessly, since it can revert the dbscheme back to a
+ * previously seen state (matching a previously seen SHA), which would make the upgrade
+ * mechanism not work properly.
+ */
+
+/**
+ * An invocation of the compiler. Note that more than one file may be
+ * compiled per invocation. For example, this command compiles three
+ * source files:
+ *
+ * csc f1.cs f2.cs f3.cs
+ *
+ * The `id` simply identifies the invocation, while `cwd` is the working
+ * directory from which the compiler was invoked.
+ */
+compilations(
+ unique int id : @compilation,
+ string cwd : string ref
+);
+
+compilation_info(
+ int id : @compilation ref,
+ string info_key: string ref,
+ string info_value: string ref
+)
+
+/**
+ * The arguments that were passed to the extractor for a compiler
+ * invocation. If `id` is for the compiler invocation
+ *
+ * csc f1.cs f2.cs f3.cs
+ *
+ * then typically there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | --compiler
+ * 1 | *path to compiler*
+ * 2 | f1.cs
+ * 3 | f2.cs
+ * 4 | f3.cs
+ */
+#keyset[id, num]
+compilation_args(
+ int id : @compilation ref,
+ int num : int ref,
+ string arg : string ref
+);
+
+/**
+ * The expanded arguments that were passed to the extractor for a
+ * compiler invocation. This is similar to `compilation_args`, but
+ * for a `@someFile.rsp` argument, it includes the arguments from that
+ * file, rather than just taking the argument literally.
+ */
+#keyset[id, num]
+compilation_expanded_args(
+ int id : @compilation ref,
+ int num : int ref,
+ string arg : string ref
+);
+
+/**
+ * The source files that are compiled by a compiler invocation.
+ * If `id` is for the compiler invocation
+ *
+ * csc f1.cs f2.cs f3.cs
+ *
+ * then there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | f1.cs
+ * 1 | f2.cs
+ * 2 | f3.cs
+ */
+#keyset[id, num]
+compilation_compiling_files(
+ int id : @compilation ref,
+ int num : int ref,
+ int file : @file ref
+);
+
+/**
+ * The references used by a compiler invocation.
+ * If `id` is for the compiler invocation
+ *
+ * csc f1.cs f2.cs f3.cs /r:ref1.dll /r:ref2.dll /r:ref3.dll
+ *
+ * then there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | ref1.dll
+ * 1 | ref2.dll
+ * 2 | ref3.dll
+ */
+#keyset[id, num]
+compilation_referencing_files(
+ int id : @compilation ref,
+ int num : int ref,
+ int file : @file ref
+);
+
+/**
+ * The time taken by the extractor for a compiler invocation.
+ *
+ * For each file `num`, there will be rows for
+ *
+ * kind | seconds
+ * ---- | ---
+ * 1 | CPU seconds used by the extractor frontend
+ * 2 | Elapsed seconds during the extractor frontend
+ * 3 | CPU seconds used by the extractor backend
+ * 4 | Elapsed seconds during the extractor backend
+ */
+#keyset[id, num, kind]
+compilation_time(
+ int id : @compilation ref,
+ int num : int ref,
+ /* kind:
+ 1 = frontend_cpu_seconds
+ 2 = frontend_elapsed_seconds
+ 3 = extractor_cpu_seconds
+ 4 = extractor_elapsed_seconds
+ */
+ int kind : int ref,
+ float seconds : float ref
+);
+
+/**
+ * An error or warning generated by the extractor.
+ * The diagnostic message `diagnostic` was generated during compiler
+ * invocation `compilation`, and is the `file_number_diagnostic_number`th
+ * message generated while extracting the `file_number`th file of that
+ * invocation.
+ */
+#keyset[compilation, file_number, file_number_diagnostic_number]
+diagnostic_for(
+ unique int diagnostic : @diagnostic ref,
+ int compilation : @compilation ref,
+ int file_number : int ref,
+ int file_number_diagnostic_number : int ref
+);
+
+diagnostics(
+ unique int id: @diagnostic,
+ int severity: int ref,
+ string error_tag: string ref,
+ string error_message: string ref,
+ string full_error_message: string ref,
+ int location: @location ref
+);
+
+extractor_messages(
+ unique int id: @extractor_message,
+ int severity: int ref,
+ string origin : string ref,
+ string text : string ref,
+ string entity : string ref,
+ int location: @location ref,
+ string stack_trace : string ref
+);
+
+/**
+ * If extraction was successful, then `cpu_seconds` and
+ * `elapsed_seconds` are the CPU time and elapsed time (respectively)
+ * that extraction took for compiler invocation `id`.
+ */
+compilation_finished(
+ unique int id : @compilation ref,
+ float cpu_seconds : float ref,
+ float elapsed_seconds : float ref
+);
+
+compilation_assembly(
+ unique int id : @compilation ref,
+ int assembly: @assembly ref
+)
+
+// Populated by the CSV extractor
+externalData(
+ int id: @externalDataElement,
+ string path: string ref,
+ int column: int ref,
+ string value: string ref);
+
+sourceLocationPrefix(
+ string prefix: string ref);
+
+/*
+ * Overlay support
+ */
+
+/**
+ * The CLI will automatically emit the tuple `databaseMetadata("isOverlay", "true")`,
+ * along with an `overlayChangedFiles` tuple for each new/modified/deleted file,
+ * when building an overlay database, and these can be used by the discard predicates.
+ */
+databaseMetadata(
+ string metadataKey : string ref,
+ string value : string ref
+);
+
+overlayChangedFiles(
+ string path : string ref
+);
+
+/*
+ * C# dbscheme
+ */
+
+/** ELEMENTS **/
+
+@element = @declaration | @stmt | @expr | @modifier | @attribute | @namespace_declaration
+ | @using_directive | @type_parameter_constraints | @externalDataElement
+ | @xmllocatable | @asp_element | @namespace | @preprocessor_directive;
+
+@declaration = @callable | @generic | @assignable | @namespace;
+
+@named_element = @namespace | @declaration;
+
+@declaration_with_accessors = @property | @indexer | @event;
+
+@assignable = @variable | @assignable_with_accessors | @event;
+
+@assignable_with_accessors = @property | @indexer;
+
+@attributable = @assembly | @field | @parameter | @operator | @method | @constructor
+ | @destructor | @callable_accessor | @value_or_ref_type | @declaration_with_accessors
+ | @local_function | @lambda_expr;
+
+/** LOCATIONS, ASEMMBLIES, MODULES, FILES and FOLDERS **/
+
+@location = @location_default | @assembly;
+
+@locatable = @declaration_with_accessors | @callable_accessor | @declaration_or_directive
+ | @diagnostic | @extractor_message | @preprocessor_directive | @attribute | @type_mention | @type_parameter_constraints
+ | @declaration_with_accessors | @callable_accessor | @operator | @method
+ | @constructor | @destructor | @field | @local_variable | @parameter | @stmt | @expr
+ | @xmllocatable | @commentline | @commentblock | @asp_element
+
+locations_default(
+ unique int id: @location_default,
+ int file: @file ref,
+ int beginLine: int ref,
+ int beginColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref);
+
+locations_mapped(
+ unique int id: @location_default ref,
+ int mapped_to: @location_default ref);
+
+@sourceline = @file | @callable | @xmllocatable;
+
+numlines(
+ int element_id: @sourceline ref,
+ int num_lines: int ref,
+ int num_code: int ref,
+ int num_comment: int ref);
+
+assemblies(
+ unique int id: @assembly,
+ int file: @file ref,
+ string fullname: string ref,
+ string name: string ref,
+ string version: string ref);
+
+files(
+ unique int id: @file,
+ string name: string ref);
+
+folders(
+ unique int id: @folder,
+ string name: string ref);
+
+@container = @folder | @file ;
+
+containerparent(
+ int parent: @container ref,
+ unique int child: @container ref);
+
+file_extraction_mode(
+ unique int file: @file ref,
+ int mode: int ref
+ /* 0 = normal, 1 = standalone extractor */
+ );
+
+/** NAMESPACES **/
+
+@type_container = @namespace | @type;
+
+namespaces(
+ unique int id: @namespace,
+ string name: string ref);
+
+namespace_declarations(
+ unique int id: @namespace_declaration,
+ int namespace_id: @namespace ref);
+
+namespace_declaration_location(
+ unique int id: @namespace_declaration ref,
+ int loc: @location ref);
+
+parent_namespace(
+ unique int child_id: @type_container ref,
+ int namespace_id: @namespace ref);
+
+@declaration_or_directive = @namespace_declaration | @type | @using_directive;
+
+parent_namespace_declaration(
+ int child_id: @declaration_or_directive ref, // cannot be unique because of partial classes
+ int namespace_id: @namespace_declaration ref);
+
+@using_directive = @using_namespace_directive | @using_static_directive;
+
+using_global(
+ unique int id: @using_directive ref
+);
+
+using_namespace_directives(
+ unique int id: @using_namespace_directive,
+ int namespace_id: @namespace ref);
+
+using_static_directives(
+ unique int id: @using_static_directive,
+ int type_id: @type_or_ref ref);
+
+using_directive_location(
+ unique int id: @using_directive ref,
+ int loc: @location ref);
+
+@preprocessor_directive = @pragma_warning | @pragma_checksum | @directive_define | @directive_undefine | @directive_warning
+ | @directive_error | @directive_nullable | @directive_line | @directive_region | @directive_endregion | @directive_if
+ | @directive_elif | @directive_else | @directive_endif;
+
+@conditional_directive = @directive_if | @directive_elif;
+@branch_directive = @directive_if | @directive_elif | @directive_else;
+
+directive_ifs(
+ unique int id: @directive_if,
+ int branchTaken: int ref, /* 0: false, 1: true */
+ int conditionValue: int ref); /* 0: false, 1: true */
+
+directive_elifs(
+ unique int id: @directive_elif,
+ int branchTaken: int ref, /* 0: false, 1: true */
+ int conditionValue: int ref, /* 0: false, 1: true */
+ int parent: @directive_if ref,
+ int index: int ref);
+
+directive_elses(
+ unique int id: @directive_else,
+ int branchTaken: int ref, /* 0: false, 1: true */
+ int parent: @directive_if ref,
+ int index: int ref);
+
+#keyset[id, start]
+directive_endifs(
+ unique int id: @directive_endif,
+ unique int start: @directive_if ref);
+
+directive_define_symbols(
+ unique int id: @define_symbol_expr ref,
+ string name: string ref);
+
+directive_regions(
+ unique int id: @directive_region,
+ string name: string ref);
+
+#keyset[id, start]
+directive_endregions(
+ unique int id: @directive_endregion,
+ unique int start: @directive_region ref);
+
+directive_lines(
+ unique int id: @directive_line,
+ int kind: int ref); /* 0: default, 1: hidden, 2: numeric, 3: span */
+
+directive_line_value(
+ unique int id: @directive_line ref,
+ int line: int ref);
+
+directive_line_file(
+ unique int id: @directive_line ref,
+ int file: @file ref);
+
+directive_line_offset(
+ unique int id: @directive_line ref,
+ int offset: int ref);
+
+directive_line_span(
+ unique int id: @directive_line ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref);
+
+directive_nullables(
+ unique int id: @directive_nullable,
+ int setting: int ref, /* 0: disable, 1: enable, 2: restore */
+ int target: int ref); /* 0: none, 1: annotations, 2: warnings */
+
+directive_warnings(
+ unique int id: @directive_warning,
+ string message: string ref);
+
+directive_errors(
+ unique int id: @directive_error,
+ string message: string ref);
+
+directive_undefines(
+ unique int id: @directive_undefine,
+ string name: string ref);
+
+directive_defines(
+ unique int id: @directive_define,
+ string name: string ref);
+
+pragma_checksums(
+ unique int id: @pragma_checksum,
+ int file: @file ref,
+ string guid: string ref,
+ string bytes: string ref);
+
+pragma_warnings(
+ unique int id: @pragma_warning,
+ int kind: int ref /* 0 = disable, 1 = restore */);
+
+#keyset[id, index]
+pragma_warning_error_codes(
+ int id: @pragma_warning ref,
+ string errorCode: string ref,
+ int index: int ref);
+
+preprocessor_directive_location(
+ unique int id: @preprocessor_directive ref,
+ int loc: @location ref);
+
+preprocessor_directive_compilation(
+ int id: @preprocessor_directive ref,
+ int compilation: @compilation ref);
+
+preprocessor_directive_active(
+ unique int id: @preprocessor_directive ref,
+ int active: int ref); /* 0: false, 1: true */
+
+/** TYPES **/
+
+types(
+ unique int id: @type,
+ int kind: int ref,
+ string name: string ref);
+
+case @type.kind of
+ 1 = @bool_type
+| 2 = @char_type
+| 3 = @decimal_type
+| 4 = @sbyte_type
+| 5 = @short_type
+| 6 = @int_type
+| 7 = @long_type
+| 8 = @byte_type
+| 9 = @ushort_type
+| 10 = @uint_type
+| 11 = @ulong_type
+| 12 = @float_type
+| 13 = @double_type
+| 14 = @enum_type
+| 15 = @struct_type
+| 17 = @class_type
+| 19 = @interface_type
+| 20 = @delegate_type
+| 21 = @null_type
+| 22 = @type_parameter
+| 23 = @pointer_type
+| 24 = @nullable_type
+| 25 = @array_type
+| 26 = @void_type
+| 27 = @int_ptr_type
+| 28 = @uint_ptr_type
+| 29 = @dynamic_type
+| 30 = @arglist_type
+| 31 = @unknown_type
+| 32 = @tuple_type
+| 33 = @function_pointer_type
+| 34 = @inline_array_type
+| 35 = @extension_type
+ ;
+
+@simple_type = @bool_type | @char_type | @integral_type | @floating_point_type | @decimal_type;
+@integral_type = @signed_integral_type | @unsigned_integral_type;
+@signed_integral_type = @sbyte_type | @short_type | @int_type | @long_type;
+@unsigned_integral_type = @byte_type | @ushort_type | @uint_type | @ulong_type;
+@floating_point_type = @float_type | @double_type;
+@value_type = @simple_type | @enum_type | @struct_type | @nullable_type | @int_ptr_type
+ | @uint_ptr_type | @tuple_type | @void_type | @inline_array_type;
+@ref_type = @class_type | @interface_type | @array_type | @delegate_type | @null_type
+ | @dynamic_type | @extension_type;
+@value_or_ref_type = @value_type | @ref_type;
+
+typerefs(
+ unique int id: @typeref,
+ string name: string ref);
+
+typeref_type(
+ int id: @typeref ref,
+ unique int typeId: @type ref);
+
+@type_or_ref = @type | @typeref;
+
+array_element_type(
+ unique int array: @array_type ref,
+ int dimension: int ref,
+ int rank: int ref,
+ int element: @type_or_ref ref);
+
+nullable_underlying_type(
+ unique int nullable: @nullable_type ref,
+ int underlying: @type_or_ref ref);
+
+pointer_referent_type(
+ unique int pointer: @pointer_type ref,
+ int referent: @type_or_ref ref);
+
+enum_underlying_type(
+ unique int enum_id: @enum_type ref,
+ int underlying_type_id: @type_or_ref ref);
+
+delegate_return_type(
+ unique int delegate_id: @delegate_type ref,
+ int return_type_id: @type_or_ref ref);
+
+function_pointer_return_type(
+ unique int function_pointer_id: @function_pointer_type ref,
+ int return_type_id: @type_or_ref ref);
+
+extension_receiver_type(
+ unique int extension: @extension_type ref,
+ int receiver_type_id: @type_or_ref ref);
+
+extend(
+ int sub: @type ref,
+ int super: @type_or_ref ref);
+
+anonymous_types(
+ unique int id: @type ref);
+
+@interface_or_ref = @interface_type | @typeref;
+
+implement(
+ int sub: @type ref,
+ int super: @type_or_ref ref);
+
+type_location(
+ int id: @type ref,
+ int loc: @location ref);
+
+tuple_underlying_type(
+ unique int tuple: @tuple_type ref,
+ int struct: @type_or_ref ref);
+
+#keyset[tuple, index]
+tuple_element(
+ int tuple: @tuple_type ref,
+ int index: int ref,
+ unique int field: @field ref);
+
+attributes(
+ unique int id: @attribute,
+ int kind: int ref,
+ int type_id: @type_or_ref ref,
+ int target: @attributable ref);
+
+case @attribute.kind of
+ 0 = @attribute_default
+| 1 = @attribute_return
+| 2 = @attribute_assembly
+| 3 = @attribute_module
+;
+
+attribute_location(
+ int id: @attribute ref,
+ int loc: @location ref);
+
+@type_mention_parent = @element | @type_mention;
+
+type_mention(
+ unique int id: @type_mention,
+ int type_id: @type_or_ref ref,
+ int parent: @type_mention_parent ref);
+
+type_mention_location(
+ unique int id: @type_mention ref,
+ int loc: @location ref);
+
+@has_type_annotation = @assignable | @type_parameter | @callable | @expr | @delegate_type | @generic | @function_pointer_type;
+
+/**
+ * A direct annotation on an entity, for example `string? x;`.
+ *
+ * Annotations:
+ * 2 = reftype is not annotated "!"
+ * 3 = reftype is annotated "?"
+ * 4 = readonly ref type / in parameter
+ * 5 = ref type parameter, return or local variable
+ * 6 = out parameter
+ *
+ * Note that the annotation depends on the element it annotates.
+ * @assignable: The annotation is on the type of the assignable, for example the variable type.
+ * @type_parameter: The annotation is on the reftype constraint
+ * @callable: The annotation is on the return type
+ * @array_type: The annotation is on the element type
+ */
+type_annotation(int id: @has_type_annotation ref, int annotation: int ref);
+
+nullability(unique int nullability: @nullability, int kind: int ref);
+
+case @nullability.kind of
+ 0 = @oblivious
+| 1 = @not_annotated
+| 2 = @annotated
+;
+
+#keyset[parent, index]
+nullability_parent(int nullability: @nullability ref, int index: int ref, int parent: @nullability ref)
+
+type_nullability(int id: @has_type_annotation ref, int nullability: @nullability ref);
+
+/**
+ * The nullable flow state of an expression, as determined by Roslyn.
+ * 0 = none (default, not populated)
+ * 1 = not null
+ * 2 = maybe null
+ */
+expr_flowstate(unique int id: @expr ref, int state: int ref);
+
+/** GENERICS **/
+
+@generic = @type | @method | @local_function;
+
+type_parameters(
+ unique int id: @type_parameter ref,
+ int index: int ref,
+ int generic_id: @generic ref,
+ int variance: int ref /* none = 0, out = 1, in = 2 */);
+
+#keyset[constructed_id, index]
+type_arguments(
+ int id: @type_or_ref ref,
+ int index: int ref,
+ int constructed_id: @generic_or_ref ref);
+
+@generic_or_ref = @generic | @typeref;
+
+constructed_generic(
+ unique int constructed: @generic ref,
+ int generic: @generic_or_ref ref);
+
+type_parameter_constraints(
+ unique int id: @type_parameter_constraints,
+ int param_id: @type_parameter ref);
+
+type_parameter_constraints_location(
+ int id: @type_parameter_constraints ref,
+ int loc: @location ref);
+
+general_type_parameter_constraints(
+ int id: @type_parameter_constraints ref,
+ int kind: int ref /* class = 1, struct = 2, new = 3 */);
+
+specific_type_parameter_constraints(
+ int id: @type_parameter_constraints ref,
+ int base_id: @type_or_ref ref);
+
+specific_type_parameter_nullability(
+ int id: @type_parameter_constraints ref,
+ int base_id: @type_or_ref ref,
+ int nullability: @nullability ref);
+
+/** FUNCTION POINTERS */
+
+function_pointer_calling_conventions(
+ int id: @function_pointer_type ref,
+ int kind: int ref);
+
+#keyset[id, index]
+has_unmanaged_calling_conventions(
+ int id: @function_pointer_type ref,
+ int index: int ref,
+ int conv_id: @type_or_ref ref);
+
+/** MODIFIERS */
+
+@modifiable = @modifiable_direct | @event_accessor;
+
+@modifiable_direct = @member | @accessor | @local_function | @anonymous_function_expr;
+
+modifiers(
+ unique int id: @modifier,
+ string name: string ref);
+
+has_modifiers(
+ int id: @modifiable_direct ref,
+ int mod_id: @modifier ref);
+
+/** MEMBERS **/
+
+@member = @method | @constructor | @destructor | @field | @property | @event | @operator | @indexer | @type;
+
+@named_exprorstmt = @goto_stmt | @labeled_stmt | @expr;
+
+@virtualizable = @method | @property | @indexer | @event | @operator;
+
+exprorstmt_name(
+ unique int parent_id: @named_exprorstmt ref,
+ string name: string ref);
+
+nested_types(
+ unique int id: @type ref,
+ int declaring_type_id: @type ref,
+ int unbound_id: @type ref);
+
+properties(
+ unique int id: @property,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @property ref);
+
+property_location(
+ int id: @property ref,
+ int loc: @location ref);
+
+indexers(
+ unique int id: @indexer,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @indexer ref);
+
+indexer_location(
+ int id: @indexer ref,
+ int loc: @location ref);
+
+accessors(
+ unique int id: @accessor,
+ int kind: int ref,
+ string name: string ref,
+ int declaring_member_id: @member ref,
+ int unbound_id: @accessor ref);
+
+case @accessor.kind of
+ 1 = @getter
+| 2 = @setter
+ ;
+
+init_only_accessors(
+ unique int id: @accessor ref);
+
+accessor_location(
+ int id: @accessor ref,
+ int loc: @location ref);
+
+events(
+ unique int id: @event,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @event ref);
+
+event_location(
+ int id: @event ref,
+ int loc: @location ref);
+
+event_accessors(
+ unique int id: @event_accessor,
+ int kind: int ref,
+ string name: string ref,
+ int declaring_event_id: @event ref,
+ int unbound_id: @event_accessor ref);
+
+case @event_accessor.kind of
+ 1 = @add_event_accessor
+| 2 = @remove_event_accessor
+ ;
+
+event_accessor_location(
+ int id: @event_accessor ref,
+ int loc: @location ref);
+
+operators(
+ unique int id: @operator,
+ string name: string ref,
+ string symbol: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @operator ref);
+
+operator_location(
+ int id: @operator ref,
+ int loc: @location ref);
+
+constant_value(
+ int id: @variable ref,
+ string value: string ref);
+
+/** CALLABLES **/
+
+@callable = @method | @constructor | @destructor | @operator | @callable_accessor | @anonymous_function_expr | @local_function;
+
+@callable_accessor = @accessor | @event_accessor;
+
+methods(
+ unique int id: @method,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @method ref);
+
+method_location(
+ int id: @method ref,
+ int loc: @location ref);
+
+constructors(
+ unique int id: @constructor,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int unbound_id: @constructor ref);
+
+constructor_location(
+ int id: @constructor ref,
+ int loc: @location ref);
+
+destructors(
+ unique int id: @destructor,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int unbound_id: @destructor ref);
+
+destructor_location(
+ int id: @destructor ref,
+ int loc: @location ref);
+
+overrides(
+ int id: @callable ref,
+ int base_id: @callable ref);
+
+explicitly_implements(
+ int id: @member ref,
+ int interface_id: @interface_or_ref ref);
+
+local_functions(
+ unique int id: @local_function,
+ string name: string ref,
+ int return_type: @type ref,
+ int unbound_id: @local_function ref);
+
+local_function_stmts(
+ unique int fn: @local_function_stmt ref,
+ int stmt: @local_function ref);
+
+/** VARIABLES **/
+
+@variable = @local_scope_variable | @field;
+
+@local_scope_variable = @local_variable | @parameter;
+
+fields(
+ unique int id: @field,
+ int kind: int ref,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @field ref);
+
+case @field.kind of
+ 1 = @addressable_field
+| 2 = @constant
+ ;
+
+field_location(
+ int id: @field ref,
+ int loc: @location ref);
+
+localvars(
+ unique int id: @local_variable,
+ int kind: int ref,
+ string name: string ref,
+ int implicitly_typed: int ref /* 0 = no, 1 = yes */,
+ int type_id: @type_or_ref ref,
+ int parent_id: @local_var_decl_expr ref);
+
+case @local_variable.kind of
+ 1 = @addressable_local_variable
+| 2 = @local_constant
+| 3 = @local_variable_ref
+ ;
+
+localvar_location(
+ unique int id: @local_variable ref,
+ int loc: @location ref);
+
+@parameterizable = @callable | @delegate_type | @indexer | @function_pointer_type | @extension_type;
+
+#keyset[name, parent_id]
+#keyset[index, parent_id]
+params(
+ unique int id: @parameter,
+ string name: string ref,
+ int type_id: @type_or_ref ref,
+ int index: int ref,
+ int mode: int ref, /* value = 0, ref = 1, out = 2, params/array = 3, this = 4, in = 5, ref readonly = 6 */
+ int parent_id: @parameterizable ref,
+ int unbound_id: @parameter ref);
+
+param_location(
+ int id: @parameter ref,
+ int loc: @location ref);
+
+@has_scoped_annotation = @local_scope_variable
+
+scoped_annotation(
+ int id: @has_scoped_annotation ref,
+ int kind: int ref // scoped ref = 1, scoped value = 2
+ );
+
+/** STATEMENTS **/
+
+@exprorstmt_parent = @control_flow_element | @top_level_exprorstmt_parent;
+
+statements(
+ unique int id: @stmt,
+ int kind: int ref);
+
+#keyset[index, parent]
+stmt_parent(
+ unique int stmt: @stmt ref,
+ int index: int ref,
+ int parent: @control_flow_element ref);
+
+@top_level_stmt_parent = @callable;
+
+// [index, parent] is not a keyset because the same parent may be compiled multiple times
+stmt_parent_top_level(
+ unique int stmt: @stmt ref,
+ int index: int ref,
+ int parent: @top_level_stmt_parent ref);
+
+case @stmt.kind of
+ 1 = @block_stmt
+| 2 = @expr_stmt
+| 3 = @if_stmt
+| 4 = @switch_stmt
+| 5 = @while_stmt
+| 6 = @do_stmt
+| 7 = @for_stmt
+| 8 = @foreach_stmt
+| 9 = @break_stmt
+| 10 = @continue_stmt
+| 11 = @goto_stmt
+| 12 = @goto_case_stmt
+| 13 = @goto_default_stmt
+| 14 = @throw_stmt
+| 15 = @return_stmt
+| 16 = @yield_stmt
+| 17 = @try_stmt
+| 18 = @checked_stmt
+| 19 = @unchecked_stmt
+| 20 = @lock_stmt
+| 21 = @using_block_stmt
+| 22 = @var_decl_stmt
+| 23 = @const_decl_stmt
+| 24 = @empty_stmt
+| 25 = @unsafe_stmt
+| 26 = @fixed_stmt
+| 27 = @label_stmt
+| 28 = @catch
+| 29 = @case_stmt
+| 30 = @local_function_stmt
+| 31 = @using_decl_stmt
+ ;
+
+@using_stmt = @using_block_stmt | @using_decl_stmt;
+
+@labeled_stmt = @label_stmt | @case;
+
+@decl_stmt = @var_decl_stmt | @const_decl_stmt | @using_decl_stmt;
+
+@cond_stmt = @if_stmt | @switch_stmt;
+
+@loop_stmt = @while_stmt | @do_stmt | @for_stmt | @foreach_stmt;
+
+@jump_stmt = @break_stmt | @goto_any_stmt | @continue_stmt | @throw_stmt | @return_stmt
+ | @yield_stmt;
+
+@goto_any_stmt = @goto_default_stmt | @goto_case_stmt | @goto_stmt;
+
+
+stmt_location(
+ unique int id: @stmt ref,
+ int loc: @location ref);
+
+catch_type(
+ unique int catch_id: @catch ref,
+ int type_id: @type_or_ref ref,
+ int kind: int ref /* explicit = 1, implicit = 2 */);
+
+foreach_stmt_info(
+ unique int id: @foreach_stmt ref,
+ int kind: int ref /* non-async = 1, async = 2 */);
+
+@foreach_symbol = @method | @property | @type_or_ref;
+
+#keyset[id, kind]
+foreach_stmt_desugar(
+ int id: @foreach_stmt ref,
+ int symbol: @foreach_symbol ref,
+ int kind: int ref /* GetEnumeratorMethod = 1, CurrentProperty = 2, MoveNextMethod = 3, DisposeMethod = 4, ElementType = 5 */);
+
+/** EXPRESSIONS **/
+
+expressions(
+ unique int id: @expr,
+ int kind: int ref,
+ int type_id: @type_or_ref ref);
+
+#keyset[index, parent]
+expr_parent(
+ unique int expr: @expr ref,
+ int index: int ref,
+ int parent: @control_flow_element ref);
+
+@top_level_expr_parent = @attribute | @field | @property | @indexer | @parameter | @directive_if | @directive_elif;
+
+@top_level_exprorstmt_parent = @top_level_expr_parent | @top_level_stmt_parent;
+
+// [index, parent] is not a keyset because the same parent may be compiled multiple times
+expr_parent_top_level(
+ unique int expr: @expr ref,
+ int index: int ref,
+ int parent: @top_level_exprorstmt_parent ref);
+
+case @expr.kind of
+/* literal */
+ 1 = @bool_literal_expr
+| 2 = @char_literal_expr
+| 3 = @decimal_literal_expr
+| 4 = @int_literal_expr
+| 5 = @long_literal_expr
+| 6 = @uint_literal_expr
+| 7 = @ulong_literal_expr
+| 8 = @float_literal_expr
+| 9 = @double_literal_expr
+| 10 = @utf16_string_literal_expr
+| 11 = @null_literal_expr
+/* primary & unary */
+| 12 = @this_access_expr
+| 13 = @base_access_expr
+| 14 = @local_variable_access_expr
+| 15 = @parameter_access_expr
+| 16 = @field_access_expr
+| 17 = @property_access_expr
+| 18 = @method_access_expr
+| 19 = @event_access_expr
+| 20 = @indexer_access_expr
+| 21 = @array_access_expr
+| 22 = @type_access_expr
+| 23 = @typeof_expr
+| 24 = @method_invocation_expr
+| 25 = @delegate_invocation_expr
+| 26 = @operator_invocation_expr
+| 27 = @cast_expr
+| 28 = @object_creation_expr
+| 29 = @explicit_delegate_creation_expr
+| 30 = @implicit_delegate_creation_expr
+| 31 = @array_creation_expr
+| 32 = @default_expr
+| 33 = @plus_expr
+| 34 = @minus_expr
+| 35 = @bit_not_expr
+| 36 = @log_not_expr
+| 37 = @post_incr_expr
+| 38 = @post_decr_expr
+| 39 = @pre_incr_expr
+| 40 = @pre_decr_expr
+/* multiplicative */
+| 41 = @mul_expr
+| 42 = @div_expr
+| 43 = @rem_expr
+/* additive */
+| 44 = @add_expr
+| 45 = @sub_expr
+/* shift */
+| 46 = @lshift_expr
+| 47 = @rshift_expr
+/* relational */
+| 48 = @lt_expr
+| 49 = @gt_expr
+| 50 = @le_expr
+| 51 = @ge_expr
+/* equality */
+| 52 = @eq_expr
+| 53 = @ne_expr
+/* logical */
+| 54 = @bit_and_expr
+| 55 = @bit_xor_expr
+| 56 = @bit_or_expr
+| 57 = @log_and_expr
+| 58 = @log_or_expr
+/* type testing */
+| 59 = @is_expr
+| 60 = @as_expr
+/* null coalescing */
+| 61 = @null_coalescing_expr
+/* conditional */
+| 62 = @conditional_expr
+/* assignment */
+| 63 = @simple_assign_expr
+| 64 = @assign_add_expr
+| 65 = @assign_sub_expr
+| 66 = @assign_mul_expr
+| 67 = @assign_div_expr
+| 68 = @assign_rem_expr
+| 69 = @assign_and_expr
+| 70 = @assign_xor_expr
+| 71 = @assign_or_expr
+| 72 = @assign_lshift_expr
+| 73 = @assign_rshift_expr
+/* more */
+| 74 = @object_init_expr
+| 75 = @collection_init_expr
+| 76 = @array_init_expr
+| 77 = @checked_expr
+| 78 = @unchecked_expr
+| 79 = @constructor_init_expr
+| 80 = @add_event_expr
+| 81 = @remove_event_expr
+| 82 = @par_expr
+| 83 = @local_var_decl_expr
+| 84 = @lambda_expr
+| 85 = @anonymous_method_expr
+| 86 = @namespace_expr
+/* dynamic */
+| 92 = @dynamic_element_access_expr
+| 93 = @dynamic_member_access_expr
+/* unsafe */
+| 100 = @pointer_indirection_expr
+| 101 = @address_of_expr
+| 102 = @sizeof_expr
+/* async */
+| 103 = @await_expr
+/* C# 6.0 */
+| 104 = @nameof_expr
+| 105 = @interpolated_string_expr
+| 106 = @unknown_expr
+/* C# 7.0 */
+| 107 = @throw_expr
+| 108 = @tuple_expr
+| 109 = @local_function_invocation_expr
+| 110 = @ref_expr
+| 111 = @discard_expr
+/* C# 8.0 */
+| 112 = @range_expr
+| 113 = @index_expr
+| 114 = @switch_expr
+| 115 = @recursive_pattern_expr
+| 116 = @property_pattern_expr
+| 117 = @positional_pattern_expr
+| 118 = @switch_case_expr
+| 119 = @assign_coalesce_expr
+| 120 = @suppress_nullable_warning_expr
+| 121 = @namespace_access_expr
+/* C# 9.0 */
+| 122 = @lt_pattern_expr
+| 123 = @gt_pattern_expr
+| 124 = @le_pattern_expr
+| 125 = @ge_pattern_expr
+| 126 = @not_pattern_expr
+| 127 = @and_pattern_expr
+| 128 = @or_pattern_expr
+| 129 = @function_pointer_invocation_expr
+| 130 = @with_expr
+/* C# 11.0 */
+| 131 = @list_pattern_expr
+| 132 = @slice_pattern_expr
+| 133 = @urshift_expr
+| 134 = @assign_urshift_expr
+| 135 = @utf8_string_literal_expr
+/* C# 12.0 */
+| 136 = @collection_expr
+| 137 = @spread_element_expr
+| 138 = @interpolated_string_insert_expr
+/* Preprocessor */
+| 999 = @define_symbol_expr
+;
+
+@switch = @switch_stmt | @switch_expr;
+@case = @case_stmt | @switch_case_expr;
+@pattern_match = @case | @is_expr;
+@unary_pattern_expr = @not_pattern_expr;
+@relational_pattern_expr = @gt_pattern_expr | @lt_pattern_expr | @ge_pattern_expr | @le_pattern_expr;
+@binary_pattern_expr = @and_pattern_expr | @or_pattern_expr;
+
+@integer_literal_expr = @int_literal_expr | @long_literal_expr | @uint_literal_expr | @ulong_literal_expr;
+@real_literal_expr = @float_literal_expr | @double_literal_expr | @decimal_literal_expr;
+@string_literal_expr = @utf16_string_literal_expr | @utf8_string_literal_expr;
+@literal_expr = @bool_literal_expr | @char_literal_expr | @integer_literal_expr | @real_literal_expr
+ | @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_event_expr = @add_event_expr | @remove_event_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
+ | @assign_lshift_expr | @assign_rshift_expr | @assign_urshift_expr;
+
+@member_access_expr = @field_access_expr | @property_access_expr | @indexer_access_expr | @event_access_expr
+ | @method_access_expr | @type_access_expr | @dynamic_member_access_expr;
+@access_expr = @member_access_expr | @this_access_expr | @base_access_expr | @assignable_access_expr | @namespace_access_expr;
+@element_access_expr = @indexer_access_expr | @array_access_expr | @dynamic_element_access_expr;
+
+@local_variable_access = @local_variable_access_expr | @local_var_decl_expr;
+@local_scope_variable_access_expr = @parameter_access_expr | @local_variable_access;
+@variable_access_expr = @local_scope_variable_access_expr | @field_access_expr;
+
+@assignable_access_expr = @variable_access_expr | @property_access_expr | @element_access_expr
+ | @event_access_expr | @dynamic_member_access_expr;
+
+@objectorcollection_init_expr = @object_init_expr | @collection_init_expr;
+
+@delegate_creation_expr = @explicit_delegate_creation_expr | @implicit_delegate_creation_expr;
+
+@bin_arith_op_expr = @mul_expr | @div_expr | @rem_expr | @add_expr | @sub_expr;
+@incr_op_expr = @pre_incr_expr | @post_incr_expr;
+@decr_op_expr = @pre_decr_expr | @post_decr_expr;
+@mut_op_expr = @incr_op_expr | @decr_op_expr;
+@un_arith_op_expr = @plus_expr | @minus_expr | @mut_op_expr;
+@arith_op_expr = @bin_arith_op_expr | @un_arith_op_expr;
+
+@ternary_log_op_expr = @conditional_expr;
+@bin_log_op_expr = @log_and_expr | @log_or_expr | @null_coalescing_expr;
+@un_log_op_expr = @log_not_expr;
+@log_expr = @un_log_op_expr | @bin_log_op_expr | @ternary_log_op_expr;
+
+@bin_bit_op_expr = @bit_and_expr | @bit_or_expr | @bit_xor_expr | @lshift_expr
+ | @rshift_expr | @urshift_expr;
+@un_bit_op_expr = @bit_not_expr;
+@bit_expr = @un_bit_op_expr | @bin_bit_op_expr;
+
+@equality_op_expr = @eq_expr | @ne_expr;
+@rel_op_expr = @gt_expr | @lt_expr| @ge_expr | @le_expr;
+@comp_expr = @equality_op_expr | @rel_op_expr;
+
+@op_expr = @un_op | @bin_op | @ternary_op;
+
+@ternary_op = @ternary_log_op_expr;
+@bin_op = @assign_expr | @bin_arith_op_expr | @bin_log_op_expr | @bin_bit_op_expr | @comp_expr;
+@un_op = @un_arith_op_expr | @un_log_op_expr | @un_bit_op_expr | @sizeof_expr
+ | @pointer_indirection_expr | @address_of_expr;
+
+@anonymous_function_expr = @lambda_expr | @anonymous_method_expr;
+
+@call = @method_invocation_expr | @constructor_init_expr | @operator_invocation_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;
+
+@throw_element = @throw_expr | @throw_stmt;
+
+@implicitly_typeable_object_creation_expr = @object_creation_expr | @explicit_delegate_creation_expr;
+
+implicitly_typed_array_creation(
+ unique int id: @array_creation_expr ref);
+
+explicitly_sized_array_creation(
+ unique int id: @array_creation_expr ref);
+
+stackalloc_array_creation(
+ unique int id: @array_creation_expr ref);
+
+implicitly_typed_object_creation(
+ unique int id: @implicitly_typeable_object_creation_expr ref);
+
+mutator_invocation_mode(
+ unique int id: @operator_invocation_expr ref,
+ int mode: int ref /* prefix = 1, postfix = 2*/);
+
+expr_value(
+ unique int id: @expr ref,
+ string value: string ref);
+
+expr_call(
+ unique int caller_id: @expr ref,
+ int target_id: @callable ref);
+
+expr_access(
+ unique int accesser_id: @access_expr ref,
+ int target_id: @accessible ref);
+
+@accessible = @method | @assignable | @local_function | @namespace;
+
+expr_location(
+ unique int id: @expr ref,
+ int loc: @location ref);
+
+dynamic_member_name(
+ unique int id: @late_bindable_expr ref,
+ string name: string ref);
+
+@qualifiable_expr = @member_access_expr
+ | @method_invocation_expr
+ | @element_access_expr;
+
+conditional_access(
+ unique int id: @qualifiable_expr ref);
+
+expr_argument(
+ unique int id: @expr ref,
+ int mode: int ref);
+ /* mode is the same as params: value = 0, ref = 1, out = 2 */
+
+expr_argument_name(
+ unique int id: @expr ref,
+ string name: string ref);
+
+lambda_expr_return_type(
+ unique int id: @lambda_expr ref,
+ int type_id: @type_or_ref ref);
+
+/* Compiler generated */
+
+compiler_generated(unique int id: @element ref);
+
+/** CONTROL/DATA FLOW **/
+
+@control_flow_element = @stmt | @expr;
+
+/* XML Files */
+
+xmlEncoding (
+ unique int id: @file ref,
+ string encoding: string ref);
+
+xmlDTDs(
+ unique int id: @xmldtd,
+ string root: string ref,
+ string publicId: string ref,
+ string systemId: string ref,
+ int fileid: @file ref);
+
+xmlElements(
+ unique int id: @xmlelement,
+ string name: string ref,
+ int parentid: @xmlparent ref,
+ int idx: int ref,
+ int fileid: @file ref);
+
+xmlAttrs(
+ unique int id: @xmlattribute,
+ int elementid: @xmlelement ref,
+ string name: string ref,
+ string value: string ref,
+ int idx: int ref,
+ int fileid: @file ref);
+
+xmlNs(
+ int id: @xmlnamespace,
+ string prefixName: string ref,
+ string URI: string ref,
+ int fileid: @file ref);
+
+xmlHasNs(
+ int elementId: @xmlnamespaceable ref,
+ int nsId: @xmlnamespace ref,
+ int fileid: @file ref);
+
+xmlComments(
+ unique int id: @xmlcomment,
+ string text: string ref,
+ int parentid: @xmlparent ref,
+ int fileid: @file ref);
+
+xmlChars(
+ unique int id: @xmlcharacters,
+ string text: string ref,
+ int parentid: @xmlparent ref,
+ int idx: int ref,
+ int isCDATA: int ref,
+ int fileid: @file ref);
+
+@xmlparent = @file | @xmlelement;
+@xmlnamespaceable = @xmlelement | @xmlattribute;
+
+xmllocations(
+ int xmlElement: @xmllocatable ref,
+ int location: @location_default ref);
+
+@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace;
+
+/* Comments */
+
+commentline(
+ unique int id: @commentline,
+ int kind: int ref,
+ string text: string ref,
+ string rawtext: string ref);
+
+case @commentline.kind of
+ 0 = @singlelinecomment
+| 1 = @xmldoccomment
+| 2 = @multilinecomment;
+
+commentline_location(
+ unique int id: @commentline ref,
+ int loc: @location ref);
+
+commentblock(
+ unique int id : @commentblock);
+
+commentblock_location(
+ unique int id: @commentblock ref,
+ int loc: @location ref);
+
+commentblock_binding(
+ int id: @commentblock ref,
+ int entity: @element ref,
+ int bindtype: int ref); /* 0: Parent, 1: Best, 2: Before, 3: After */
+
+commentblock_child(
+ int id: @commentblock ref,
+ int commentline: @commentline ref,
+ int index: int ref);
+
+/* ASP.NET */
+
+case @asp_element.kind of
+ 0=@asp_close_tag
+| 1=@asp_code
+| 2=@asp_comment
+| 3=@asp_data_binding
+| 4=@asp_directive
+| 5=@asp_open_tag
+| 6=@asp_quoted_string
+| 7=@asp_text
+| 8=@asp_xml_directive;
+
+@asp_attribute = @asp_code | @asp_data_binding | @asp_quoted_string;
+
+asp_elements(
+ unique int id: @asp_element,
+ int kind: int ref,
+ int loc: @location ref);
+
+asp_comment_server(unique int comment: @asp_comment ref);
+asp_code_inline(unique int code: @asp_code ref);
+asp_directive_attribute(
+ int directive: @asp_directive ref,
+ int index: int ref,
+ string name: string ref,
+ int value: @asp_quoted_string ref);
+asp_directive_name(
+ unique int directive: @asp_directive ref,
+ string name: string ref);
+asp_element_body(
+ unique int element: @asp_element ref,
+ string body: string ref);
+asp_tag_attribute(
+ int tag: @asp_open_tag ref,
+ int index: int ref,
+ string name: string ref,
+ int attribute: @asp_attribute ref);
+asp_tag_name(
+ unique int tag: @asp_open_tag ref,
+ string name: string ref);
+asp_tag_isempty(int tag: @asp_open_tag ref);
diff --git a/csharp/ql/lib/upgrades/e73ca2c93df8aae162f1704edc4817a5cb330529/rotate_assignments.ql b/csharp/ql/lib/upgrades/e73ca2c93df8aae162f1704edc4817a5cb330529/rotate_assignments.ql
new file mode 100644
index 000000000000..644a25a06331
--- /dev/null
+++ b/csharp/ql/lib/upgrades/e73ca2c93df8aae162f1704edc4817a5cb330529/rotate_assignments.ql
@@ -0,0 +1,105 @@
+class Expr extends @expr {
+ string toString() { none() }
+}
+
+class ControlFlowElement extends @control_flow_element {
+ string toString() { none() }
+}
+
+class TypeOrRef extends @type_or_ref {
+ string toString() { none() }
+}
+
+class Callable extends @callable {
+ string toString() { none() }
+}
+
+class LateBindableExpr extends Expr, @late_bindable_expr { }
+
+predicate assignmentKind(int kind) {
+ // | 63 = @simple_assign_expr
+ // | 80 = @add_event_expr
+ // | 81 = @remove_event_expr
+ // | 83 = @local_var_decl_expr
+ kind = [63, 80, 81, 83]
+}
+
+predicate compoundAssignmentKind(int kind) {
+ // | 64 = @assign_add_expr
+ // | 65 = @assign_sub_expr
+ // | 66 = @assign_mul_expr
+ // | 67 = @assign_div_expr
+ // | 68 = @assign_rem_expr
+ // | 69 = @assign_and_expr
+ // | 70 = @assign_xor_expr
+ // | 71 = @assign_or_expr
+ // | 72 = @assign_lshift_expr
+ // | 73 = @assign_rshift_expr
+ // | 119 = @assign_coalesce_expr
+ kind = [64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 119]
+}
+
+predicate isAssignment(ControlFlowElement cfe) {
+ exists(int kind | assignmentKind(kind) |
+ expressions(cfe, kind, _) and
+ // Exclude assignments that are part of a compound assignment. These are handled seperatly.
+ not exists(Expr e | isCompoundAssignment(e) and expr_parent(cfe, 2, e))
+ )
+}
+
+predicate isCompoundAssignment(ControlFlowElement cfe) {
+ exists(int kind | compoundAssignmentKind(kind) | expressions(cfe, kind, _))
+}
+
+Expr getOperatorCall(Expr e) {
+ isCompoundAssignment(e) and
+ exists(Expr assignment |
+ expr_parent(assignment, 2, e) and
+ expr_parent(result, 0, assignment)
+ )
+}
+
+query predicate new_expr_parent(Expr e, int child, ControlFlowElement parent) {
+ // Swap children for assignments, local variable declarations and add/remove event.
+ if isAssignment(parent)
+ then
+ child = 0 and expr_parent(e, 1, parent)
+ or
+ child = 1 and expr_parent(e, 0, parent)
+ else (
+ // Case for compound assignments. The parent child relation is contracted.
+ exists(Expr op | op = getOperatorCall(parent) |
+ // Swap children.
+ child = 0 and expr_parent(e, 1, op)
+ or
+ child = 1 and expr_parent(e, 0, op)
+ )
+ or
+ expr_parent(e, child, parent) and not exists(getOperatorCall(parent))
+ )
+}
+
+query predicate new_expressions(Expr e, int kind, TypeOrRef t) {
+ expressions(e, kind, t) and
+ // Remove the unused expanded assignment expressions.
+ not exists(ControlFlowElement parent, Expr assignment |
+ expr_parent(assignment, 2, parent) and
+ isCompoundAssignment(parent)
+ |
+ e = assignment or
+ expr_parent(e, 0, assignment) or
+ expr_parent(e, 1, assignment)
+ )
+}
+
+query predicate new_expr_call(Expr e, Callable c) {
+ exists(Expr op | op = getOperatorCall(e) | expr_call(op, c))
+ or
+ expr_call(e, c) and not e = getOperatorCall(_)
+}
+
+query predicate new_dynamic_member_name(LateBindableExpr e, string name) {
+ exists(Expr op | op = getOperatorCall(e) | dynamic_member_name(op, name))
+ or
+ dynamic_member_name(e, name) and not e = getOperatorCall(_)
+}
diff --git a/csharp/ql/lib/upgrades/e73ca2c93df8aae162f1704edc4817a5cb330529/semmlecode.csharp.dbscheme b/csharp/ql/lib/upgrades/e73ca2c93df8aae162f1704edc4817a5cb330529/semmlecode.csharp.dbscheme
new file mode 100644
index 000000000000..19b8cc3e2dc7
--- /dev/null
+++ b/csharp/ql/lib/upgrades/e73ca2c93df8aae162f1704edc4817a5cb330529/semmlecode.csharp.dbscheme
@@ -0,0 +1,1504 @@
+/* This is a dummy line to alter the dbscheme, so we can make a database upgrade
+ * without actually changing any of the dbscheme predicates. It contains a date
+ * to allow for such updates in the future as well.
+ *
+ * 2021-07-14
+ *
+ * DO NOT remove this comment carelessly, since it can revert the dbscheme back to a
+ * previously seen state (matching a previously seen SHA), which would make the upgrade
+ * mechanism not work properly.
+ */
+
+/**
+ * An invocation of the compiler. Note that more than one file may be
+ * compiled per invocation. For example, this command compiles three
+ * source files:
+ *
+ * csc f1.cs f2.cs f3.cs
+ *
+ * The `id` simply identifies the invocation, while `cwd` is the working
+ * directory from which the compiler was invoked.
+ */
+compilations(
+ unique int id : @compilation,
+ string cwd : string ref
+);
+
+compilation_info(
+ int id : @compilation ref,
+ string info_key: string ref,
+ string info_value: string ref
+)
+
+/**
+ * The arguments that were passed to the extractor for a compiler
+ * invocation. If `id` is for the compiler invocation
+ *
+ * csc f1.cs f2.cs f3.cs
+ *
+ * then typically there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | --compiler
+ * 1 | *path to compiler*
+ * 2 | f1.cs
+ * 3 | f2.cs
+ * 4 | f3.cs
+ */
+#keyset[id, num]
+compilation_args(
+ int id : @compilation ref,
+ int num : int ref,
+ string arg : string ref
+);
+
+/**
+ * The expanded arguments that were passed to the extractor for a
+ * compiler invocation. This is similar to `compilation_args`, but
+ * for a `@someFile.rsp` argument, it includes the arguments from that
+ * file, rather than just taking the argument literally.
+ */
+#keyset[id, num]
+compilation_expanded_args(
+ int id : @compilation ref,
+ int num : int ref,
+ string arg : string ref
+);
+
+/**
+ * The source files that are compiled by a compiler invocation.
+ * If `id` is for the compiler invocation
+ *
+ * csc f1.cs f2.cs f3.cs
+ *
+ * then there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | f1.cs
+ * 1 | f2.cs
+ * 2 | f3.cs
+ */
+#keyset[id, num]
+compilation_compiling_files(
+ int id : @compilation ref,
+ int num : int ref,
+ int file : @file ref
+);
+
+/**
+ * The references used by a compiler invocation.
+ * If `id` is for the compiler invocation
+ *
+ * csc f1.cs f2.cs f3.cs /r:ref1.dll /r:ref2.dll /r:ref3.dll
+ *
+ * then there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | ref1.dll
+ * 1 | ref2.dll
+ * 2 | ref3.dll
+ */
+#keyset[id, num]
+compilation_referencing_files(
+ int id : @compilation ref,
+ int num : int ref,
+ int file : @file ref
+);
+
+/**
+ * The time taken by the extractor for a compiler invocation.
+ *
+ * For each file `num`, there will be rows for
+ *
+ * kind | seconds
+ * ---- | ---
+ * 1 | CPU seconds used by the extractor frontend
+ * 2 | Elapsed seconds during the extractor frontend
+ * 3 | CPU seconds used by the extractor backend
+ * 4 | Elapsed seconds during the extractor backend
+ */
+#keyset[id, num, kind]
+compilation_time(
+ int id : @compilation ref,
+ int num : int ref,
+ /* kind:
+ 1 = frontend_cpu_seconds
+ 2 = frontend_elapsed_seconds
+ 3 = extractor_cpu_seconds
+ 4 = extractor_elapsed_seconds
+ */
+ int kind : int ref,
+ float seconds : float ref
+);
+
+/**
+ * An error or warning generated by the extractor.
+ * The diagnostic message `diagnostic` was generated during compiler
+ * invocation `compilation`, and is the `file_number_diagnostic_number`th
+ * message generated while extracting the `file_number`th file of that
+ * invocation.
+ */
+#keyset[compilation, file_number, file_number_diagnostic_number]
+diagnostic_for(
+ unique int diagnostic : @diagnostic ref,
+ int compilation : @compilation ref,
+ int file_number : int ref,
+ int file_number_diagnostic_number : int ref
+);
+
+diagnostics(
+ unique int id: @diagnostic,
+ int severity: int ref,
+ string error_tag: string ref,
+ string error_message: string ref,
+ string full_error_message: string ref,
+ int location: @location ref
+);
+
+extractor_messages(
+ unique int id: @extractor_message,
+ int severity: int ref,
+ string origin : string ref,
+ string text : string ref,
+ string entity : string ref,
+ int location: @location ref,
+ string stack_trace : string ref
+);
+
+/**
+ * If extraction was successful, then `cpu_seconds` and
+ * `elapsed_seconds` are the CPU time and elapsed time (respectively)
+ * that extraction took for compiler invocation `id`.
+ */
+compilation_finished(
+ unique int id : @compilation ref,
+ float cpu_seconds : float ref,
+ float elapsed_seconds : float ref
+);
+
+compilation_assembly(
+ unique int id : @compilation ref,
+ int assembly: @assembly ref
+)
+
+// Populated by the CSV extractor
+externalData(
+ int id: @externalDataElement,
+ string path: string ref,
+ int column: int ref,
+ string value: string ref);
+
+sourceLocationPrefix(
+ string prefix: string ref);
+
+/*
+ * Overlay support
+ */
+
+/**
+ * The CLI will automatically emit the tuple `databaseMetadata("isOverlay", "true")`,
+ * along with an `overlayChangedFiles` tuple for each new/modified/deleted file,
+ * when building an overlay database, and these can be used by the discard predicates.
+ */
+databaseMetadata(
+ string metadataKey : string ref,
+ string value : string ref
+);
+
+overlayChangedFiles(
+ string path : string ref
+);
+
+/*
+ * C# dbscheme
+ */
+
+/** ELEMENTS **/
+
+@element = @declaration | @stmt | @expr | @modifier | @attribute | @namespace_declaration
+ | @using_directive | @type_parameter_constraints | @externalDataElement
+ | @xmllocatable | @asp_element | @namespace | @preprocessor_directive;
+
+@declaration = @callable | @generic | @assignable | @namespace;
+
+@named_element = @namespace | @declaration;
+
+@declaration_with_accessors = @property | @indexer | @event;
+
+@assignable = @variable | @assignable_with_accessors | @event;
+
+@assignable_with_accessors = @property | @indexer;
+
+@attributable = @assembly | @field | @parameter | @operator | @method | @constructor
+ | @destructor | @callable_accessor | @value_or_ref_type | @declaration_with_accessors
+ | @local_function | @lambda_expr;
+
+/** LOCATIONS, ASEMMBLIES, MODULES, FILES and FOLDERS **/
+
+@location = @location_default | @assembly;
+
+@locatable = @declaration_with_accessors | @callable_accessor | @declaration_or_directive
+ | @diagnostic | @extractor_message | @preprocessor_directive | @attribute | @type_mention | @type_parameter_constraints
+ | @declaration_with_accessors | @callable_accessor | @operator | @method
+ | @constructor | @destructor | @field | @local_variable | @parameter | @stmt | @expr
+ | @xmllocatable | @commentline | @commentblock | @asp_element
+
+locations_default(
+ unique int id: @location_default,
+ int file: @file ref,
+ int beginLine: int ref,
+ int beginColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref);
+
+locations_mapped(
+ unique int id: @location_default ref,
+ int mapped_to: @location_default ref);
+
+@sourceline = @file | @callable | @xmllocatable;
+
+numlines(
+ int element_id: @sourceline ref,
+ int num_lines: int ref,
+ int num_code: int ref,
+ int num_comment: int ref);
+
+assemblies(
+ unique int id: @assembly,
+ int file: @file ref,
+ string fullname: string ref,
+ string name: string ref,
+ string version: string ref);
+
+files(
+ unique int id: @file,
+ string name: string ref);
+
+folders(
+ unique int id: @folder,
+ string name: string ref);
+
+@container = @folder | @file ;
+
+containerparent(
+ int parent: @container ref,
+ unique int child: @container ref);
+
+file_extraction_mode(
+ unique int file: @file ref,
+ int mode: int ref
+ /* 0 = normal, 1 = standalone extractor */
+ );
+
+/** NAMESPACES **/
+
+@type_container = @namespace | @type;
+
+namespaces(
+ unique int id: @namespace,
+ string name: string ref);
+
+namespace_declarations(
+ unique int id: @namespace_declaration,
+ int namespace_id: @namespace ref);
+
+namespace_declaration_location(
+ unique int id: @namespace_declaration ref,
+ int loc: @location ref);
+
+parent_namespace(
+ unique int child_id: @type_container ref,
+ int namespace_id: @namespace ref);
+
+@declaration_or_directive = @namespace_declaration | @type | @using_directive;
+
+parent_namespace_declaration(
+ int child_id: @declaration_or_directive ref, // cannot be unique because of partial classes
+ int namespace_id: @namespace_declaration ref);
+
+@using_directive = @using_namespace_directive | @using_static_directive;
+
+using_global(
+ unique int id: @using_directive ref
+);
+
+using_namespace_directives(
+ unique int id: @using_namespace_directive,
+ int namespace_id: @namespace ref);
+
+using_static_directives(
+ unique int id: @using_static_directive,
+ int type_id: @type_or_ref ref);
+
+using_directive_location(
+ unique int id: @using_directive ref,
+ int loc: @location ref);
+
+@preprocessor_directive = @pragma_warning | @pragma_checksum | @directive_define | @directive_undefine | @directive_warning
+ | @directive_error | @directive_nullable | @directive_line | @directive_region | @directive_endregion | @directive_if
+ | @directive_elif | @directive_else | @directive_endif;
+
+@conditional_directive = @directive_if | @directive_elif;
+@branch_directive = @directive_if | @directive_elif | @directive_else;
+
+directive_ifs(
+ unique int id: @directive_if,
+ int branchTaken: int ref, /* 0: false, 1: true */
+ int conditionValue: int ref); /* 0: false, 1: true */
+
+directive_elifs(
+ unique int id: @directive_elif,
+ int branchTaken: int ref, /* 0: false, 1: true */
+ int conditionValue: int ref, /* 0: false, 1: true */
+ int parent: @directive_if ref,
+ int index: int ref);
+
+directive_elses(
+ unique int id: @directive_else,
+ int branchTaken: int ref, /* 0: false, 1: true */
+ int parent: @directive_if ref,
+ int index: int ref);
+
+#keyset[id, start]
+directive_endifs(
+ unique int id: @directive_endif,
+ unique int start: @directive_if ref);
+
+directive_define_symbols(
+ unique int id: @define_symbol_expr ref,
+ string name: string ref);
+
+directive_regions(
+ unique int id: @directive_region,
+ string name: string ref);
+
+#keyset[id, start]
+directive_endregions(
+ unique int id: @directive_endregion,
+ unique int start: @directive_region ref);
+
+directive_lines(
+ unique int id: @directive_line,
+ int kind: int ref); /* 0: default, 1: hidden, 2: numeric, 3: span */
+
+directive_line_value(
+ unique int id: @directive_line ref,
+ int line: int ref);
+
+directive_line_file(
+ unique int id: @directive_line ref,
+ int file: @file ref);
+
+directive_line_offset(
+ unique int id: @directive_line ref,
+ int offset: int ref);
+
+directive_line_span(
+ unique int id: @directive_line ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref);
+
+directive_nullables(
+ unique int id: @directive_nullable,
+ int setting: int ref, /* 0: disable, 1: enable, 2: restore */
+ int target: int ref); /* 0: none, 1: annotations, 2: warnings */
+
+directive_warnings(
+ unique int id: @directive_warning,
+ string message: string ref);
+
+directive_errors(
+ unique int id: @directive_error,
+ string message: string ref);
+
+directive_undefines(
+ unique int id: @directive_undefine,
+ string name: string ref);
+
+directive_defines(
+ unique int id: @directive_define,
+ string name: string ref);
+
+pragma_checksums(
+ unique int id: @pragma_checksum,
+ int file: @file ref,
+ string guid: string ref,
+ string bytes: string ref);
+
+pragma_warnings(
+ unique int id: @pragma_warning,
+ int kind: int ref /* 0 = disable, 1 = restore */);
+
+#keyset[id, index]
+pragma_warning_error_codes(
+ int id: @pragma_warning ref,
+ string errorCode: string ref,
+ int index: int ref);
+
+preprocessor_directive_location(
+ unique int id: @preprocessor_directive ref,
+ int loc: @location ref);
+
+preprocessor_directive_compilation(
+ int id: @preprocessor_directive ref,
+ int compilation: @compilation ref);
+
+preprocessor_directive_active(
+ unique int id: @preprocessor_directive ref,
+ int active: int ref); /* 0: false, 1: true */
+
+/** TYPES **/
+
+types(
+ unique int id: @type,
+ int kind: int ref,
+ string name: string ref);
+
+case @type.kind of
+ 1 = @bool_type
+| 2 = @char_type
+| 3 = @decimal_type
+| 4 = @sbyte_type
+| 5 = @short_type
+| 6 = @int_type
+| 7 = @long_type
+| 8 = @byte_type
+| 9 = @ushort_type
+| 10 = @uint_type
+| 11 = @ulong_type
+| 12 = @float_type
+| 13 = @double_type
+| 14 = @enum_type
+| 15 = @struct_type
+| 17 = @class_type
+| 19 = @interface_type
+| 20 = @delegate_type
+| 21 = @null_type
+| 22 = @type_parameter
+| 23 = @pointer_type
+| 24 = @nullable_type
+| 25 = @array_type
+| 26 = @void_type
+| 27 = @int_ptr_type
+| 28 = @uint_ptr_type
+| 29 = @dynamic_type
+| 30 = @arglist_type
+| 31 = @unknown_type
+| 32 = @tuple_type
+| 33 = @function_pointer_type
+| 34 = @inline_array_type
+| 35 = @extension_type
+ ;
+
+@simple_type = @bool_type | @char_type | @integral_type | @floating_point_type | @decimal_type;
+@integral_type = @signed_integral_type | @unsigned_integral_type;
+@signed_integral_type = @sbyte_type | @short_type | @int_type | @long_type;
+@unsigned_integral_type = @byte_type | @ushort_type | @uint_type | @ulong_type;
+@floating_point_type = @float_type | @double_type;
+@value_type = @simple_type | @enum_type | @struct_type | @nullable_type | @int_ptr_type
+ | @uint_ptr_type | @tuple_type | @void_type | @inline_array_type;
+@ref_type = @class_type | @interface_type | @array_type | @delegate_type | @null_type
+ | @dynamic_type | @extension_type;
+@value_or_ref_type = @value_type | @ref_type;
+
+typerefs(
+ unique int id: @typeref,
+ string name: string ref);
+
+typeref_type(
+ int id: @typeref ref,
+ unique int typeId: @type ref);
+
+@type_or_ref = @type | @typeref;
+
+array_element_type(
+ unique int array: @array_type ref,
+ int dimension: int ref,
+ int rank: int ref,
+ int element: @type_or_ref ref);
+
+nullable_underlying_type(
+ unique int nullable: @nullable_type ref,
+ int underlying: @type_or_ref ref);
+
+pointer_referent_type(
+ unique int pointer: @pointer_type ref,
+ int referent: @type_or_ref ref);
+
+enum_underlying_type(
+ unique int enum_id: @enum_type ref,
+ int underlying_type_id: @type_or_ref ref);
+
+delegate_return_type(
+ unique int delegate_id: @delegate_type ref,
+ int return_type_id: @type_or_ref ref);
+
+function_pointer_return_type(
+ unique int function_pointer_id: @function_pointer_type ref,
+ int return_type_id: @type_or_ref ref);
+
+extension_receiver_type(
+ unique int extension: @extension_type ref,
+ int receiver_type_id: @type_or_ref ref);
+
+extend(
+ int sub: @type ref,
+ int super: @type_or_ref ref);
+
+anonymous_types(
+ unique int id: @type ref);
+
+@interface_or_ref = @interface_type | @typeref;
+
+implement(
+ int sub: @type ref,
+ int super: @type_or_ref ref);
+
+type_location(
+ int id: @type ref,
+ int loc: @location ref);
+
+tuple_underlying_type(
+ unique int tuple: @tuple_type ref,
+ int struct: @type_or_ref ref);
+
+#keyset[tuple, index]
+tuple_element(
+ int tuple: @tuple_type ref,
+ int index: int ref,
+ unique int field: @field ref);
+
+attributes(
+ unique int id: @attribute,
+ int kind: int ref,
+ int type_id: @type_or_ref ref,
+ int target: @attributable ref);
+
+case @attribute.kind of
+ 0 = @attribute_default
+| 1 = @attribute_return
+| 2 = @attribute_assembly
+| 3 = @attribute_module
+;
+
+attribute_location(
+ int id: @attribute ref,
+ int loc: @location ref);
+
+@type_mention_parent = @element | @type_mention;
+
+type_mention(
+ unique int id: @type_mention,
+ int type_id: @type_or_ref ref,
+ int parent: @type_mention_parent ref);
+
+type_mention_location(
+ unique int id: @type_mention ref,
+ int loc: @location ref);
+
+@has_type_annotation = @assignable | @type_parameter | @callable | @expr | @delegate_type | @generic | @function_pointer_type;
+
+/**
+ * A direct annotation on an entity, for example `string? x;`.
+ *
+ * Annotations:
+ * 2 = reftype is not annotated "!"
+ * 3 = reftype is annotated "?"
+ * 4 = readonly ref type / in parameter
+ * 5 = ref type parameter, return or local variable
+ * 6 = out parameter
+ *
+ * Note that the annotation depends on the element it annotates.
+ * @assignable: The annotation is on the type of the assignable, for example the variable type.
+ * @type_parameter: The annotation is on the reftype constraint
+ * @callable: The annotation is on the return type
+ * @array_type: The annotation is on the element type
+ */
+type_annotation(int id: @has_type_annotation ref, int annotation: int ref);
+
+nullability(unique int nullability: @nullability, int kind: int ref);
+
+case @nullability.kind of
+ 0 = @oblivious
+| 1 = @not_annotated
+| 2 = @annotated
+;
+
+#keyset[parent, index]
+nullability_parent(int nullability: @nullability ref, int index: int ref, int parent: @nullability ref)
+
+type_nullability(int id: @has_type_annotation ref, int nullability: @nullability ref);
+
+/**
+ * The nullable flow state of an expression, as determined by Roslyn.
+ * 0 = none (default, not populated)
+ * 1 = not null
+ * 2 = maybe null
+ */
+expr_flowstate(unique int id: @expr ref, int state: int ref);
+
+/** GENERICS **/
+
+@generic = @type | @method | @local_function;
+
+type_parameters(
+ unique int id: @type_parameter ref,
+ int index: int ref,
+ int generic_id: @generic ref,
+ int variance: int ref /* none = 0, out = 1, in = 2 */);
+
+#keyset[constructed_id, index]
+type_arguments(
+ int id: @type_or_ref ref,
+ int index: int ref,
+ int constructed_id: @generic_or_ref ref);
+
+@generic_or_ref = @generic | @typeref;
+
+constructed_generic(
+ unique int constructed: @generic ref,
+ int generic: @generic_or_ref ref);
+
+type_parameter_constraints(
+ unique int id: @type_parameter_constraints,
+ int param_id: @type_parameter ref);
+
+type_parameter_constraints_location(
+ int id: @type_parameter_constraints ref,
+ int loc: @location ref);
+
+general_type_parameter_constraints(
+ int id: @type_parameter_constraints ref,
+ int kind: int ref /* class = 1, struct = 2, new = 3 */);
+
+specific_type_parameter_constraints(
+ int id: @type_parameter_constraints ref,
+ int base_id: @type_or_ref ref);
+
+specific_type_parameter_nullability(
+ int id: @type_parameter_constraints ref,
+ int base_id: @type_or_ref ref,
+ int nullability: @nullability ref);
+
+/** FUNCTION POINTERS */
+
+function_pointer_calling_conventions(
+ int id: @function_pointer_type ref,
+ int kind: int ref);
+
+#keyset[id, index]
+has_unmanaged_calling_conventions(
+ int id: @function_pointer_type ref,
+ int index: int ref,
+ int conv_id: @type_or_ref ref);
+
+/** MODIFIERS */
+
+@modifiable = @modifiable_direct | @event_accessor;
+
+@modifiable_direct = @member | @accessor | @local_function | @anonymous_function_expr;
+
+modifiers(
+ unique int id: @modifier,
+ string name: string ref);
+
+has_modifiers(
+ int id: @modifiable_direct ref,
+ int mod_id: @modifier ref);
+
+/** MEMBERS **/
+
+@member = @method | @constructor | @destructor | @field | @property | @event | @operator | @indexer | @type;
+
+@named_exprorstmt = @goto_stmt | @labeled_stmt | @expr;
+
+@virtualizable = @method | @property | @indexer | @event | @operator;
+
+exprorstmt_name(
+ unique int parent_id: @named_exprorstmt ref,
+ string name: string ref);
+
+nested_types(
+ unique int id: @type ref,
+ int declaring_type_id: @type ref,
+ int unbound_id: @type ref);
+
+properties(
+ unique int id: @property,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @property ref);
+
+property_location(
+ int id: @property ref,
+ int loc: @location ref);
+
+indexers(
+ unique int id: @indexer,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @indexer ref);
+
+indexer_location(
+ int id: @indexer ref,
+ int loc: @location ref);
+
+accessors(
+ unique int id: @accessor,
+ int kind: int ref,
+ string name: string ref,
+ int declaring_member_id: @member ref,
+ int unbound_id: @accessor ref);
+
+case @accessor.kind of
+ 1 = @getter
+| 2 = @setter
+ ;
+
+init_only_accessors(
+ unique int id: @accessor ref);
+
+accessor_location(
+ int id: @accessor ref,
+ int loc: @location ref);
+
+events(
+ unique int id: @event,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @event ref);
+
+event_location(
+ int id: @event ref,
+ int loc: @location ref);
+
+event_accessors(
+ unique int id: @event_accessor,
+ int kind: int ref,
+ string name: string ref,
+ int declaring_event_id: @event ref,
+ int unbound_id: @event_accessor ref);
+
+case @event_accessor.kind of
+ 1 = @add_event_accessor
+| 2 = @remove_event_accessor
+ ;
+
+event_accessor_location(
+ int id: @event_accessor ref,
+ int loc: @location ref);
+
+operators(
+ unique int id: @operator,
+ string name: string ref,
+ string symbol: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @operator ref);
+
+operator_location(
+ int id: @operator ref,
+ int loc: @location ref);
+
+constant_value(
+ int id: @variable ref,
+ string value: string ref);
+
+/** CALLABLES **/
+
+@callable = @method | @constructor | @destructor | @operator | @callable_accessor | @anonymous_function_expr | @local_function;
+
+@callable_accessor = @accessor | @event_accessor;
+
+methods(
+ unique int id: @method,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @method ref);
+
+method_location(
+ int id: @method ref,
+ int loc: @location ref);
+
+constructors(
+ unique int id: @constructor,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int unbound_id: @constructor ref);
+
+constructor_location(
+ int id: @constructor ref,
+ int loc: @location ref);
+
+destructors(
+ unique int id: @destructor,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int unbound_id: @destructor ref);
+
+destructor_location(
+ int id: @destructor ref,
+ int loc: @location ref);
+
+overrides(
+ int id: @callable ref,
+ int base_id: @callable ref);
+
+explicitly_implements(
+ int id: @member ref,
+ int interface_id: @interface_or_ref ref);
+
+local_functions(
+ unique int id: @local_function,
+ string name: string ref,
+ int return_type: @type ref,
+ int unbound_id: @local_function ref);
+
+local_function_stmts(
+ unique int fn: @local_function_stmt ref,
+ int stmt: @local_function ref);
+
+/** VARIABLES **/
+
+@variable = @local_scope_variable | @field;
+
+@local_scope_variable = @local_variable | @parameter;
+
+fields(
+ unique int id: @field,
+ int kind: int ref,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @field ref);
+
+case @field.kind of
+ 1 = @addressable_field
+| 2 = @constant
+ ;
+
+field_location(
+ int id: @field ref,
+ int loc: @location ref);
+
+localvars(
+ unique int id: @local_variable,
+ int kind: int ref,
+ string name: string ref,
+ int implicitly_typed: int ref /* 0 = no, 1 = yes */,
+ int type_id: @type_or_ref ref,
+ int parent_id: @local_var_decl_expr ref);
+
+case @local_variable.kind of
+ 1 = @addressable_local_variable
+| 2 = @local_constant
+| 3 = @local_variable_ref
+ ;
+
+localvar_location(
+ unique int id: @local_variable ref,
+ int loc: @location ref);
+
+@parameterizable = @callable | @delegate_type | @indexer | @function_pointer_type | @extension_type;
+
+#keyset[name, parent_id]
+#keyset[index, parent_id]
+params(
+ unique int id: @parameter,
+ string name: string ref,
+ int type_id: @type_or_ref ref,
+ int index: int ref,
+ int mode: int ref, /* value = 0, ref = 1, out = 2, params/array = 3, this = 4, in = 5, ref readonly = 6 */
+ int parent_id: @parameterizable ref,
+ int unbound_id: @parameter ref);
+
+param_location(
+ int id: @parameter ref,
+ int loc: @location ref);
+
+@has_scoped_annotation = @local_scope_variable
+
+scoped_annotation(
+ int id: @has_scoped_annotation ref,
+ int kind: int ref // scoped ref = 1, scoped value = 2
+ );
+
+/** STATEMENTS **/
+
+@exprorstmt_parent = @control_flow_element | @top_level_exprorstmt_parent;
+
+statements(
+ unique int id: @stmt,
+ int kind: int ref);
+
+#keyset[index, parent]
+stmt_parent(
+ unique int stmt: @stmt ref,
+ int index: int ref,
+ int parent: @control_flow_element ref);
+
+@top_level_stmt_parent = @callable;
+
+// [index, parent] is not a keyset because the same parent may be compiled multiple times
+stmt_parent_top_level(
+ unique int stmt: @stmt ref,
+ int index: int ref,
+ int parent: @top_level_stmt_parent ref);
+
+case @stmt.kind of
+ 1 = @block_stmt
+| 2 = @expr_stmt
+| 3 = @if_stmt
+| 4 = @switch_stmt
+| 5 = @while_stmt
+| 6 = @do_stmt
+| 7 = @for_stmt
+| 8 = @foreach_stmt
+| 9 = @break_stmt
+| 10 = @continue_stmt
+| 11 = @goto_stmt
+| 12 = @goto_case_stmt
+| 13 = @goto_default_stmt
+| 14 = @throw_stmt
+| 15 = @return_stmt
+| 16 = @yield_stmt
+| 17 = @try_stmt
+| 18 = @checked_stmt
+| 19 = @unchecked_stmt
+| 20 = @lock_stmt
+| 21 = @using_block_stmt
+| 22 = @var_decl_stmt
+| 23 = @const_decl_stmt
+| 24 = @empty_stmt
+| 25 = @unsafe_stmt
+| 26 = @fixed_stmt
+| 27 = @label_stmt
+| 28 = @catch
+| 29 = @case_stmt
+| 30 = @local_function_stmt
+| 31 = @using_decl_stmt
+ ;
+
+@using_stmt = @using_block_stmt | @using_decl_stmt;
+
+@labeled_stmt = @label_stmt | @case;
+
+@decl_stmt = @var_decl_stmt | @const_decl_stmt | @using_decl_stmt;
+
+@cond_stmt = @if_stmt | @switch_stmt;
+
+@loop_stmt = @while_stmt | @do_stmt | @for_stmt | @foreach_stmt;
+
+@jump_stmt = @break_stmt | @goto_any_stmt | @continue_stmt | @throw_stmt | @return_stmt
+ | @yield_stmt;
+
+@goto_any_stmt = @goto_default_stmt | @goto_case_stmt | @goto_stmt;
+
+
+stmt_location(
+ unique int id: @stmt ref,
+ int loc: @location ref);
+
+catch_type(
+ unique int catch_id: @catch ref,
+ int type_id: @type_or_ref ref,
+ int kind: int ref /* explicit = 1, implicit = 2 */);
+
+foreach_stmt_info(
+ unique int id: @foreach_stmt ref,
+ int kind: int ref /* non-async = 1, async = 2 */);
+
+@foreach_symbol = @method | @property | @type_or_ref;
+
+#keyset[id, kind]
+foreach_stmt_desugar(
+ int id: @foreach_stmt ref,
+ int symbol: @foreach_symbol ref,
+ int kind: int ref /* GetEnumeratorMethod = 1, CurrentProperty = 2, MoveNextMethod = 3, DisposeMethod = 4, ElementType = 5 */);
+
+/** EXPRESSIONS **/
+
+expressions(
+ unique int id: @expr,
+ int kind: int ref,
+ int type_id: @type_or_ref ref);
+
+#keyset[index, parent]
+expr_parent(
+ unique int expr: @expr ref,
+ int index: int ref,
+ int parent: @control_flow_element ref);
+
+@top_level_expr_parent = @attribute | @field | @property | @indexer | @parameter | @directive_if | @directive_elif;
+
+@top_level_exprorstmt_parent = @top_level_expr_parent | @top_level_stmt_parent;
+
+// [index, parent] is not a keyset because the same parent may be compiled multiple times
+expr_parent_top_level(
+ unique int expr: @expr ref,
+ int index: int ref,
+ int parent: @top_level_exprorstmt_parent ref);
+
+case @expr.kind of
+/* literal */
+ 1 = @bool_literal_expr
+| 2 = @char_literal_expr
+| 3 = @decimal_literal_expr
+| 4 = @int_literal_expr
+| 5 = @long_literal_expr
+| 6 = @uint_literal_expr
+| 7 = @ulong_literal_expr
+| 8 = @float_literal_expr
+| 9 = @double_literal_expr
+| 10 = @utf16_string_literal_expr
+| 11 = @null_literal_expr
+/* primary & unary */
+| 12 = @this_access_expr
+| 13 = @base_access_expr
+| 14 = @local_variable_access_expr
+| 15 = @parameter_access_expr
+| 16 = @field_access_expr
+| 17 = @property_access_expr
+| 18 = @method_access_expr
+| 19 = @event_access_expr
+| 20 = @indexer_access_expr
+| 21 = @array_access_expr
+| 22 = @type_access_expr
+| 23 = @typeof_expr
+| 24 = @method_invocation_expr
+| 25 = @delegate_invocation_expr
+| 26 = @operator_invocation_expr
+| 27 = @cast_expr
+| 28 = @object_creation_expr
+| 29 = @explicit_delegate_creation_expr
+| 30 = @implicit_delegate_creation_expr
+| 31 = @array_creation_expr
+| 32 = @default_expr
+| 33 = @plus_expr
+| 34 = @minus_expr
+| 35 = @bit_not_expr
+| 36 = @log_not_expr
+| 37 = @post_incr_expr
+| 38 = @post_decr_expr
+| 39 = @pre_incr_expr
+| 40 = @pre_decr_expr
+/* multiplicative */
+| 41 = @mul_expr
+| 42 = @div_expr
+| 43 = @rem_expr
+/* additive */
+| 44 = @add_expr
+| 45 = @sub_expr
+/* shift */
+| 46 = @lshift_expr
+| 47 = @rshift_expr
+/* relational */
+| 48 = @lt_expr
+| 49 = @gt_expr
+| 50 = @le_expr
+| 51 = @ge_expr
+/* equality */
+| 52 = @eq_expr
+| 53 = @ne_expr
+/* logical */
+| 54 = @bit_and_expr
+| 55 = @bit_xor_expr
+| 56 = @bit_or_expr
+| 57 = @log_and_expr
+| 58 = @log_or_expr
+/* type testing */
+| 59 = @is_expr
+| 60 = @as_expr
+/* null coalescing */
+| 61 = @null_coalescing_expr
+/* conditional */
+| 62 = @conditional_expr
+/* assignment */
+| 63 = @simple_assign_expr
+| 64 = @assign_add_expr
+| 65 = @assign_sub_expr
+| 66 = @assign_mul_expr
+| 67 = @assign_div_expr
+| 68 = @assign_rem_expr
+| 69 = @assign_and_expr
+| 70 = @assign_xor_expr
+| 71 = @assign_or_expr
+| 72 = @assign_lshift_expr
+| 73 = @assign_rshift_expr
+/* more */
+| 74 = @object_init_expr
+| 75 = @collection_init_expr
+| 76 = @array_init_expr
+| 77 = @checked_expr
+| 78 = @unchecked_expr
+| 79 = @constructor_init_expr
+| 80 = @add_event_expr
+| 81 = @remove_event_expr
+| 82 = @par_expr
+| 83 = @local_var_decl_expr
+| 84 = @lambda_expr
+| 85 = @anonymous_method_expr
+| 86 = @namespace_expr
+/* dynamic */
+| 92 = @dynamic_element_access_expr
+| 93 = @dynamic_member_access_expr
+/* unsafe */
+| 100 = @pointer_indirection_expr
+| 101 = @address_of_expr
+| 102 = @sizeof_expr
+/* async */
+| 103 = @await_expr
+/* C# 6.0 */
+| 104 = @nameof_expr
+| 105 = @interpolated_string_expr
+| 106 = @unknown_expr
+/* C# 7.0 */
+| 107 = @throw_expr
+| 108 = @tuple_expr
+| 109 = @local_function_invocation_expr
+| 110 = @ref_expr
+| 111 = @discard_expr
+/* C# 8.0 */
+| 112 = @range_expr
+| 113 = @index_expr
+| 114 = @switch_expr
+| 115 = @recursive_pattern_expr
+| 116 = @property_pattern_expr
+| 117 = @positional_pattern_expr
+| 118 = @switch_case_expr
+| 119 = @assign_coalesce_expr
+| 120 = @suppress_nullable_warning_expr
+| 121 = @namespace_access_expr
+/* C# 9.0 */
+| 122 = @lt_pattern_expr
+| 123 = @gt_pattern_expr
+| 124 = @le_pattern_expr
+| 125 = @ge_pattern_expr
+| 126 = @not_pattern_expr
+| 127 = @and_pattern_expr
+| 128 = @or_pattern_expr
+| 129 = @function_pointer_invocation_expr
+| 130 = @with_expr
+/* C# 11.0 */
+| 131 = @list_pattern_expr
+| 132 = @slice_pattern_expr
+| 133 = @urshift_expr
+| 134 = @assign_urshift_expr
+| 135 = @utf8_string_literal_expr
+/* C# 12.0 */
+| 136 = @collection_expr
+| 137 = @spread_element_expr
+| 138 = @interpolated_string_insert_expr
+/* Preprocessor */
+| 999 = @define_symbol_expr
+;
+
+@switch = @switch_stmt | @switch_expr;
+@case = @case_stmt | @switch_case_expr;
+@pattern_match = @case | @is_expr;
+@unary_pattern_expr = @not_pattern_expr;
+@relational_pattern_expr = @gt_pattern_expr | @lt_pattern_expr | @ge_pattern_expr | @le_pattern_expr;
+@binary_pattern_expr = @and_pattern_expr | @or_pattern_expr;
+
+@integer_literal_expr = @int_literal_expr | @long_literal_expr | @uint_literal_expr | @ulong_literal_expr;
+@real_literal_expr = @float_literal_expr | @double_literal_expr | @decimal_literal_expr;
+@string_literal_expr = @utf16_string_literal_expr | @utf8_string_literal_expr;
+@literal_expr = @bool_literal_expr | @char_literal_expr | @integer_literal_expr | @real_literal_expr
+ | @string_literal_expr | @null_literal_expr;
+
+@assign_expr = @simple_assign_expr | @assign_op_expr | @local_var_decl_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
+ | @assign_lshift_expr | @assign_rshift_expr | @assign_urshift_expr;
+
+@member_access_expr = @field_access_expr | @property_access_expr | @indexer_access_expr | @event_access_expr
+ | @method_access_expr | @type_access_expr | @dynamic_member_access_expr;
+@access_expr = @member_access_expr | @this_access_expr | @base_access_expr | @assignable_access_expr | @namespace_access_expr;
+@element_access_expr = @indexer_access_expr | @array_access_expr | @dynamic_element_access_expr;
+
+@local_variable_access = @local_variable_access_expr | @local_var_decl_expr;
+@local_scope_variable_access_expr = @parameter_access_expr | @local_variable_access;
+@variable_access_expr = @local_scope_variable_access_expr | @field_access_expr;
+
+@assignable_access_expr = @variable_access_expr | @property_access_expr | @element_access_expr
+ | @event_access_expr | @dynamic_member_access_expr;
+
+@objectorcollection_init_expr = @object_init_expr | @collection_init_expr;
+
+@delegate_creation_expr = @explicit_delegate_creation_expr | @implicit_delegate_creation_expr;
+
+@bin_arith_op_expr = @mul_expr | @div_expr | @rem_expr | @add_expr | @sub_expr;
+@incr_op_expr = @pre_incr_expr | @post_incr_expr;
+@decr_op_expr = @pre_decr_expr | @post_decr_expr;
+@mut_op_expr = @incr_op_expr | @decr_op_expr;
+@un_arith_op_expr = @plus_expr | @minus_expr | @mut_op_expr;
+@arith_op_expr = @bin_arith_op_expr | @un_arith_op_expr;
+
+@ternary_log_op_expr = @conditional_expr;
+@bin_log_op_expr = @log_and_expr | @log_or_expr | @null_coalescing_expr;
+@un_log_op_expr = @log_not_expr;
+@log_expr = @un_log_op_expr | @bin_log_op_expr | @ternary_log_op_expr;
+
+@bin_bit_op_expr = @bit_and_expr | @bit_or_expr | @bit_xor_expr | @lshift_expr
+ | @rshift_expr | @urshift_expr;
+@un_bit_op_expr = @bit_not_expr;
+@bit_expr = @un_bit_op_expr | @bin_bit_op_expr;
+
+@equality_op_expr = @eq_expr | @ne_expr;
+@rel_op_expr = @gt_expr | @lt_expr| @ge_expr | @le_expr;
+@comp_expr = @equality_op_expr | @rel_op_expr;
+
+@op_expr = @un_op | @bin_op | @ternary_op;
+
+@ternary_op = @ternary_log_op_expr;
+@bin_op = @assign_expr | @bin_arith_op_expr | @bin_log_op_expr | @bin_bit_op_expr | @comp_expr;
+@un_op = @un_arith_op_expr | @un_log_op_expr | @un_bit_op_expr | @sizeof_expr
+ | @pointer_indirection_expr | @address_of_expr;
+
+@anonymous_function_expr = @lambda_expr | @anonymous_method_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 | @op_invoke_expr;
+
+@throw_element = @throw_expr | @throw_stmt;
+
+@implicitly_typeable_object_creation_expr = @object_creation_expr | @explicit_delegate_creation_expr;
+
+implicitly_typed_array_creation(
+ unique int id: @array_creation_expr ref);
+
+explicitly_sized_array_creation(
+ unique int id: @array_creation_expr ref);
+
+stackalloc_array_creation(
+ unique int id: @array_creation_expr ref);
+
+implicitly_typed_object_creation(
+ unique int id: @implicitly_typeable_object_creation_expr ref);
+
+mutator_invocation_mode(
+ unique int id: @operator_invocation_expr ref,
+ int mode: int ref /* prefix = 1, postfix = 2*/);
+
+expr_value(
+ unique int id: @expr ref,
+ string value: string ref);
+
+expr_call(
+ unique int caller_id: @expr ref,
+ int target_id: @callable ref);
+
+expr_access(
+ unique int accesser_id: @access_expr ref,
+ int target_id: @accessible ref);
+
+@accessible = @method | @assignable | @local_function | @namespace;
+
+expr_location(
+ unique int id: @expr ref,
+ int loc: @location ref);
+
+dynamic_member_name(
+ unique int id: @late_bindable_expr ref,
+ string name: string ref);
+
+@qualifiable_expr = @member_access_expr
+ | @method_invocation_expr
+ | @element_access_expr;
+
+conditional_access(
+ unique int id: @qualifiable_expr ref);
+
+expr_argument(
+ unique int id: @expr ref,
+ int mode: int ref);
+ /* mode is the same as params: value = 0, ref = 1, out = 2 */
+
+expr_argument_name(
+ unique int id: @expr ref,
+ string name: string ref);
+
+lambda_expr_return_type(
+ unique int id: @lambda_expr ref,
+ int type_id: @type_or_ref ref);
+
+/* Compiler generated */
+
+compiler_generated(unique int id: @element ref);
+
+/** CONTROL/DATA FLOW **/
+
+@control_flow_element = @stmt | @expr;
+
+/* XML Files */
+
+xmlEncoding (
+ unique int id: @file ref,
+ string encoding: string ref);
+
+xmlDTDs(
+ unique int id: @xmldtd,
+ string root: string ref,
+ string publicId: string ref,
+ string systemId: string ref,
+ int fileid: @file ref);
+
+xmlElements(
+ unique int id: @xmlelement,
+ string name: string ref,
+ int parentid: @xmlparent ref,
+ int idx: int ref,
+ int fileid: @file ref);
+
+xmlAttrs(
+ unique int id: @xmlattribute,
+ int elementid: @xmlelement ref,
+ string name: string ref,
+ string value: string ref,
+ int idx: int ref,
+ int fileid: @file ref);
+
+xmlNs(
+ int id: @xmlnamespace,
+ string prefixName: string ref,
+ string URI: string ref,
+ int fileid: @file ref);
+
+xmlHasNs(
+ int elementId: @xmlnamespaceable ref,
+ int nsId: @xmlnamespace ref,
+ int fileid: @file ref);
+
+xmlComments(
+ unique int id: @xmlcomment,
+ string text: string ref,
+ int parentid: @xmlparent ref,
+ int fileid: @file ref);
+
+xmlChars(
+ unique int id: @xmlcharacters,
+ string text: string ref,
+ int parentid: @xmlparent ref,
+ int idx: int ref,
+ int isCDATA: int ref,
+ int fileid: @file ref);
+
+@xmlparent = @file | @xmlelement;
+@xmlnamespaceable = @xmlelement | @xmlattribute;
+
+xmllocations(
+ int xmlElement: @xmllocatable ref,
+ int location: @location_default ref);
+
+@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace;
+
+/* Comments */
+
+commentline(
+ unique int id: @commentline,
+ int kind: int ref,
+ string text: string ref,
+ string rawtext: string ref);
+
+case @commentline.kind of
+ 0 = @singlelinecomment
+| 1 = @xmldoccomment
+| 2 = @multilinecomment;
+
+commentline_location(
+ unique int id: @commentline ref,
+ int loc: @location ref);
+
+commentblock(
+ unique int id : @commentblock);
+
+commentblock_location(
+ unique int id: @commentblock ref,
+ int loc: @location ref);
+
+commentblock_binding(
+ int id: @commentblock ref,
+ int entity: @element ref,
+ int bindtype: int ref); /* 0: Parent, 1: Best, 2: Before, 3: After */
+
+commentblock_child(
+ int id: @commentblock ref,
+ int commentline: @commentline ref,
+ int index: int ref);
+
+/* ASP.NET */
+
+case @asp_element.kind of
+ 0=@asp_close_tag
+| 1=@asp_code
+| 2=@asp_comment
+| 3=@asp_data_binding
+| 4=@asp_directive
+| 5=@asp_open_tag
+| 6=@asp_quoted_string
+| 7=@asp_text
+| 8=@asp_xml_directive;
+
+@asp_attribute = @asp_code | @asp_data_binding | @asp_quoted_string;
+
+asp_elements(
+ unique int id: @asp_element,
+ int kind: int ref,
+ int loc: @location ref);
+
+asp_comment_server(unique int comment: @asp_comment ref);
+asp_code_inline(unique int code: @asp_code ref);
+asp_directive_attribute(
+ int directive: @asp_directive ref,
+ int index: int ref,
+ string name: string ref,
+ int value: @asp_quoted_string ref);
+asp_directive_name(
+ unique int directive: @asp_directive ref,
+ string name: string ref);
+asp_element_body(
+ unique int element: @asp_element ref,
+ string body: string ref);
+asp_tag_attribute(
+ int tag: @asp_open_tag ref,
+ int index: int ref,
+ string name: string ref,
+ int attribute: @asp_attribute ref);
+asp_tag_name(
+ unique int tag: @asp_open_tag ref,
+ string name: string ref);
+asp_tag_isempty(int tag: @asp_open_tag ref);
diff --git a/csharp/ql/lib/upgrades/e73ca2c93df8aae162f1704edc4817a5cb330529/upgrade.properties b/csharp/ql/lib/upgrades/e73ca2c93df8aae162f1704edc4817a5cb330529/upgrade.properties
new file mode 100644
index 000000000000..e8ac18078a80
--- /dev/null
+++ b/csharp/ql/lib/upgrades/e73ca2c93df8aae162f1704edc4817a5cb330529/upgrade.properties
@@ -0,0 +1,6 @@
+description: Add operation kinds for operations, cleanup expanded assignments and rotate assignment child expressions.
+compatibility: full
+expr_parent: run rotate_assignments.ql new_expr_parent
+expressions: run rotate_assignments.ql new_expressions
+expr_calls: run rotate_assignments.ql new_expr_calls
+dynamic_member_name: run rotate_assignments.ql new_dynamic_member_name
diff --git a/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql b/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql
index 386b238e049c..a58e19049ff9 100644
--- a/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql
+++ b/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql
@@ -43,7 +43,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 +114,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 =
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/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/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/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/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/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/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