C# 14: User defined compound assignment operators.#21372
C# 14: User defined compound assignment operators.#21372michaelnebel wants to merge 14 commits intogithub:mainfrom
Conversation
07345fe to
a9a8936
Compare
a9a8936 to
1faac41
Compare
1faac41 to
76f65d5
Compare
… operators declared as extensions.
76f65d5 to
fafe360
Compare
|
DCA looks good
|
There was a problem hiding this comment.
Pull request overview
This PR adds C# 14 support for user-defined compound assignment operators (for example a += b where operator += is declared) by extracting them as operator invocations rather than desugaring them into a = a + b, and updates QL dispatch/dataflow plus tests accordingly.
Changes:
- Add new extractor handling for compound assignment expressions to emit direct operator-call extraction for user-defined (instance) compound assignment operators.
- Extend the C# QL library to represent and dispatch
CompoundAssignmentOperatorCalldistinctly from ordinaryOperatorCall. - Update/extend AST and dataflow library tests and expected outputs for compound assignment operator declarations and invocations (including extension-based operators).
Reviewed changes
Copilot reviewed 19 out of 20 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| csharp/ql/test/library-tests/operators/operators.cs | Adds user-defined compound assignment operator declarations and invocations for AST coverage. |
| csharp/ql/test/library-tests/operators/PrintAst.expected | Updates AST expectations to include compound assignment operators/calls. |
| csharp/ql/test/library-tests/operators/Operators1.expected | Updates operator location expectations after AST shifts. |
| csharp/ql/test/library-tests/operators/Operators2.expected | Updates operator location expectations after AST shifts. |
| csharp/ql/test/library-tests/operators/Operators3.expected | Updates conversion-operator location expectations after AST shifts. |
| csharp/ql/test/library-tests/operators/Operators4.expected | Updates conversion-operator location expectations after AST shifts. |
| csharp/ql/test/library-tests/dataflow/operators/Operator.cs | Adds a dataflow test for user-defined compound assignment operator calls. |
| csharp/ql/test/library-tests/dataflow/operators/operatorFlow.expected | Updates expected dataflow graph to reflect compound assignment operator modeling. |
| csharp/ql/test/library-tests/dataflow/extensions/extensions.cs | Adds an extension-based compound assignment operator and a flow test covering it. |
| csharp/ql/test/library-tests/dataflow/extensions/ExtensionFlow.expected | Updates expected dataflow output for the new extension compound assignment test. |
| csharp/ql/lib/semmle/code/csharp/exprs/Call.qll | Introduces CompoundAssignmentOperatorCall in the QL expression hierarchy. |
| csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll | Adjusts dispatch to treat compound assignment operator calls as dispatchable calls with a qualifier. |
| csharp/ql/lib/semmle/code/csharp/Callable.qll | Adds QL operator classes for compound assignment operator declarations. |
| csharp/ql/lib/change-notes/2026-03-06-compound-assignment-operations.md | Adds changelog entry for the new C# 14 support. |
| csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/UserCompoundAssignmentInvocation.cs | New extractor expression node for user-defined compound assignment operator invocations. |
| csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Invocation.cs | Refactors call target resolution to use a shared helper. |
| csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Factory.cs | Routes compound assignment syntax kinds through new compound-assignment extraction logic. |
| csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/CompoundAssignment.cs | New helper to decide between desugaring vs direct operator-call extraction for compound assignments. |
| csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs | Adds ExpressionNodeInfo.GetTargetSymbol helper for consistent target resolution. |
| csharp/extractor/Semmle.Extraction.CSharp.Util/SymbolExtensions.cs | Extends operator-symbol parsing to recognize checked and assignment operator method names. |
You can also share your feedback on Copilot code review. Take the survey.
| * Either a unary operator (`UnaryOperator`), a binary operator | ||
| * (`BinaryOperator`), or a conversion operator (`ConversionOperator`). | ||
| * (`BinaryOperator`), a conversion operator (`ConversionOperator`), or | ||
| * a (`CompoundAssignmentOperator`). |
There was a problem hiding this comment.
Minor wording/grammar: “or a (CompoundAssignmentOperator).” reads oddly and is missing the noun. Consider changing to “or a compound assignment operator (CompoundAssignmentOperator).”
| * a (`CompoundAssignmentOperator`). | |
| * a compound assignment operator (`CompoundAssignmentOperator`). |
| * class A { | ||
| * public void operator+=(A other) { | ||
| * ... | ||
| * } | ||
| * | ||
| * public A Add(A other) { | ||
| * return this += other; | ||
| * } |
There was a problem hiding this comment.
The example in this doc comment appears to be invalid C#: the operator is declared to return void, but the sample then uses return this += other;. Consider adjusting the example to either not return the compound assignment, or to show a (valid) return pattern that matches the operator’s return type.
| IntVector iv3 = new IntVector(4); // vector of 4 x 0 | ||
| iv3 += iv2; // iv3 contains 4 x 2 | ||
|
|
||
| // The following operations doesn't do anything. |
There was a problem hiding this comment.
Grammar nit: “The following operations doesn't do anything.” → “The following operations don't do anything.”
| // The following operations doesn't do anything. | |
| // The following operations don't do anything. |
In this PR we introduce support for user-defined compound assignment operators.
Compound assignment operator calls are not implicitly assignments, but instead the left hand side of the compound assignment is updated. That is, user-defined compound operator calls behave more like instance method calls than static operator calls.
Had we instead declared
then
a1 += a2is also valid, but instead it meansa1 = a1 + a2(where the right hand side is a call to the static operator).As a consequence, we don't want to expand
a1 += a2intoa1 = a1 + a2in case of user-defined compound operator invocations. Instead we want to extract the syntax as is and consider these type of invocations more like method calls in the QL library code.Notes on the implementation