diff --git a/mdoc/Mono.Documentation/Updater/Formatters/CSharpFullMemberFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/CSharpFullMemberFormatter.cs index ec0f20573..898cc2c38 100644 --- a/mdoc/Mono.Documentation/Updater/Formatters/CSharpFullMemberFormatter.cs +++ b/mdoc/Mono.Documentation/Updater/Formatters/CSharpFullMemberFormatter.cs @@ -392,6 +392,9 @@ protected override StringBuilder AppendMethodName(StringBuilder buf, MethodDefin methodName = ifaceMethod.Name; } + // TODO this is liable to register false positives, but that's only likely to happen + // in an adversarial situation. Operators have to be public static, with the + // specialname bit set, with the right parameters for the type of operation. if (methodName.StartsWith("op_", StringComparison.Ordinal)) { // this is an operator @@ -399,6 +402,7 @@ protected override StringBuilder AppendMethodName(StringBuilder buf, MethodDefin { case "op_Implicit": case "op_Explicit": + case "op_CheckedExplicit": buf.Length--; // remove the last space, which assumes a member name is coming return buf; case "op_Addition": @@ -447,6 +451,22 @@ protected override StringBuilder AppendMethodName(StringBuilder buf, MethodDefin return buf.Append("operator >"); case "op_GreaterThanOrEqual": return buf.Append("operator >="); + case "op_UnsignedRightShift": + return buf.Append("operator >>>"); + case "op_CheckedIncrement": + return buf.Append("operator checked ++"); + case "op_CheckedDecrement": + return buf.Append("operator checked --"); + case "op_CheckedUnaryNegation": + return buf.Append("operator checked -"); + case "op_CheckedAddition": + return buf.Append("operator checked +"); + case "op_CheckedSubtraction": + return buf.Append("operator checked -"); + case "op_CheckedMultiply": + return buf.Append("operator checked *"); + case "op_CheckedDivision": + return buf.Append("operator checked /"); default: return buf.Append(methodName); } @@ -535,6 +555,9 @@ protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefin modifiers += buf.Length == 0 ? "readonly" : " readonly"; } + // TODO this is liable to register false positives, but that's only likely to happen + // in an adversarial situation. Operators have to be public static, with the + // specialname bit set, with the right parameters for the type of operation. switch (method.Name) { case "op_Implicit": @@ -543,6 +566,9 @@ protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefin case "op_Explicit": modifiers += " explicit operator"; break; + case "op_CheckedExplicit": + modifiers += " explicit operator checked"; + break; } return buf.Append (buf.Length == 0 ? modifiers.TrimStart() : modifiers); diff --git a/mdoc/mdoc.Test/FormatterTests.cs b/mdoc/mdoc.Test/FormatterTests.cs index 187c7fa27..9f2866289 100644 --- a/mdoc/mdoc.Test/FormatterTests.cs +++ b/mdoc/mdoc.Test/FormatterTests.cs @@ -80,6 +80,26 @@ public void CSharp_op_LeftShift () => public void CSharp_op_RightShift () => TestBinaryOp ("RightShift", ">>", secondType: "int"); + [Test] + public void CSharp_op_UnsignedRightShift () => + TestBinaryOp("UnsignedRightShift", ">>>", secondType: "int"); + + [Test] + public void CSharp_op_CheckedAddition () => + TestBinaryOp("CheckedAddition", "checked +"); + + [Test] + public void CSharp_op_CheckedSubtraction () => + TestBinaryOp("CheckedSubtraction", "checked -"); + + [Test] + public void CSharp_op_CheckedMultiply () => + TestBinaryOp("CheckedMultiply", "checked *"); + + [Test] + public void CSharp_op_CheckedDivision () => + TestBinaryOp("CheckedDivision", "checked /"); + [Test] public void CSharp_op_UnaryPlus () => TestUnaryOp ("UnaryPlus", "+"); @@ -112,6 +132,18 @@ public void CSharp_op_True () => public void CSharp_op_False () => TestUnaryOp ("False", "false", returnType: "bool"); + [Test] + public void CSharp_op_CheckedIncrement () => + TestUnaryOp("CheckedIncrement", "checked ++"); + + [Test] + public void CSharp_op_CheckedDecrement () => + TestUnaryOp("CheckedDecrement", "checked --"); + + [Test] + public void CSharp_op_CheckedUnaryNegation () => + TestUnaryOp("CheckedUnaryNegation", "checked -"); + [Test] public void CSharp_op_Equality () => TestComparisonOp ("Equality", "=="); @@ -152,6 +184,14 @@ public void CSharp_op_Explicit () => public void CSharp_op_Explicit_inverse () => TestConversionOp ("Explicit", "explicit", "TestClass", "int"); + [Test] + public void CSharp_op_Explicit_checked () => + TestConversionOp("CheckedExplicit", "explicit", "int", "TestClass"); + + [Test] + public void CSharp_op_Explicit_checked_inverse () => + TestConversionOp("CheckedExplicit", "explicit", "TestClass", "int"); + [Test] public void CSharp_modopt () => TestMod ("SomeFunc2", "public SomeClass* SomeFunc2 (SomeClass param);", returnType: "SomeClass*"); @@ -538,7 +578,7 @@ public void CSharpFuctionPointersUnmanagedExtTest(string methodName, string expe [TestCase("StaticVirtualMembers.StaticVirtualMemberInInterface`3", "StaticVirtualMethod", "public static virtual int StaticVirtualMethod (int left, int right);")] [TestCase("StaticVirtualMembers.StaticVirtualMemberInInterface`3", "op_Addition", "public static abstract TResult operator + (TSelf left, TOther right);")] - [TestCase("StaticVirtualMembers.StaticVirtualMemberInInterface`3", "op_CheckedAddition", "public static virtual TResult op_CheckedAddition (TSelf left, TOther right);")] + [TestCase("StaticVirtualMembers.StaticVirtualMemberInInterface`3", "op_CheckedAddition", "public static virtual TResult operator checked + (TSelf left, TOther right);")] [TestCase("StaticVirtualMembers.InterfaceI`1", "M", "public static abstract void M ();")] [TestCase("StaticVirtualMembers.InterfaceI`1", "M1", "public static virtual void M1 ();")] [TestCase("StaticVirtualMembers.InterfaceI`1", "op_Addition", "public static abstract T operator + (T l, T r);")] @@ -546,7 +586,7 @@ public void CSharpFuctionPointersUnmanagedExtTest(string methodName, string expe [TestCase("StaticVirtualMembers.InterfaceI`1", "op_Inequality", "public static abstract bool operator != (T l, T r);")] [TestCase("StaticVirtualMembers.InterfaceI`1", "op_Implicit", "public static abstract implicit operator T (string s);")] [TestCase("StaticVirtualMembers.InterfaceI`1", "op_Explicit", "public static abstract explicit operator string (T t);")] - [TestCase("StaticVirtualMembers.InterfaceI`1", "op_CheckedAddition", "public static virtual T op_CheckedAddition (T l, T r);")] + [TestCase("StaticVirtualMembers.InterfaceI`1", "op_CheckedAddition", "public static virtual T operator checked + (T l, T r);")] public void CSharpStaticVirtualMethodTest(string typeFullName, string methodName, string expectedSignature) { var staticVirtualMemberDllPath = "../../../../external/Test/StaticVirtualMembers.dll"; @@ -612,7 +652,7 @@ string RealTypeName(string name){ void TestConversionOp (string name, string type, string leftType, string rightType) { - TestOp (name, $"public static {type} operator {leftType} ({rightType} c1);", argCount: 1, returnType: leftType); + TestOp (name, $"public static {type} operator {(name.StartsWith("Checked") ? "checked " : "")}{leftType} ({rightType} c1);", argCount: 1, returnType: leftType); } void TestComparisonOp (string name, string op) diff --git a/mdoc/mdoc.Test/SampleClasses/TestClass.cs b/mdoc/mdoc.Test/SampleClasses/TestClass.cs index 443545962..66ed08a50 100644 --- a/mdoc/mdoc.Test/SampleClasses/TestClass.cs +++ b/mdoc/mdoc.Test/SampleClasses/TestClass.cs @@ -14,6 +14,9 @@ public TestClass () { } public static TestClass operator ~ (TestClass c1) { return new TestClass (); } public static TestClass operator ++ (TestClass c1) { return new TestClass (); } public static TestClass operator -- (TestClass c1) { return new TestClass (); } + public static TestClass operator checked ++ (TestClass c1) { return new TestClass (); } + public static TestClass operator checked -- (TestClass c1) { return new TestClass (); } + public static TestClass operator checked - (TestClass c1) { return new TestClass (); } // Binary Operators public static TestClass operator + (TestClass c1, TestClass c2) { return new TestClass (); } @@ -26,6 +29,11 @@ public TestClass () { } public static TestClass operator ^ (TestClass c1, TestClass c2) { return new TestClass (); } public static TestClass operator << (TestClass c1, int c2) { return new TestClass (); } public static TestClass operator >> (TestClass c1, int c2) { return new TestClass (); } + public static TestClass operator >>> (TestClass c1, int c2) { return new TestClass(); } + public static TestClass operator checked * (TestClass c1, TestClass c2) { return new TestClass(); } + public static TestClass operator checked / (TestClass c1, TestClass c2) { return new TestClass(); } + public static TestClass operator checked + (TestClass c1, TestClass c2) { return new TestClass(); } + public static TestClass operator checked - (TestClass c1, TestClass c2) { return new TestClass(); } // Comparison Operators public static bool operator true (TestClass c1) { return false; } @@ -42,6 +50,8 @@ public TestClass () { } public static implicit operator TestClass (TestClassTwo c1) { return new TestClass (); } public static explicit operator int (TestClass c1) { return 0; } public static explicit operator TestClass (int c1) { return new TestClass (); } + public static explicit operator checked TestClass (int c1) { return new TestClass (); } + public static explicit operator checked int (TestClass c1) { return 0; } public void DoSomethingWithParams (params int[] values) { } public void RefAndOut (ref int a, out int b) { b = 1; }