From c20b948b3fed5ce2f66da5a4c17c354b5620b46e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 10:25:49 +0000 Subject: [PATCH 1/3] Initial plan From fdbc72a673a8e9f2ac6041290142526d929a8c98 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 10:34:49 +0000 Subject: [PATCH 2/3] Remove ObjectType parameter checks from System.qll methods Removes instanceof ObjectType checks from method signatures in System.qll to support nullable reference types (object?). This fixes false positives when Equals(object?) is used instead of Equals(object), as they are the same underlying type with different nullability annotations. Fixes: - SystemIComparableInterface.getCompareToMethod() - SystemObjectClass.getEqualsMethod() - SystemObjectClass.getStaticEqualsMethod() - SystemObjectClass.getReferenceEqualsMethod() Co-authored-by: hvitved <3667920+hvitved@users.noreply.github.com> --- .../semmle/code/csharp/frameworks/System.qll | 6 --- .../NullableTest.cs | 46 +++++++++++++++++++ 2 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 csharp/ql/test/query-tests/API Abuse/ClassDoesNotImplementEquals/NullableTest.cs diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/System.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/System.qll index 2681b9437b69..3872ad13dcb9 100644 --- a/csharp/ql/lib/semmle/code/csharp/frameworks/System.qll +++ b/csharp/ql/lib/semmle/code/csharp/frameworks/System.qll @@ -144,7 +144,6 @@ class SystemIComparableInterface extends SystemInterface { result.getDeclaringType() = this and result.hasName("CompareTo") and result.getNumberOfParameters() = 1 and - result.getParameter(0).getType() instanceof ObjectType and result.getReturnType() instanceof IntType } } @@ -263,7 +262,6 @@ class SystemObjectClass extends SystemClass instanceof ObjectType { result.getDeclaringType() = this and result.hasName("Equals") and result.getNumberOfParameters() = 1 and - result.getParameter(0).getType() instanceof ObjectType and result.getReturnType() instanceof BoolType } @@ -273,8 +271,6 @@ class SystemObjectClass extends SystemClass instanceof ObjectType { result.getDeclaringType() = this and result.hasName("Equals") and result.getNumberOfParameters() = 2 and - result.getParameter(0).getType() instanceof ObjectType and - result.getParameter(1).getType() instanceof ObjectType and result.getReturnType() instanceof BoolType } @@ -284,8 +280,6 @@ class SystemObjectClass extends SystemClass instanceof ObjectType { result.getDeclaringType() = this and result.hasName("ReferenceEquals") and result.getNumberOfParameters() = 2 and - result.getParameter(0).getType() instanceof ObjectType and - result.getParameter(1).getType() instanceof ObjectType and result.getReturnType() instanceof BoolType } diff --git a/csharp/ql/test/query-tests/API Abuse/ClassDoesNotImplementEquals/NullableTest.cs b/csharp/ql/test/query-tests/API Abuse/ClassDoesNotImplementEquals/NullableTest.cs new file mode 100644 index 000000000000..81c96940a909 --- /dev/null +++ b/csharp/ql/test/query-tests/API Abuse/ClassDoesNotImplementEquals/NullableTest.cs @@ -0,0 +1,46 @@ +using System; + +#nullable enable + +namespace Test +{ + class TestClass1 : IEquatable + { + private int field1; + + public bool Equals(TestClass1? param1) + { + return param1 != null && field1 == param1.field1; + } + + public override bool Equals(object? param2) + { + return param2 is TestClass1 tc && Equals(tc); + } + + public override int GetHashCode() + { + return field1; + } + } + + class TestClass2 + { + private string field2; + + public TestClass2(string s) + { + field2 = s; + } + + public override bool Equals(object? param3) + { + return param3 is TestClass2 tc && field2 == tc.field2; + } + + public override int GetHashCode() + { + return field2?.GetHashCode() ?? 0; + } + } +} From 0bc0c59a14f06a4d33b5f9914d0947e03384b440 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 10:35:29 +0000 Subject: [PATCH 3/3] Add test cases for nullable Equals methods Adds test cases for Equals methods with nullable reference type parameters (object?) to verify the fix works correctly. Co-authored-by: hvitved <3667920+hvitved@users.noreply.github.com> --- .../frameworks/system/Equals/Equals.cs | 13 +++++++++++++ .../frameworks/system/Equals/Equals.expected | 2 ++ 2 files changed, 15 insertions(+) diff --git a/csharp/ql/test/library-tests/frameworks/system/Equals/Equals.cs b/csharp/ql/test/library-tests/frameworks/system/Equals/Equals.cs index 504c81198355..02b9e6b340a6 100644 --- a/csharp/ql/test/library-tests/frameworks/system/Equals/Equals.cs +++ b/csharp/ql/test/library-tests/frameworks/system/Equals/Equals.cs @@ -24,3 +24,16 @@ struct Equals1Struct { public override bool Equals(object other) => false; } + +#nullable enable + +class NullableEquals1 +{ + public override bool Equals(object? other) => false; +} + +class NullableEquals2 : IEquatable +{ + public bool Equals(NullableEquals2? other) => other != null; + public override bool Equals(object? other) => other is NullableEquals2 n && Equals(n); +} diff --git a/csharp/ql/test/library-tests/frameworks/system/Equals/Equals.expected b/csharp/ql/test/library-tests/frameworks/system/Equals/Equals.expected index b05c8852b2bb..30dafbad3418 100644 --- a/csharp/ql/test/library-tests/frameworks/system/Equals/Equals.expected +++ b/csharp/ql/test/library-tests/frameworks/system/Equals/Equals.expected @@ -5,3 +5,5 @@ | Equals.cs:16:7:16:13 | Equals3 | Equals3.Equals(Equals3) | true | | Equals.cs:21:8:21:21 | NoEqualsStruct | System.ValueType.Equals(object) | false | | Equals.cs:23:8:23:20 | Equals1Struct | Equals1Struct.Equals(object) | true | +| Equals.cs:31:7:31:21 | NullableEquals1 | NullableEquals1.Equals(object) | true | +| Equals.cs:36:7:36:21 | NullableEquals2 | NullableEquals2.Equals(NullableEquals2) | true |