namespace BizHawk.Tests.Analyzers; using Verify = Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier< BizHawk.Analyzers.DefineCheckedOpAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>; [TestClass] public sealed class DefineCheckedOpAnalyzerTests { [TestMethod] public Task CheckMisuseOfCheckedOperatorOverloading() => Verify.VerifyAnalyzerAsync(""" /* very much a sample; real numeric types should follow these rules according to [the docs](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/arithmetic-operators#user-defined-checked-operators): * - A checked operator throws an OverflowException. * - An operator without the checked modifier returns an instance representing a truncated result. * I didn't do any of that, I just came up with something that threw and something that didn't and called it a day --yoshi */ using System; public struct GF7 { private static readonly int[] Inverses = [ -1, 1, 4, 5, 2, 3, 6 ]; public static GF7 operator checked -(GF7 x) => new(-x._value); // the most useless but here for completeness public static GF7 {|BHI1300:operator|} /*unchecked*/ -(GF7 x) => new(-x._value % 7); public static GF7 operator checked ++(GF7 x) => new(x._value + 1); public static GF7 {|BHI1300:operator|} /*unchecked*/ ++(GF7 x) => new((x._value + 1) % 7); public static GF7 operator checked --(GF7 x) => new(x._value - 1); public static GF7 {|BHI1300:operator|} /*unchecked*/ --(GF7 x) => new((x._value - 1) % 7); public static GF7 operator checked +(GF7 x, GF7 y) => new(x._value + y._value); public static GF7 {|BHI1300:operator|} /*unchecked*/ +(GF7 x, GF7 y) => new((x._value + y._value) % 7); public static GF7 operator checked +(GF7 x, int y) => new(x._value + y); public static GF7 {|BHI1300:operator|} /*unchecked*/ +(GF7 x, int y) => new((x._value + y) % 7); public static GF7 operator checked -(GF7 minuend, int subtrahend) => new(minuend._value - subtrahend); public static GF7 {|BHI1300:operator|} /*unchecked*/ -(GF7 minuend, int subtrahend) => new((minuend._value - subtrahend) % 7); public static GF7 operator checked *(GF7 x, int y) => new(x._value * y); public static GF7 {|BHI1300:operator|} /*unchecked*/ *(GF7 x, int y) => new((x._value * y) % 7); public static GF7 operator checked /(GF7 dividend, GF7 divisor) => new((dividend._value * Inverses[divisor._value]) % 7); public static GF7 {|BHI1300:operator|} /*unchecked*/ /(GF7 dividend, GF7 divisor) => divisor._value is 0 ? default : new((dividend._value * Inverses[divisor._value]) % 7); public static explicit operator checked GF7(int n) => new(n); public static explicit {|BHI1300:operator|} /*unchecked*/ GF7(int n) => new(n % 7); private byte _value; public byte Value { get => _value; set { if (value < 0 || 6 < value) throw new ArgumentOutOfRangeException(paramName: nameof(value), value, message: "must be in 0..<7"); _value = value; } } public GF7(byte value) => Value = value; private GF7(int value) => Value = unchecked((byte) value); } """); [TestMethod] public Task CheckMisuseOfUncheckedOperatorOverloading() => Verify.VerifyAnalyzerAsync(""" using System; public struct GF7 { private static readonly int[] Inverses = [ -1, 1, 4, 5, 2, 3, 6 ]; public static GF7 {|BHI1300:operator|} -(GF7 x) => new(-x._value % 7); public static GF7 {|BHI1300:operator|} ++(GF7 x) => new((x._value + 1) % 7); public static GF7 {|BHI1300:operator|} --(GF7 x) => new((x._value - 1) % 7); public static GF7 {|BHI1300:operator|} +(GF7 x, GF7 y) => new((x._value + y._value) % 7); public static GF7 {|BHI1300:operator|} +(GF7 x, int y) => new((x._value + y) % 7); public static GF7 {|BHI1300:operator|} -(GF7 minuend, int subtrahend) => new((minuend._value - subtrahend) % 7); public static GF7 {|BHI1300:operator|} *(GF7 x, int y) => new((x._value * y) % 7); public static GF7 {|BHI1300:operator|} /(GF7 dividend, GF7 divisor) => divisor._value is 0 ? default : new((dividend._value * Inverses[divisor._value]) % 7); public static explicit {|BHI1300:operator|} GF7(int n) => new(n % 7); private byte _value; public byte Value { get => _value; set { if (value < 0 || 6 < value) throw new ArgumentOutOfRangeException(paramName: nameof(value), value, message: "must be in 0..<7"); _value = value; } } public GF7(byte value) => Value = value; private GF7(int value) => Value = unchecked((byte) value); } """); }