Skip to content

Commit f030794

Browse files
committed
C#: Add compound assignment operator call classes.
1 parent 231336c commit f030794

2 files changed

Lines changed: 68 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: 64 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,57 @@ 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 A Add(A other) {
584+
* return 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+
Expr getQualifier() { result = this.getChildExpr(0) }
595+
}
596+
597+
/**
598+
* A call to a compound assignment extension operator, for example `s1 *= s2` on
599+
* line 9 in
600+
*
601+
* ```csharp
602+
* static class MyExtensions {
603+
* extension(string s) {
604+
* public void operator *=(string other) { ... }
605+
* }
606+
* }
607+
*
608+
* class A {
609+
* void M(string s1, string s2) {
610+
* s1 *= s2;
611+
* }
612+
* }
613+
*/
614+
class ExtensionCompoundAssignmentOperatorCall extends CompoundAssignmentOperatorCall,
615+
ExtensionOperatorCall
616+
{
617+
override Expr getArgument(int i) { result = ExtensionOperatorCall.super.getArgument(i) }
618+
619+
override Expr getQualifier() { none() }
620+
621+
override string getAPrimaryQlClass() { result = "ExtensionCompoundAssignmentOperatorCall" }
622+
}
623+
560624
private class DelegateLikeCall_ = @delegate_invocation_expr or @function_pointer_invocation_expr;
561625

562626
/**

0 commit comments

Comments
 (0)