diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Util/SymbolExtensions.cs b/csharp/extractor/Semmle.Extraction.CSharp.Util/SymbolExtensions.cs
index 92d7ecfad6bb..50604e2404e4 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp.Util/SymbolExtensions.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp.Util/SymbolExtensions.cs
@@ -52,6 +52,13 @@ public static string GetName(this ISymbol symbol, bool useMetadataName = false)
{ "op_False", "false" }
});
+ ///
+ /// The operatorname for user-defined increment and decrement operators are "op_IncrementAssignment" and
+ /// "op_DecrementAssignment" respectively.
+ /// Thus we need to handle this explicitly to avoid postfixing them with an "=".
+ ///
+ private static bool isIncrementOrDecrement(string operatorName) => operatorName == "++" || operatorName == "--";
+
///
/// Convert an operator method name in to a symbolic name.
/// A return value indicates whether the conversion succeeded.
@@ -72,7 +79,7 @@ public static bool TryGetOperatorSymbol(this ISymbol symbol, out string operator
if (match.Success && methodToOperator.TryGetValue($"op_{match.Groups[2]}", out var rawOperatorName))
{
var prefix = match.Groups[1].Success ? "checked " : "";
- var postfix = match.Groups[3].Success ? "=" : "";
+ var postfix = match.Groups[3].Success && !isIncrementOrDecrement(rawOperatorName) ? "=" : "";
operatorName = $"{prefix}{rawOperatorName}{postfix}";
return true;
}
diff --git a/csharp/ql/lib/semmle/code/csharp/Callable.qll b/csharp/ql/lib/semmle/code/csharp/Callable.qll
index 9416a7d4d9c7..198ad2af1801 100644
--- a/csharp/ql/lib/semmle/code/csharp/Callable.qll
+++ b/csharp/ql/lib/semmle/code/csharp/Callable.qll
@@ -613,6 +613,9 @@ class UnaryOperator extends Operator {
this.getNumberOfParameters() = 1 and
not this instanceof ConversionOperator and
not this instanceof CompoundAssignmentOperator
+ or
+ // Instance increment and decrement operators don't have a parameter (only a qualifier).
+ this.getNumberOfParameters() = 0 and not this.isStatic()
}
}
diff --git a/csharp/ql/test/library-tests/operators/Operators3.expected b/csharp/ql/test/library-tests/operators/Operators3.expected
index e81aec79ba19..bda7edb99bae 100644
--- a/csharp/ql/test/library-tests/operators/Operators3.expected
+++ b/csharp/ql/test/library-tests/operators/Operators3.expected
@@ -1 +1 @@
-| operators.cs:96:32:96:39 | implicit conversion |
+| operators.cs:118:36:118:43 | implicit conversion |
diff --git a/csharp/ql/test/library-tests/operators/Operators4.expected b/csharp/ql/test/library-tests/operators/Operators4.expected
index 49db993c093d..bee27656f264 100644
--- a/csharp/ql/test/library-tests/operators/Operators4.expected
+++ b/csharp/ql/test/library-tests/operators/Operators4.expected
@@ -1 +1 @@
-| operators.cs:101:32:101:39 | explicit conversion |
+| operators.cs:123:36:123:43 | explicit conversion |
diff --git a/csharp/ql/test/library-tests/operators/Operators5.expected b/csharp/ql/test/library-tests/operators/Operators5.expected
index 8e506e5119df..900b5170c349 100644
--- a/csharp/ql/test/library-tests/operators/Operators5.expected
+++ b/csharp/ql/test/library-tests/operators/Operators5.expected
@@ -1,15 +1,15 @@
-| operators.cs:23:30:23:31 | += | operators.cs:61:13:61:22 | ... += ... |
-| operators.cs:31:38:31:39 | checked += | operators.cs:77:17:77:26 | ... += ... |
-| operators.cs:33:38:33:39 | checked -= | operators.cs:78:17:78:26 | ... -= ... |
-| operators.cs:34:30:34:31 | -= | operators.cs:64:13:64:22 | ... -= ... |
-| operators.cs:36:38:36:39 | checked *= | operators.cs:79:17:79:26 | ... *= ... |
-| operators.cs:37:30:37:31 | *= | operators.cs:65:13:65:22 | ... *= ... |
-| operators.cs:39:38:39:39 | checked /= | operators.cs:80:17:80:26 | ... /= ... |
-| operators.cs:40:30:40:31 | /= | operators.cs:66:13:66:22 | ... /= ... |
-| operators.cs:42:30:42:31 | %= | operators.cs:67:13:67:22 | ... %= ... |
-| operators.cs:43:30:43:31 | &= | operators.cs:68:13:68:22 | ... &= ... |
-| operators.cs:44:30:44:31 | \|= | operators.cs:69:13:69:22 | ... \|= ... |
-| operators.cs:45:30:45:31 | ^= | operators.cs:70:13:70:22 | ... ^= ... |
-| operators.cs:46:30:46:32 | <<= | operators.cs:71:13:71:23 | ... <<= ... |
-| operators.cs:47:30:47:32 | >>= | operators.cs:72:13:72:23 | ... >>= ... |
-| operators.cs:48:30:48:33 | >>>= | operators.cs:73:13:73:24 | ... >>>= ... |
+| operators.cs:23:30:23:31 | += | operators.cs:70:13:70:22 | ... += ... |
+| operators.cs:31:38:31:39 | checked += | operators.cs:86:17:86:26 | ... += ... |
+| operators.cs:33:38:33:39 | checked -= | operators.cs:87:17:87:26 | ... -= ... |
+| operators.cs:34:30:34:31 | -= | operators.cs:73:13:73:22 | ... -= ... |
+| operators.cs:36:38:36:39 | checked *= | operators.cs:88:17:88:26 | ... *= ... |
+| operators.cs:37:30:37:31 | *= | operators.cs:74:13:74:22 | ... *= ... |
+| operators.cs:39:38:39:39 | checked /= | operators.cs:89:17:89:26 | ... /= ... |
+| operators.cs:40:30:40:31 | /= | operators.cs:75:13:75:22 | ... /= ... |
+| operators.cs:42:30:42:31 | %= | operators.cs:76:13:76:22 | ... %= ... |
+| operators.cs:43:30:43:31 | &= | operators.cs:77:13:77:22 | ... &= ... |
+| operators.cs:44:30:44:31 | \|= | operators.cs:78:13:78:22 | ... \|= ... |
+| operators.cs:45:30:45:31 | ^= | operators.cs:79:13:79:22 | ... ^= ... |
+| operators.cs:46:30:46:32 | <<= | operators.cs:80:13:80:23 | ... <<= ... |
+| operators.cs:47:30:47:32 | >>= | operators.cs:81:13:81:23 | ... >>= ... |
+| operators.cs:48:30:48:33 | >>>= | operators.cs:82:13:82:24 | ... >>>= ... |
diff --git a/csharp/ql/test/library-tests/operators/Operators6.expected b/csharp/ql/test/library-tests/operators/Operators6.expected
new file mode 100644
index 000000000000..f0878a511d9b
--- /dev/null
+++ b/csharp/ql/test/library-tests/operators/Operators6.expected
@@ -0,0 +1,10 @@
+| operators.cs:15:42:15:43 | ++ | operators.cs:66:19:66:23 | call to operator ++ |
+| operators.cs:15:42:15:43 | ++ | operators.cs:67:19:67:23 | call to operator ++ |
+| operators.cs:54:38:54:39 | checked ++ | operators.cs:100:17:100:19 | call to operator checked ++ |
+| operators.cs:54:38:54:39 | checked ++ | operators.cs:101:17:101:19 | call to operator checked ++ |
+| operators.cs:55:30:55:31 | ++ | operators.cs:93:13:93:15 | call to operator ++ |
+| operators.cs:55:30:55:31 | ++ | operators.cs:94:13:94:15 | call to operator ++ |
+| operators.cs:56:38:56:39 | checked -- | operators.cs:102:17:102:19 | call to operator checked -- |
+| operators.cs:56:38:56:39 | checked -- | operators.cs:103:17:103:19 | call to operator checked -- |
+| operators.cs:57:30:57:31 | -- | operators.cs:95:13:95:15 | call to operator -- |
+| operators.cs:57:30:57:31 | -- | operators.cs:96:13:96:15 | call to operator -- |
diff --git a/csharp/ql/test/library-tests/operators/Operators6.ql b/csharp/ql/test/library-tests/operators/Operators6.ql
new file mode 100644
index 000000000000..0eb578a11340
--- /dev/null
+++ b/csharp/ql/test/library-tests/operators/Operators6.ql
@@ -0,0 +1,17 @@
+/**
+ * @name Test for operators
+ */
+
+import csharp
+
+from Operator op, OperatorCall call
+where
+ op.fromSource() and
+ (
+ op instanceof IncrementOperator or
+ op instanceof CheckedIncrementOperator or
+ op instanceof DecrementOperator or
+ op instanceof CheckedDecrementOperator
+ ) and
+ call.getTarget() = op
+select op, call
diff --git a/csharp/ql/test/library-tests/operators/PrintAst.expected b/csharp/ql/test/library-tests/operators/PrintAst.expected
index 8ea38d79b148..2087e5f96dc3 100644
--- a/csharp/ql/test/library-tests/operators/PrintAst.expected
+++ b/csharp/ql/test/library-tests/operators/PrintAst.expected
@@ -181,159 +181,204 @@ operators.cs:
# 48| 0: [Parameter] n
# 48| -1: [TypeMention] IntVector
# 48| 4: [BlockStmt] {...}
-# 51| 2: [Class] TestOperator
-# 53| 6: [Method] Main
-# 53| -1: [TypeMention] Void
+# 51| 2: [Class] C
+# 54| 6: [CheckedIncrementOperator] checked ++
+# 54| -1: [TypeMention] Void
# 54| 4: [BlockStmt] {...}
-# 55| 0: [LocalVariableDeclStmt] ... ...;
-# 55| 0: [LocalVariableDeclAndInitExpr] IntVector iv1 = ...
-# 55| -1: [TypeMention] IntVector
-# 55| 0: [LocalVariableAccess] access to local variable iv1
-# 55| 1: [ObjectCreation] object creation of type IntVector
-# 55| -1: [TypeMention] IntVector
-# 55| 0: [IntLiteral] 4
-# 56| 1: [LocalVariableDeclStmt] ... ...;
-# 56| 0: [LocalVariableDeclExpr] IntVector iv2
-# 56| 0: [TypeMention] IntVector
-# 57| 2: [ExprStmt] ...;
-# 57| 0: [AssignExpr] ... = ...
-# 57| 0: [LocalVariableAccess] access to local variable iv2
-# 57| 1: [OperatorCall] call to operator ++
-# 57| 0: [LocalVariableAccess] access to local variable iv1
-# 58| 3: [ExprStmt] ...;
-# 58| 0: [AssignExpr] ... = ...
-# 58| 0: [LocalVariableAccess] access to local variable iv2
-# 58| 1: [OperatorCall] call to operator ++
-# 58| 0: [LocalVariableAccess] access to local variable iv1
-# 60| 4: [LocalVariableDeclStmt] ... ...;
-# 60| 0: [LocalVariableDeclAndInitExpr] IntVector iv3 = ...
-# 60| -1: [TypeMention] IntVector
-# 60| 0: [LocalVariableAccess] access to local variable iv3
-# 60| 1: [ObjectCreation] object creation of type IntVector
-# 60| -1: [TypeMention] IntVector
-# 60| 0: [IntLiteral] 4
-# 61| 5: [ExprStmt] ...;
-# 61| 0: [AssignAddExpr] ... += ...
-# 61| 0: [LocalVariableAccess] access to local variable iv3
-# 61| 1: [LocalVariableAccess] access to local variable iv2
-# 64| 6: [ExprStmt] ...;
-# 64| 0: [AssignSubExpr] ... -= ...
-# 64| 0: [LocalVariableAccess] access to local variable iv3
-# 64| 1: [LocalVariableAccess] access to local variable iv2
-# 65| 7: [ExprStmt] ...;
-# 65| 0: [AssignMulExpr] ... *= ...
-# 65| 0: [LocalVariableAccess] access to local variable iv3
-# 65| 1: [LocalVariableAccess] access to local variable iv2
-# 66| 8: [ExprStmt] ...;
-# 66| 0: [AssignDivExpr] ... /= ...
-# 66| 0: [LocalVariableAccess] access to local variable iv3
-# 66| 1: [LocalVariableAccess] access to local variable iv2
-# 67| 9: [ExprStmt] ...;
-# 67| 0: [AssignRemExpr] ... %= ...
-# 67| 0: [LocalVariableAccess] access to local variable iv3
-# 67| 1: [LocalVariableAccess] access to local variable iv2
-# 68| 10: [ExprStmt] ...;
-# 68| 0: [AssignAndExpr] ... &= ...
-# 68| 0: [LocalVariableAccess] access to local variable iv3
-# 68| 1: [LocalVariableAccess] access to local variable iv2
-# 69| 11: [ExprStmt] ...;
-# 69| 0: [AssignOrExpr] ... |= ...
+# 55| 7: [IncrementOperator] ++
+# 55| -1: [TypeMention] Void
+# 55| 4: [BlockStmt] {...}
+# 56| 8: [CheckedDecrementOperator] checked --
+# 56| -1: [TypeMention] Void
+# 56| 4: [BlockStmt] {...}
+# 57| 9: [DecrementOperator] --
+# 57| -1: [TypeMention] Void
+# 57| 4: [BlockStmt] {...}
+# 60| 3: [Class] TestOperator
+# 62| 6: [Method] Main
+# 62| -1: [TypeMention] Void
+# 63| 4: [BlockStmt] {...}
+# 64| 0: [LocalVariableDeclStmt] ... ...;
+# 64| 0: [LocalVariableDeclAndInitExpr] IntVector iv1 = ...
+# 64| -1: [TypeMention] IntVector
+# 64| 0: [LocalVariableAccess] access to local variable iv1
+# 64| 1: [ObjectCreation] object creation of type IntVector
+# 64| -1: [TypeMention] IntVector
+# 64| 0: [IntLiteral] 4
+# 65| 1: [LocalVariableDeclStmt] ... ...;
+# 65| 0: [LocalVariableDeclExpr] IntVector iv2
+# 65| 0: [TypeMention] IntVector
+# 66| 2: [ExprStmt] ...;
+# 66| 0: [AssignExpr] ... = ...
+# 66| 0: [LocalVariableAccess] access to local variable iv2
+# 66| 1: [OperatorCall] call to operator ++
+# 66| 0: [LocalVariableAccess] access to local variable iv1
+# 67| 3: [ExprStmt] ...;
+# 67| 0: [AssignExpr] ... = ...
+# 67| 0: [LocalVariableAccess] access to local variable iv2
+# 67| 1: [OperatorCall] call to operator ++
+# 67| 0: [LocalVariableAccess] access to local variable iv1
+# 69| 4: [LocalVariableDeclStmt] ... ...;
+# 69| 0: [LocalVariableDeclAndInitExpr] IntVector iv3 = ...
+# 69| -1: [TypeMention] IntVector
# 69| 0: [LocalVariableAccess] access to local variable iv3
-# 69| 1: [LocalVariableAccess] access to local variable iv2
-# 70| 12: [ExprStmt] ...;
-# 70| 0: [AssignXorExpr] ... ^= ...
+# 69| 1: [ObjectCreation] object creation of type IntVector
+# 69| -1: [TypeMention] IntVector
+# 69| 0: [IntLiteral] 4
+# 70| 5: [ExprStmt] ...;
+# 70| 0: [AssignAddExpr] ... += ...
# 70| 0: [LocalVariableAccess] access to local variable iv3
# 70| 1: [LocalVariableAccess] access to local variable iv2
-# 71| 13: [ExprStmt] ...;
-# 71| 0: [AssignLeftShiftExpr] ... <<= ...
-# 71| 0: [LocalVariableAccess] access to local variable iv3
-# 71| 1: [LocalVariableAccess] access to local variable iv2
-# 72| 14: [ExprStmt] ...;
-# 72| 0: [AssignRightShiftExpr] ... >>= ...
-# 72| 0: [LocalVariableAccess] access to local variable iv3
-# 72| 1: [LocalVariableAccess] access to local variable iv2
-# 73| 15: [ExprStmt] ...;
-# 73| 0: [AssignUnsignedRightShiftExpr] ... >>>= ...
+# 73| 6: [ExprStmt] ...;
+# 73| 0: [AssignSubExpr] ... -= ...
# 73| 0: [LocalVariableAccess] access to local variable iv3
# 73| 1: [LocalVariableAccess] access to local variable iv2
-# 75| 16: [CheckedStmt] checked {...}
-# 76| 0: [BlockStmt] {...}
-# 77| 0: [ExprStmt] ...;
-# 77| 0: [AssignAddExpr] ... += ...
-# 77| 0: [LocalVariableAccess] access to local variable iv3
-# 77| 1: [LocalVariableAccess] access to local variable iv2
-# 78| 1: [ExprStmt] ...;
-# 78| 0: [AssignSubExpr] ... -= ...
-# 78| 0: [LocalVariableAccess] access to local variable iv3
-# 78| 1: [LocalVariableAccess] access to local variable iv2
-# 79| 2: [ExprStmt] ...;
-# 79| 0: [AssignMulExpr] ... *= ...
-# 79| 0: [LocalVariableAccess] access to local variable iv3
-# 79| 1: [LocalVariableAccess] access to local variable iv2
-# 80| 3: [ExprStmt] ...;
-# 80| 0: [AssignDivExpr] ... /= ...
-# 80| 0: [LocalVariableAccess] access to local variable iv3
-# 80| 1: [LocalVariableAccess] access to local variable iv2
-# 85| 3: [Struct] Digit
-# 87| 6: [Field] value
-# 87| -1: [TypeMention] byte
-# 89| 7: [InstanceConstructor] Digit
-#-----| 2: (Parameters)
-# 89| 0: [Parameter] value
-# 89| -1: [TypeMention] byte
-# 90| 4: [BlockStmt] {...}
-# 91| 0: [IfStmt] if (...) ...
-# 91| 0: [LogicalOrExpr] ... || ...
-# 91| 0: [LTExpr] ... < ...
-# 91| 0: [CastExpr] (...) ...
-# 91| 1: [ParameterAccess] access to parameter value
-# 91| 1: [IntLiteral] 0
-# 91| 1: [GTExpr] ... > ...
-# 91| 0: [CastExpr] (...) ...
-# 91| 1: [ParameterAccess] access to parameter value
-# 91| 1: [IntLiteral] 9
-# 92| 1: [ThrowStmt] throw ...;
-# 92| 0: [ObjectCreation] object creation of type ArgumentException
-# 92| 0: [TypeMention] ArgumentException
-# 93| 1: [ExprStmt] ...;
-# 93| 0: [AssignExpr] ... = ...
-# 93| 0: [FieldAccess] access to field value
-# 93| -1: [ThisAccess] this access
-# 93| 1: [ParameterAccess] access to parameter value
-# 96| 8: [ImplicitConversionOperator] implicit conversion
-# 96| -1: [TypeMention] byte
-#-----| 2: (Parameters)
-# 96| 0: [Parameter] d
-# 96| -1: [TypeMention] Digit
-# 97| 4: [BlockStmt] {...}
-# 98| 0: [ReturnStmt] return ...;
-# 98| 0: [FieldAccess] access to field value
-# 98| -1: [ParameterAccess] access to parameter d
-# 101| 9: [ExplicitConversionOperator] explicit conversion
-# 101| -1: [TypeMention] Digit
-#-----| 2: (Parameters)
-# 101| 0: [Parameter] b
-# 101| -1: [TypeMention] byte
-# 102| 4: [BlockStmt] {...}
-# 103| 0: [ReturnStmt] return ...;
-# 103| 0: [ObjectCreation] object creation of type Digit
-# 103| -1: [TypeMention] Digit
-# 103| 0: [ParameterAccess] access to parameter b
-# 108| 4: [Class] TestConversionOperator
-# 111| 6: [Method] Main
-# 111| -1: [TypeMention] Void
-# 112| 4: [BlockStmt] {...}
-# 113| 0: [LocalVariableDeclStmt] ... ...;
-# 113| 0: [LocalVariableDeclAndInitExpr] Digit d = ...
-# 113| -1: [TypeMention] Digit
-# 113| 0: [LocalVariableAccess] access to local variable d
-# 113| 1: [OperatorCall] call to operator explicit conversion
-# 113| -1: [TypeMention] Digit
-# 113| 0: [CastExpr] (...) ...
-# 113| 1: [IntLiteral] 8
-# 114| 1: [LocalVariableDeclStmt] ... ...;
-# 114| 0: [LocalVariableDeclAndInitExpr] Byte b = ...
-# 114| -1: [TypeMention] byte
-# 114| 0: [LocalVariableAccess] access to local variable b
-# 114| 1: [OperatorCall] call to operator implicit conversion
-# 114| 0: [LocalVariableAccess] access to local variable d
+# 74| 7: [ExprStmt] ...;
+# 74| 0: [AssignMulExpr] ... *= ...
+# 74| 0: [LocalVariableAccess] access to local variable iv3
+# 74| 1: [LocalVariableAccess] access to local variable iv2
+# 75| 8: [ExprStmt] ...;
+# 75| 0: [AssignDivExpr] ... /= ...
+# 75| 0: [LocalVariableAccess] access to local variable iv3
+# 75| 1: [LocalVariableAccess] access to local variable iv2
+# 76| 9: [ExprStmt] ...;
+# 76| 0: [AssignRemExpr] ... %= ...
+# 76| 0: [LocalVariableAccess] access to local variable iv3
+# 76| 1: [LocalVariableAccess] access to local variable iv2
+# 77| 10: [ExprStmt] ...;
+# 77| 0: [AssignAndExpr] ... &= ...
+# 77| 0: [LocalVariableAccess] access to local variable iv3
+# 77| 1: [LocalVariableAccess] access to local variable iv2
+# 78| 11: [ExprStmt] ...;
+# 78| 0: [AssignOrExpr] ... |= ...
+# 78| 0: [LocalVariableAccess] access to local variable iv3
+# 78| 1: [LocalVariableAccess] access to local variable iv2
+# 79| 12: [ExprStmt] ...;
+# 79| 0: [AssignXorExpr] ... ^= ...
+# 79| 0: [LocalVariableAccess] access to local variable iv3
+# 79| 1: [LocalVariableAccess] access to local variable iv2
+# 80| 13: [ExprStmt] ...;
+# 80| 0: [AssignLeftShiftExpr] ... <<= ...
+# 80| 0: [LocalVariableAccess] access to local variable iv3
+# 80| 1: [LocalVariableAccess] access to local variable iv2
+# 81| 14: [ExprStmt] ...;
+# 81| 0: [AssignRightShiftExpr] ... >>= ...
+# 81| 0: [LocalVariableAccess] access to local variable iv3
+# 81| 1: [LocalVariableAccess] access to local variable iv2
+# 82| 15: [ExprStmt] ...;
+# 82| 0: [AssignUnsignedRightShiftExpr] ... >>>= ...
+# 82| 0: [LocalVariableAccess] access to local variable iv3
+# 82| 1: [LocalVariableAccess] access to local variable iv2
+# 84| 16: [CheckedStmt] checked {...}
+# 85| 0: [BlockStmt] {...}
+# 86| 0: [ExprStmt] ...;
+# 86| 0: [AssignAddExpr] ... += ...
+# 86| 0: [LocalVariableAccess] access to local variable iv3
+# 86| 1: [LocalVariableAccess] access to local variable iv2
+# 87| 1: [ExprStmt] ...;
+# 87| 0: [AssignSubExpr] ... -= ...
+# 87| 0: [LocalVariableAccess] access to local variable iv3
+# 87| 1: [LocalVariableAccess] access to local variable iv2
+# 88| 2: [ExprStmt] ...;
+# 88| 0: [AssignMulExpr] ... *= ...
+# 88| 0: [LocalVariableAccess] access to local variable iv3
+# 88| 1: [LocalVariableAccess] access to local variable iv2
+# 89| 3: [ExprStmt] ...;
+# 89| 0: [AssignDivExpr] ... /= ...
+# 89| 0: [LocalVariableAccess] access to local variable iv3
+# 89| 1: [LocalVariableAccess] access to local variable iv2
+# 92| 17: [LocalVariableDeclStmt] ... ...;
+# 92| 0: [LocalVariableDeclAndInitExpr] C c = ...
+# 92| -1: [TypeMention] C
+# 92| 0: [LocalVariableAccess] access to local variable c
+# 92| 1: [ObjectCreation] object creation of type C
+# 92| 0: [TypeMention] C
+# 93| 18: [ExprStmt] ...;
+# 93| 0: [OperatorCall] call to operator ++
+# 93| 0: [LocalVariableAccess] access to local variable c
+# 94| 19: [ExprStmt] ...;
+# 94| 0: [OperatorCall] call to operator ++
+# 94| 0: [LocalVariableAccess] access to local variable c
+# 95| 20: [ExprStmt] ...;
+# 95| 0: [OperatorCall] call to operator --
+# 95| 0: [LocalVariableAccess] access to local variable c
+# 96| 21: [ExprStmt] ...;
+# 96| 0: [OperatorCall] call to operator --
+# 96| 0: [LocalVariableAccess] access to local variable c
+# 98| 22: [CheckedStmt] checked {...}
+# 99| 0: [BlockStmt] {...}
+# 100| 0: [ExprStmt] ...;
+# 100| 0: [OperatorCall] call to operator checked ++
+# 100| 0: [LocalVariableAccess] access to local variable c
+# 101| 1: [ExprStmt] ...;
+# 101| 0: [OperatorCall] call to operator checked ++
+# 101| 0: [LocalVariableAccess] access to local variable c
+# 102| 2: [ExprStmt] ...;
+# 102| 0: [OperatorCall] call to operator checked --
+# 102| 0: [LocalVariableAccess] access to local variable c
+# 103| 3: [ExprStmt] ...;
+# 103| 0: [OperatorCall] call to operator checked --
+# 103| 0: [LocalVariableAccess] access to local variable c
+# 107| 7: [Struct] Digit
+# 109| 6: [Field] value
+# 109| -1: [TypeMention] byte
+# 111| 7: [InstanceConstructor] Digit
+#-----| 2: (Parameters)
+# 111| 0: [Parameter] value
+# 111| -1: [TypeMention] byte
+# 112| 4: [BlockStmt] {...}
+# 113| 0: [IfStmt] if (...) ...
+# 113| 0: [LogicalOrExpr] ... || ...
+# 113| 0: [LTExpr] ... < ...
+# 113| 0: [CastExpr] (...) ...
+# 113| 1: [ParameterAccess] access to parameter value
+# 113| 1: [IntLiteral] 0
+# 113| 1: [GTExpr] ... > ...
+# 113| 0: [CastExpr] (...) ...
+# 113| 1: [ParameterAccess] access to parameter value
+# 113| 1: [IntLiteral] 9
+# 114| 1: [ThrowStmt] throw ...;
+# 114| 0: [ObjectCreation] object creation of type ArgumentException
+# 114| 0: [TypeMention] ArgumentException
+# 115| 1: [ExprStmt] ...;
+# 115| 0: [AssignExpr] ... = ...
+# 115| 0: [FieldAccess] access to field value
+# 115| -1: [ThisAccess] this access
+# 115| 1: [ParameterAccess] access to parameter value
+# 118| 8: [ImplicitConversionOperator] implicit conversion
+# 118| -1: [TypeMention] byte
+#-----| 2: (Parameters)
+# 118| 0: [Parameter] d
+# 118| -1: [TypeMention] Digit
+# 119| 4: [BlockStmt] {...}
+# 120| 0: [ReturnStmt] return ...;
+# 120| 0: [FieldAccess] access to field value
+# 120| -1: [ParameterAccess] access to parameter d
+# 123| 9: [ExplicitConversionOperator] explicit conversion
+# 123| -1: [TypeMention] Digit
+#-----| 2: (Parameters)
+# 123| 0: [Parameter] b
+# 123| -1: [TypeMention] byte
+# 124| 4: [BlockStmt] {...}
+# 125| 0: [ReturnStmt] return ...;
+# 125| 0: [ObjectCreation] object creation of type Digit
+# 125| -1: [TypeMention] Digit
+# 125| 0: [ParameterAccess] access to parameter b
+# 130| 8: [Class] TestConversionOperator
+# 133| 6: [Method] Main
+# 133| -1: [TypeMention] Void
+# 134| 4: [BlockStmt] {...}
+# 135| 0: [LocalVariableDeclStmt] ... ...;
+# 135| 0: [LocalVariableDeclAndInitExpr] Digit d = ...
+# 135| -1: [TypeMention] Digit
+# 135| 0: [LocalVariableAccess] access to local variable d
+# 135| 1: [OperatorCall] call to operator explicit conversion
+# 135| -1: [TypeMention] Digit
+# 135| 0: [CastExpr] (...) ...
+# 135| 1: [IntLiteral] 8
+# 136| 1: [LocalVariableDeclStmt] ... ...;
+# 136| 0: [LocalVariableDeclAndInitExpr] Byte b = ...
+# 136| -1: [TypeMention] byte
+# 136| 0: [LocalVariableAccess] access to local variable b
+# 136| 1: [OperatorCall] call to operator implicit conversion
+# 136| 0: [LocalVariableAccess] access to local variable d
diff --git a/csharp/ql/test/library-tests/operators/operators.cs b/csharp/ql/test/library-tests/operators/operators.cs
index 3ff2fe1a26bf..22aee92e36d0 100644
--- a/csharp/ql/test/library-tests/operators/operators.cs
+++ b/csharp/ql/test/library-tests/operators/operators.cs
@@ -48,6 +48,15 @@ public IntVector(int length) { }
public void operator >>>=(IntVector n) { }
}
+ public class C
+ {
+ // Unary instance operators.
+ public void operator checked ++() { }
+ public void operator ++() { }
+ public void operator checked --() { }
+ public void operator --() { }
+ }
+
class TestOperator
{
void Main()
@@ -79,41 +88,55 @@ void Main()
iv3 *= iv2;
iv3 /= iv2;
}
- }
- }
- public struct Digit
- {
- byte value;
+ var c = new C();
+ c++;
+ ++c;
+ c--;
+ --c;
- public Digit(byte value)
- {
- if (value < 0 || value > 9)
- throw new ArgumentException();
- this.value = value;
+ checked
+ {
+ c++;
+ ++c;
+ c--;
+ --c;
+ }
}
- public static implicit operator byte(Digit d)
+ public struct Digit
{
- return d.value;
- }
+ byte value;
- public static explicit operator Digit(byte b)
- {
- return new Digit(b);
- }
+ public Digit(byte value)
+ {
+ if (value < 0 || value > 9)
+ throw new ArgumentException();
+ this.value = value;
+ }
- }
+ public static implicit operator byte(Digit d)
+ {
+ return d.value;
+ }
- class TestConversionOperator
- {
+ public static explicit operator Digit(byte b)
+ {
+ return new Digit(b);
+ }
- void Main()
+ }
+
+ class TestConversionOperator
{
- Digit d = (Digit)8;
- byte b = d;
+
+ void Main()
+ {
+ Digit d = (Digit)8;
+ byte b = d;
+ }
+
}
}
-
}