C#: Update the child indices for assignments, update Assign classes to extend OperatorCall and add AssignOperation classes.

This commit is contained in:
Michael Nebel
2026-03-20 12:33:57 +01:00
parent b426c6fb39
commit 327757dbcb
7 changed files with 163 additions and 59 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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() }
@@ -64,37 +60,33 @@ 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`).
* (`AssignArithmeticOperation`), a bitwise assignment operation or
* (`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() + " ..." }
}
/**
* An assignment operation that corresponds to an operator call, for example `x += y` corresponds to `x = x + y`.
*/
class AssignCallOperation extends AssignOperation, OperatorCall, @assign_op_call_expr {
override string toString() { result = "... " + this.getOperator() + " ..." }
}
/**
* An arithmetic assignment operation. Either an addition assignment operation
* (`AssignAddExpr`), a subtraction assignment operation (`AssignSubExpr`), a
@@ -102,7 +94,7 @@ 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`.
@@ -158,7 +150,7 @@ 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`.
@@ -208,12 +200,17 @@ 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, @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 +219,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) }
}
/**

View File

@@ -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" }
}

View File

@@ -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`

View File

@@ -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
}

View File

@@ -0,0 +1,122 @@
/**
* Provides classes for operations that also have compound assignment forms.
*/
import Expr
/** A binary operation that involves a null-coalescing operation. */
abstract private class NullCoalescingOperationImpl extends BinaryOperation { }
final class NullCoalescingOperation = NullCoalescingOperationImpl;
private class AddNullCoalescingExpr extends NullCoalescingOperationImpl instanceof NullCoalescingExpr
{ }
private class AddAssignCoalesceExpr extends NullCoalescingOperationImpl instanceof AssignCoalesceExpr
{ }
/** A binary operations that involves an addition operation. */
abstract private class AddOperationImpl extends BinaryOperation { }
final class AddOperation = AddOperationImpl;
private class AddAddExpr extends AddOperationImpl instanceof AddExpr { }
private class AddAssignExpr extends AddOperationImpl instanceof AssignAddExpr { }
/** A binary operation that involves a subtraction operation. */
abstract private class SubOperationImpl extends BinaryOperation { }
final class SubOperation = SubOperationImpl;
private class AddSubExpr extends SubOperationImpl instanceof SubExpr { }
private class AddSubAssignExpr extends SubOperationImpl instanceof AssignSubExpr { }
/** A binary operation that involves a multiplication operation. */
abstract private class MulOperationImpl extends BinaryOperation { }
final class MulOperation = MulOperationImpl;
private class AddMulExpr extends MulOperationImpl instanceof MulExpr { }
private class AddMulAssignExpr extends MulOperationImpl instanceof AssignMulExpr { }
/** A binary operation that involves a division operation. */
abstract private class DivOperationImpl extends BinaryOperation {
/** Gets the denominator of this division operation. */
Expr getDenominator() { result = this.getRightOperand() }
}
final class DivOperation = DivOperationImpl;
private class AddDivExpr extends DivOperationImpl instanceof DivExpr { }
private class AddDivAssignExpr extends DivOperationImpl instanceof AssignDivExpr { }
/** A binary operation that involves a remainder operation. */
abstract private class RemOperationImpl extends BinaryOperation { }
final class RemOperation = RemOperationImpl;
private class AddRemExpr extends RemOperationImpl instanceof RemExpr { }
private class AddRemAssignExpr extends RemOperationImpl instanceof AssignRemExpr { }
/** A binary operation that involves a bitwise AND operation. */
abstract private class BitwiseAndOperationImpl extends BinaryOperation { }
final class BitwiseAndOperation = BitwiseAndOperationImpl;
private class AddBitwiseAndExpr extends BitwiseAndOperationImpl instanceof BitwiseAndExpr { }
private class AddAssignBitwiseAndExpr extends BitwiseAndOperationImpl instanceof AssignAndExpr { }
/** A binary operation that involves a bitwise OR operation. */
abstract private class BitwiseOrOperationImpl extends BinaryOperation { }
final class BitwiseOrOperation = BitwiseOrOperationImpl;
private class AddBitwiseOrExpr extends BitwiseOrOperationImpl instanceof BitwiseOrExpr { }
private class AddAssignBitwiseOrExpr extends BitwiseOrOperationImpl instanceof AssignOrExpr { }
/** A binary operation that involves a bitwise XOR operation. */
abstract private class BitwiseXorOperationImpl extends BinaryOperation { }
final class BitwiseXorOperation = BitwiseXorOperationImpl;
private class AddBitwiseXorExpr extends BitwiseXorOperationImpl instanceof BitwiseXorExpr { }
private class AddAssignBitwiseXorExpr extends BitwiseXorOperationImpl instanceof AssignXorExpr { }
/** A binary operation that involves a left shift operation. */
abstract private class LeftShiftOperationImpl extends BinaryOperation { }
final class LeftShiftOperation = LeftShiftOperationImpl;
private class AddLeftShiftExpr extends LeftShiftOperationImpl instanceof LeftShiftExpr { }
private class AddAssignLeftShiftExpr extends LeftShiftOperationImpl instanceof AssignLeftShiftExpr {
}
/** A binary operation that involves a right shift operation. */
abstract private class RightShiftOperationImpl extends BinaryOperation { }
final class RightShiftOperation = RightShiftOperationImpl;
private class AddRightShiftExpr extends RightShiftOperationImpl instanceof RightShiftExpr { }
private class AddAssignRightShiftExpr extends RightShiftOperationImpl instanceof AssignRightShiftExpr
{ }
/** A binary operation that involves a unsigned right shift operation. */
abstract private class UnsignedRightShiftOperationImpl extends BinaryOperation { }
final class UnsignedRightShiftOperation = UnsignedRightShiftOperationImpl;
private class AddUnsignedRightShiftExpr extends UnsignedRightShiftOperationImpl instanceof UnsignedRightShiftExpr
{ }
private class AddAssignUnsignedRightShiftExpr extends UnsignedRightShiftOperationImpl instanceof AssignUnsignedRightShiftExpr
{ }