Skip to content

Commit 0866f18

Browse files
committed
C#: Add compound assignment operator call classes.
1 parent 5daa8b9 commit 0866f18

2 files changed

Lines changed: 69 additions & 2 deletions

File tree

csharp/ql/lib/semmle/code/csharp/exprs/Assignment.qll

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,10 @@ class AssignOperation extends Assignment, @assign_op_expr {
9494
}
9595

9696
/**
97-
* A compound assignment operation that implicitly invokes an operator.
98-
* For example, `x += y` assigns the result of `x + y` to `x`.
97+
* A compound assignment operation that invokes an operator.
98+
*
99+
* (1) `x += y` invokes the compound assignment operator `+=` (if it exists).
100+
* (2) `x += y` invokes the operator `+` and assigns `x + y` to `x`.
99101
*
100102
* Either an arithmetic assignment operation (`AssignArithmeticOperation`) or a bitwise
101103
* assignment operation (`AssignBitwiseOperation`).

csharp/ql/lib/semmle/code/csharp/exprs/Call.qll

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,19 @@ class ExtensionOperatorCall extends OperatorCall {
529529
ExtensionOperatorCall() { this.getTarget() instanceof ExtensionOperator }
530530

531531
override string getAPrimaryQlClass() { result = "ExtensionOperatorCall" }
532+
533+
private predicate isOrdinaryStaticCall() {
534+
not exists(Expr e | e = this.getChildExpr(-1) | not e instanceof TypeAccess)
535+
}
536+
537+
override Expr getArgument(int i) {
538+
exists(int j | result = this.getChildExpr(j) |
539+
i >= 0 and
540+
if this.isOrdinaryStaticCall() or this.getTarget() instanceof CompoundAssignmentOperator
541+
then j = i
542+
else j = i - 1
543+
)
544+
}
532545
}
533546

534547
/**
@@ -557,6 +570,58 @@ class MutatorOperatorCall extends OperatorCall {
557570
predicate isPostfix() { mutator_invocation_mode(this, 2) }
558571
}
559572

573+
/**
574+
* A call to a compound assignment operator, for example `this += other`
575+
* on line 7 in
576+
*
577+
* ```csharp
578+
* class A {
579+
* public void operator +=(A other) {
580+
* ...
581+
* }
582+
*
583+
* public void Add(A other) {
584+
* this += other;
585+
* }
586+
* }
587+
* ```
588+
*/
589+
class CompoundAssignmentOperatorCall extends AssignCallOperation {
590+
CompoundAssignmentOperatorCall() { this.getTarget() instanceof CompoundAssignmentOperator }
591+
592+
override Expr getArgument(int i) { result = this.getChildExpr(i + 1) and i >= 0 }
593+
594+
/** Gets the qualifier of this compound assignment operator call. */
595+
Expr getQualifier() { result = this.getChildExpr(0) }
596+
}
597+
598+
/**
599+
* A call to a compound assignment extension operator, for example `s1 *= s2` on
600+
* line 9 in
601+
*
602+
* ```csharp
603+
* static class MyExtensions {
604+
* extension(string s) {
605+
* public void operator *=(string other) { ... }
606+
* }
607+
* }
608+
*
609+
* class A {
610+
* void M(string s1, string s2) {
611+
* s1 *= s2;
612+
* }
613+
* }
614+
*/
615+
class ExtensionCompoundAssignmentOperatorCall extends CompoundAssignmentOperatorCall,
616+
ExtensionOperatorCall
617+
{
618+
override Expr getArgument(int i) { result = ExtensionOperatorCall.super.getArgument(i) }
619+
620+
override Expr getQualifier() { none() }
621+
622+
override string getAPrimaryQlClass() { result = "ExtensionCompoundAssignmentOperatorCall" }
623+
}
624+
560625
private class DelegateLikeCall_ = @delegate_invocation_expr or @function_pointer_invocation_expr;
561626

562627
/**

0 commit comments

Comments
 (0)