Skip to content

C# 14: User defined compound assignment operators.#21372

Open
michaelnebel wants to merge 14 commits intogithub:mainfrom
michaelnebel:csharp14/usercompoundassignment
Open

C# 14: User defined compound assignment operators.#21372
michaelnebel wants to merge 14 commits intogithub:mainfrom
michaelnebel:csharp14/usercompoundassignment

Conversation

@michaelnebel
Copy link
Contributor

@michaelnebel michaelnebel commented Feb 25, 2026

In this PR we introduce support for user-defined compound assignment operators.

class A {
    // Compound operator declaration.
    public void operator +=(A x) {
       ...
    }
}

class B {
    public void M(A a1, A a2) {
        a1 += a2; // Compound operator invocation.
    }
}

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

class A {
    public static A operator +(A x1, A x2) {
        ...
    }
}

then a1 += a2 is also valid, but instead it means a1 = a1 + a2 (where the right hand side is a call to the static operator).

As a consequence, we don't want to expand a1 += a2 into a1 = a1 + a2 in 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

  • Even though it is claimed that there is some support for dynamic operator calls, I could not produce a running example (and thus haven’t introduced dispatch logic for this case).
  • There will be a separate PR for the increment/decrement operator.

@github-actions github-actions bot added the C# label Feb 25, 2026
@michaelnebel michaelnebel force-pushed the csharp14/usercompoundassignment branch 3 times, most recently from 07345fe to a9a8936 Compare March 6, 2026 08:35
@michaelnebel michaelnebel force-pushed the csharp14/usercompoundassignment branch from a9a8936 to 1faac41 Compare March 6, 2026 10:51
@michaelnebel michaelnebel force-pushed the csharp14/usercompoundassignment branch from 1faac41 to 76f65d5 Compare March 6, 2026 12:51
@michaelnebel michaelnebel force-pushed the csharp14/usercompoundassignment branch from 76f65d5 to fafe360 Compare March 9, 2026 15:34
@michaelnebel
Copy link
Contributor Author

DCA looks good

  • Performance doesn't appear to be affected.
  • Fewer extraction errors.

@michaelnebel michaelnebel marked this pull request as ready for review March 10, 2026 08:25
@michaelnebel michaelnebel requested a review from a team as a code owner March 10, 2026 08:25
Copilot AI review requested due to automatic review settings March 10, 2026 08:25
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 CompoundAssignmentOperatorCall distinctly from ordinary OperatorCall.
  • 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`).
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor wording/grammar: “or a (CompoundAssignmentOperator).” reads oddly and is missing the noun. Consider changing to “or a compound assignment operator (CompoundAssignmentOperator).”

Suggested change
* a (`CompoundAssignmentOperator`).
* a compound assignment operator (`CompoundAssignmentOperator`).

Copilot uses AI. Check for mistakes.
Comment on lines +571 to +578
* class A {
* public void operator+=(A other) {
* ...
* }
*
* public A Add(A other) {
* return this += other;
* }
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
IntVector iv3 = new IntVector(4); // vector of 4 x 0
iv3 += iv2; // iv3 contains 4 x 2

// The following operations doesn't do anything.
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grammar nit: “The following operations doesn't do anything.” → “The following operations don't do anything.”

Suggested change
// The following operations doesn't do anything.
// The following operations don't do anything.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants