From 2729bfe379ff21ff6a0e8a89e424caa65684a516 Mon Sep 17 00:00:00 2001 From: Michael Nebel Date: Wed, 22 Apr 2026 15:41:04 +0200 Subject: [PATCH] C#: Add compound assignment operator QL classes. --- csharp/ql/lib/semmle/code/csharp/Callable.qll | 258 +++++++++++++++++- csharp/ql/lib/semmle/code/csharp/Type.qll | 1 + 2 files changed, 255 insertions(+), 4 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/Callable.qll b/csharp/ql/lib/semmle/code/csharp/Callable.qll index 7859c7ea1d0..9416a7d4d9c 100644 --- a/csharp/ql/lib/semmle/code/csharp/Callable.qll +++ b/csharp/ql/lib/semmle/code/csharp/Callable.qll @@ -611,7 +611,8 @@ class ExtensionOperator extends ExtensionCallableImpl, Operator { class UnaryOperator extends Operator { UnaryOperator() { this.getNumberOfParameters() = 1 and - not this instanceof ConversionOperator + not this instanceof ConversionOperator and + not this instanceof CompoundAssignmentOperator } } @@ -784,7 +785,7 @@ class TrueOperator extends UnaryOperator { * A user-defined binary operator. * * Either an addition operator (`AddOperator`), a checked addition operator - * (`CheckedAddOperator`) a subtraction operator (`SubOperator`), a checked + * (`CheckedAddOperator`), a subtraction operator (`SubOperator`), a checked * subtraction operator (`CheckedSubOperator`), a multiplication operator * (`MulOperator`), a checked multiplication operator (`CheckedMulOperator`), * a division operator (`DivOperator`), a checked division operator @@ -795,10 +796,16 @@ class TrueOperator extends UnaryOperator { * operator(`UnsignedRightShiftOperator`), an equals operator (`EQOperator`), * a not equals operator (`NEOperator`), a lesser than operator (`LTOperator`), * a greater than operator (`GTOperator`), a less than or equals operator - * (`LEOperator`), or a greater than or equals operator (`GEOperator`). + * (`LEOperator`), a greater than or equals operator (`GEOperator`), or + * a compound assignment operator (`CompoundAssignmentOperator`). */ class BinaryOperator extends Operator { - BinaryOperator() { this.getNumberOfParameters() = 2 } + BinaryOperator() { + this.getNumberOfParameters() = 2 + or + // Instance compound assignment operators only have one parameter. + this.getNumberOfParameters() = 1 and not this.isStatic() + } } /** @@ -1184,6 +1191,249 @@ class CheckedExplicitConversionOperator extends ConversionOperator { override string getAPrimaryQlClass() { result = "CheckedExplicitConversionOperator" } } +abstract private class CompoundAssignmentOperatorImpl extends BinaryOperator { } + +/** + * A user-defined compound assignment operator. + * + * Either an addition operator (`AddCompoundAssignmentOperator`), a checked addition operator + * (`CheckedAddCompoundAssignmentOperator`), a subtraction operator (`SubCompoundAssignmentOperator`), a checked + * subtraction operator (`CheckedSubCompoundAssignmentOperator`), a multiplication operator + * (`MulCompoundAssignmentOperator`), a checked multiplication operator (`CheckedMulCompoundAssignmentOperator`), + * a division operator (`DivCompoundAssignmentOperator`), a checked division operator + * (`CheckedDivCompoundAssignmentOperator`), a remainder operator (`RemCompoundAssignmentOperator`), an and + * operator (`AndCompoundAssignmentOperator`), an or operator (`OrCompoundAssignmentOperator`), an xor + * operator (`XorCompoundAssignmentOperator`), a left shift operator (`LeftShiftCompoundAssignmentOperator`), + * a right shift operator (`RightShiftCompoundAssignmentOperator`), or an unsigned right shift + * operator(`UnsignedRightShiftCompoundAssignmentOperator`). + */ +final class CompoundAssignmentOperator = CompoundAssignmentOperatorImpl; + +/** + * A user-defined compound assignment addition operator (`+=`), for example + * + * ```csharp + * public void operator checked +=(Widget w) { + * ... + * } + * ``` + */ +class AddCompoundAssignmentOperator extends CompoundAssignmentOperatorImpl { + AddCompoundAssignmentOperator() { this.getName() = "+=" } + + override string getAPrimaryQlClass() { result = "AddCompoundAssignmentOperator" } +} + +/** + * A user-defined checked compound assignment addition operator (`checked +=`), for example + * + * ```csharp + * public void operator checked +=(Widget w) { + * ... + * } + * ``` + */ +class CheckedAddCompoundAssignmentOperator extends CompoundAssignmentOperatorImpl { + CheckedAddCompoundAssignmentOperator() { this.getName() = "checked +=" } + + override string getAPrimaryQlClass() { result = "CheckedAddCompoundAssignmentOperator" } +} + +/** + * A user-defined compound assignment subtraction operator (`-=`), for example + * + * ```csharp + * public void operator -=(Widget w) { + * ... + * } + * ``` + */ +class SubCompoundAssignmentOperator extends CompoundAssignmentOperatorImpl { + SubCompoundAssignmentOperator() { this.getName() = "-=" } + + override string getAPrimaryQlClass() { result = "SubCompoundAssignmentOperator" } +} + +/** + * A user-defined checked compound assignment subtraction operator (`checked -=`), for example + * + * ```csharp + * public void operator checked -=(Widget w) { + * ... + * } + * ``` + */ +class CheckedSubCompoundAssignmentOperator extends CompoundAssignmentOperatorImpl { + CheckedSubCompoundAssignmentOperator() { this.getName() = "checked -=" } + + override string getAPrimaryQlClass() { result = "CheckedSubCompoundAssignmentOperator" } +} + +/** + * A user-defined compound assignment multiplication operator (`*=`), for example + * + * ```csharp + * public void operator *=(Widget w) { + * ... + * } + * ``` + */ +class MulCompoundAssignmentOperator extends CompoundAssignmentOperatorImpl { + MulCompoundAssignmentOperator() { this.getName() = "*=" } + + override string getAPrimaryQlClass() { result = "MulCompoundAssignmentOperator" } +} + +/** + * A user-defined checked compound assignment multiplication operator (`checked *=`), for example + * + * ```csharp + * public void operator checked *=(Widget w) { + * ... + * } + * ``` + */ +class CheckedMulCompoundAssignmentOperator extends CompoundAssignmentOperatorImpl { + CheckedMulCompoundAssignmentOperator() { this.getName() = "checked *=" } + + override string getAPrimaryQlClass() { result = "CheckedMulCompoundAssignmentOperator" } +} + +/** + * A user-defined compound assignment division operator (`/=`), for example + * + * ```csharp + * public void operator /=(Widget w) { + * ... + * } + * ``` + */ +class DivCompoundAssignmentOperator extends CompoundAssignmentOperatorImpl { + DivCompoundAssignmentOperator() { this.getName() = "/=" } + + override string getAPrimaryQlClass() { result = "DivCompoundAssignmentOperator" } +} + +/** + * A user-defined checked compound assignment division operator (`checked /=`), for example + * + * ```csharp + * public void operator checked /=(Widget w) { + * ... + * } + * ``` + */ +class CheckedDivCompoundAssignmentOperator extends CompoundAssignmentOperatorImpl { + CheckedDivCompoundAssignmentOperator() { this.getName() = "checked /=" } + + override string getAPrimaryQlClass() { result = "CheckedDivCompoundAssignmentOperator" } +} + +/** + * A user-defined compound assignment remainder operator (`%=`), for example + * + * ```csharp + * public void operator %=(Widget w) { + * ... + * } + * ``` + */ +class RemCompoundAssignmentOperator extends CompoundAssignmentOperatorImpl { + RemCompoundAssignmentOperator() { this.getName() = "%=" } + + override string getAPrimaryQlClass() { result = "RemCompoundAssignmentOperator" } +} + +/** + * A user-defined compound assignment and operator (`&=`), for example + * + * ```csharp + * public void operator &=(Widget w) { + * ... + * } + * ``` + */ +class AndCompoundAssignmentOperator extends CompoundAssignmentOperatorImpl { + AndCompoundAssignmentOperator() { this.getName() = "&=" } + + override string getAPrimaryQlClass() { result = "AndCompoundAssignmentOperator" } +} + +/** + * A user-defined compound assignment or operator (`|=`), for example + * + * ```csharp + * public void operator |=(Widget w) { + * ... + * } + * ``` + */ +class OrCompoundAssignmentOperator extends CompoundAssignmentOperatorImpl { + OrCompoundAssignmentOperator() { this.getName() = "|=" } + + override string getAPrimaryQlClass() { result = "OrCompoundAssignmentOperator" } +} + +/** + * A user-defined compound assignment xor operator (`^=`), for example + * + * ```csharp + * public void operator ^=(Widget w) { + * ... + * } + * ``` + */ +class XorCompoundAssignmentOperator extends CompoundAssignmentOperatorImpl { + XorCompoundAssignmentOperator() { this.getName() = "^=" } + + override string getAPrimaryQlClass() { result = "XorCompoundAssignmentOperator" } +} + +/** + * A user-defined compound assignment left shift operator (`<<=`), for example + * + * ```csharp + * public void operator <<=(Widget w) { + * ... + * } + * ``` + */ +class LeftShiftCompoundAssignmentOperator extends CompoundAssignmentOperatorImpl { + LeftShiftCompoundAssignmentOperator() { this.getName() = "<<=" } + + override string getAPrimaryQlClass() { result = "LeftShiftCompoundAssignmentOperator" } +} + +/** + * A user-defined compound assignment right shift operator (`>>=`), for example + * + * ```csharp + * public void operator >>=(Widget w) { + * ... + * } + * ``` + */ +class RightShiftCompoundAssignmentOperator extends CompoundAssignmentOperatorImpl { + RightShiftCompoundAssignmentOperator() { this.getName() = ">>=" } + + override string getAPrimaryQlClass() { result = "RightShiftCompoundAssignmentOperator" } +} + +/** + * A user-defined compound assignment unsigned right shift operator (`>>>=`), for example + * + * ```csharp + * public void operator >>>=(Widget w) { + * ... + * } + * ``` + */ +class UnsignedRightShiftCompoundAssignmentOperator extends CompoundAssignmentOperatorImpl { + UnsignedRightShiftCompoundAssignmentOperator() { this.getName() = ">>>=" } + + override string getAPrimaryQlClass() { result = "UnsignedRightShiftCompoundAssignmentOperator" } +} + /** * A local function, defined within the scope of another callable. * For example, `Fac` on lines 2--4 in diff --git a/csharp/ql/lib/semmle/code/csharp/Type.qll b/csharp/ql/lib/semmle/code/csharp/Type.qll index 54bbe9a6219..7af167cd9bc 100644 --- a/csharp/ql/lib/semmle/code/csharp/Type.qll +++ b/csharp/ql/lib/semmle/code/csharp/Type.qll @@ -1334,6 +1334,7 @@ class TypeMention extends @type_mention { * ```csharp * static class MyExtensions { * extension(string s) { ... } + * } * ``` */ class ExtensionType extends Parameterizable, @extension_type {