Skip to content

Commit 82653a4

Browse files
rolfbjarneGitHub Copilot
andauthored
[Foundation] Fix nullability in NSMutableSet. (#24419)
This is file 31 of 47 files with nullability disabled in Foundation. Changes: * Enabled nullable reference types * Removed 'To be added' placeholder comments * Added comprehensive XML documentation for constructors and operators * Made operator parameters nullable to match actual usage patterns * Made subtraction operator return type nullable since it returns null for empty/null first operand * Added see cref attributes for better documentation * Add extensive tests. Contributes towards #17285. --------- Co-authored-by: GitHub Copilot <copilot@github.com>
1 parent 86004a9 commit 82653a4

File tree

3 files changed

+247
-14
lines changed

3 files changed

+247
-14
lines changed

src/Foundation/NSMutableSet.cs

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,22 @@
2929

3030
using System.Collections;
3131
using System.Collections.Generic;
32+
using System.Diagnostics.CodeAnalysis;
3233

33-
// Disable until we get around to enable + fix any issues.
34-
#nullable disable
34+
#nullable enable
3535

3636
namespace Foundation {
3737

3838
public partial class NSMutableSet : IEnumerable<NSObject> {
39-
/// <param name="objs">To be added.</param>
40-
/// <summary>To be added.</summary>
41-
/// <remarks>To be added.</remarks>
39+
/// <summary>Initializes a new mutable set with the specified objects.</summary>
40+
/// <param name="objs">The objects to add to the set.</param>
4241
public NSMutableSet (params NSObject [] objs)
4342
: this (NSArray.FromNSObjects (objs))
4443
{
4544
}
4645

47-
/// <param name="strings">To be added.</param>
48-
/// <summary>To be added.</summary>
49-
/// <remarks>To be added.</remarks>
46+
/// <summary>Initializes a new mutable set with the specified strings.</summary>
47+
/// <param name="strings">The strings to add to the set.</param>
5048
public NSMutableSet (params string [] strings)
5149
: this (NSArray.FromStrings (strings))
5250
{
@@ -57,10 +55,18 @@ internal NSMutableSet (params INativeObject [] objs)
5755
{
5856
}
5957

60-
public static NSMutableSet operator + (NSMutableSet first, NSMutableSet second)
58+
/// <summary>Creates a new mutable set containing all objects from both sets.</summary>
59+
/// <param name="first">The first set.</param>
60+
/// <param name="second">The second set.</param>
61+
/// <returns>A new <see cref="NSMutableSet" /> containing the union of both sets, or <see langword="null" /> if both sets are <see langword="null" />.</returns>
62+
[return: NotNullIfNotNull (nameof (first))]
63+
[return: NotNullIfNotNull (nameof (second))]
64+
public static NSMutableSet? operator + (NSMutableSet? first, NSMutableSet? second)
6165
{
66+
if (first is null && second is null)
67+
return null;
6268
if (first is null || first.Count == 0)
63-
return new NSMutableSet (second);
69+
return second is null ? new NSMutableSet () : new NSMutableSet (second);
6470
if (second is null || second.Count == 0)
6571
return new NSMutableSet (first);
6672

@@ -69,10 +75,17 @@ internal NSMutableSet (params INativeObject [] objs)
6975
return copy;
7076
}
7177

72-
public static NSMutableSet operator - (NSMutableSet first, NSMutableSet second)
78+
/// <summary>Creates a new mutable set with objects from the first set that are not in the second set.</summary>
79+
/// <param name="first">The first set.</param>
80+
/// <param name="second">The second set.</param>
81+
/// <returns>A new <see cref="NSMutableSet" /> containing the difference, or <see langword="null" /> if the first set is <see langword="null" />.</returns>
82+
[return: NotNullIfNotNull (nameof (first))]
83+
public static NSMutableSet? operator - (NSMutableSet? first, NSMutableSet? second)
7384
{
74-
if (first is null || first.Count == 0)
85+
if (first is null)
7586
return null;
87+
if (first.Count == 0)
88+
return new NSMutableSet ();
7689
if (second is null || second.Count == 0)
7790
return new NSMutableSet (first);
7891

tests/cecil-tests/Documentation.KnownFailures.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11877,8 +11877,6 @@ M:Foundation.NSKeyValueSharedObservers.#ctor(System.Type)
1187711877
M:Foundation.NSMachPort.Dispose(System.Boolean)
1187811878
M:Foundation.NSMetadataQuery.Dispose(System.Boolean)
1187911879
M:Foundation.NSMutableDictionary`2.FromObjectsAndKeys(`1[],`0[])
11880-
M:Foundation.NSMutableSet.op_Addition(Foundation.NSMutableSet,Foundation.NSMutableSet)
11881-
M:Foundation.NSMutableSet.op_Subtraction(Foundation.NSMutableSet,Foundation.NSMutableSet)
1188211880
M:Foundation.NSNetService.add_AddressResolved(System.EventHandler)
1188311881
M:Foundation.NSNetService.add_DidAcceptConnection(System.EventHandler{Foundation.NSNetServiceConnectionEventArgs})
1188411882
M:Foundation.NSNetService.add_Published(System.EventHandler)

tests/monotouch-test/Foundation/NSMutableSetTest.cs

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,227 @@ public void OperatorPlusReferenceTest ()
6464
Assert.AreNotEqual (IntPtr.Zero, one.Handle, "Handle must be != IntPtr.Zero");
6565
Assert.AreNotEqual (IntPtr.Zero, two.Handle, "Handle must be != IntPtr.Zero");
6666
}
67+
68+
[Test]
69+
public void OperatorAdd_BothNull ()
70+
{
71+
NSMutableSet first = null;
72+
NSMutableSet second = null;
73+
var result = first + second;
74+
Assert.IsNull (result, "BothNull should return null");
75+
}
76+
77+
[Test]
78+
public void OperatorAdd_FirstNull_SecondNonEmpty ()
79+
{
80+
NSMutableSet first = null;
81+
using (var second = new NSMutableSet ("1", "2"))
82+
using (var result = first + second) {
83+
Assert.IsNotNull (result, "FirstNull should return new set");
84+
Assert.AreEqual ((nuint) 2, result.Count, "FirstNull Count");
85+
Assert.IsTrue (result.Contains ("1"), "FirstNull Contains 1");
86+
Assert.IsTrue (result.Contains ("2"), "FirstNull Contains 2");
87+
}
88+
}
89+
90+
[Test]
91+
public void OperatorAdd_FirstNull_SecondEmpty ()
92+
{
93+
NSMutableSet first = null;
94+
using (var second = new NSMutableSet ())
95+
using (var result = first + second) {
96+
Assert.IsNotNull (result, "FirstNull SecondEmpty should return new set");
97+
Assert.AreEqual ((nuint) 0, result.Count, "FirstNull SecondEmpty Count");
98+
}
99+
}
100+
101+
[Test]
102+
public void OperatorAdd_FirstNonEmpty_SecondNull ()
103+
{
104+
using (var first = new NSMutableSet ("1", "2"))
105+
using (var result = first + null) {
106+
Assert.IsNotNull (result, "SecondNull should return new set");
107+
Assert.AreEqual ((nuint) 2, result.Count, "SecondNull Count");
108+
Assert.IsTrue (result.Contains ("1"), "SecondNull Contains 1");
109+
Assert.IsTrue (result.Contains ("2"), "SecondNull Contains 2");
110+
}
111+
}
112+
113+
[Test]
114+
public void OperatorAdd_FirstEmpty_SecondNull ()
115+
{
116+
using (var first = new NSMutableSet ())
117+
using (var result = first + null) {
118+
Assert.IsNotNull (result, "FirstEmpty SecondNull should return new set");
119+
Assert.AreEqual ((nuint) 0, result.Count, "FirstEmpty SecondNull Count");
120+
}
121+
}
122+
123+
[Test]
124+
public void OperatorAdd_FirstEmpty_SecondNonEmpty ()
125+
{
126+
using (var first = new NSMutableSet ())
127+
using (var second = new NSMutableSet ("1", "2"))
128+
using (var result = first + second) {
129+
Assert.IsNotNull (result, "FirstEmpty should return copy of second");
130+
Assert.AreEqual ((nuint) 2, result.Count, "FirstEmpty Count");
131+
Assert.IsTrue (result.Contains ("1"), "FirstEmpty Contains 1");
132+
Assert.IsTrue (result.Contains ("2"), "FirstEmpty Contains 2");
133+
}
134+
}
135+
136+
[Test]
137+
public void OperatorAdd_FirstNonEmpty_SecondEmpty ()
138+
{
139+
using (var first = new NSMutableSet ("1", "2"))
140+
using (var second = new NSMutableSet ())
141+
using (var result = first + second) {
142+
Assert.IsNotNull (result, "SecondEmpty should return copy of first");
143+
Assert.AreEqual ((nuint) 2, result.Count, "SecondEmpty Count");
144+
Assert.IsTrue (result.Contains ("1"), "SecondEmpty Contains 1");
145+
Assert.IsTrue (result.Contains ("2"), "SecondEmpty Contains 2");
146+
}
147+
}
148+
149+
[Test]
150+
public void OperatorAdd_BothEmpty ()
151+
{
152+
using (var first = new NSMutableSet ())
153+
using (var second = new NSMutableSet ())
154+
using (var result = first + second) {
155+
Assert.IsNotNull (result, "BothEmpty should return new empty set");
156+
Assert.AreEqual ((nuint) 0, result.Count, "BothEmpty Count");
157+
}
158+
}
159+
160+
[Test]
161+
public void OperatorAdd_WithOverlappingElements ()
162+
{
163+
using (var first = new NSMutableSet ("1", "2", "3"))
164+
using (var second = new NSMutableSet ("2", "3", "4"))
165+
using (var result = first + second) {
166+
Assert.IsNotNull (result, "Overlapping should return new set");
167+
Assert.AreEqual ((nuint) 4, result.Count, "Overlapping Count");
168+
Assert.IsTrue (result.Contains ("1"), "Overlapping Contains 1");
169+
Assert.IsTrue (result.Contains ("2"), "Overlapping Contains 2");
170+
Assert.IsTrue (result.Contains ("3"), "Overlapping Contains 3");
171+
Assert.IsTrue (result.Contains ("4"), "Overlapping Contains 4");
172+
}
173+
}
174+
175+
[Test]
176+
public void OperatorSubtract_FirstNull ()
177+
{
178+
NSMutableSet first = null;
179+
using (var second = new NSMutableSet ("1", "2")) {
180+
var result = first - second;
181+
Assert.IsNull (result, "FirstNull should return null");
182+
}
183+
}
184+
185+
[Test]
186+
public void OperatorSubtract_SecondNull ()
187+
{
188+
using (var first = new NSMutableSet ("1", "2"))
189+
using (var result = first - null) {
190+
Assert.IsNotNull (result, "SecondNull should return copy of first");
191+
Assert.AreEqual ((nuint) 2, result.Count, "SecondNull Count");
192+
Assert.IsTrue (result.Contains ("1"), "SecondNull Contains 1");
193+
Assert.IsTrue (result.Contains ("2"), "SecondNull Contains 2");
194+
}
195+
}
196+
197+
[Test]
198+
public void OperatorSubtract_BothNull ()
199+
{
200+
NSMutableSet first = null;
201+
NSMutableSet second = null;
202+
var result = first - second;
203+
Assert.IsNull (result, "BothNull should return null");
204+
}
205+
206+
[Test]
207+
public void OperatorSubtract_FirstEmpty ()
208+
{
209+
using (var first = new NSMutableSet ())
210+
using (var second = new NSMutableSet ("1", "2"))
211+
using (var result = first - second) {
212+
Assert.IsNotNull (result, "FirstEmpty should return empty set");
213+
Assert.AreEqual ((nuint) 0, result.Count, "FirstEmpty Count");
214+
}
215+
}
216+
217+
[Test]
218+
public void OperatorSubtract_SecondEmpty ()
219+
{
220+
using (var first = new NSMutableSet ("1", "2"))
221+
using (var second = new NSMutableSet ())
222+
using (var result = first - second) {
223+
Assert.IsNotNull (result, "SecondEmpty should return copy of first");
224+
Assert.AreEqual ((nuint) 2, result.Count, "SecondEmpty Count");
225+
Assert.IsTrue (result.Contains ("1"), "SecondEmpty Contains 1");
226+
Assert.IsTrue (result.Contains ("2"), "SecondEmpty Contains 2");
227+
}
228+
}
229+
230+
[Test]
231+
public void OperatorSubtract_BothEmpty ()
232+
{
233+
using (var first = new NSMutableSet ())
234+
using (var second = new NSMutableSet ())
235+
using (var result = first - second) {
236+
Assert.IsNotNull (result, "BothEmpty should return empty set");
237+
Assert.AreEqual ((nuint) 0, result.Count, "BothEmpty Count");
238+
}
239+
}
240+
241+
[Test]
242+
public void OperatorSubtract_NoOverlap ()
243+
{
244+
using (var first = new NSMutableSet ("1", "2"))
245+
using (var second = new NSMutableSet ("3", "4"))
246+
using (var result = first - second) {
247+
Assert.IsNotNull (result, "NoOverlap should return copy of first");
248+
Assert.AreEqual ((nuint) 2, result.Count, "NoOverlap Count");
249+
Assert.IsTrue (result.Contains ("1"), "NoOverlap Contains 1");
250+
Assert.IsTrue (result.Contains ("2"), "NoOverlap Contains 2");
251+
}
252+
}
253+
254+
[Test]
255+
public void OperatorSubtract_PartialOverlap ()
256+
{
257+
using (var first = new NSMutableSet ("1", "2", "3"))
258+
using (var second = new NSMutableSet ("2", "3", "4"))
259+
using (var result = first - second) {
260+
Assert.IsNotNull (result, "PartialOverlap should return difference");
261+
Assert.AreEqual ((nuint) 1, result.Count, "PartialOverlap Count");
262+
Assert.IsTrue (result.Contains ("1"), "PartialOverlap Contains 1");
263+
Assert.IsFalse (result.Contains ("2"), "PartialOverlap Not Contains 2");
264+
Assert.IsFalse (result.Contains ("3"), "PartialOverlap Not Contains 3");
265+
}
266+
}
267+
268+
[Test]
269+
public void OperatorSubtract_CompleteOverlap ()
270+
{
271+
using (var first = new NSMutableSet ("1", "2", "3"))
272+
using (var second = new NSMutableSet ("1", "2", "3"))
273+
using (var result = first - second) {
274+
Assert.IsNotNull (result, "CompleteOverlap should return empty set");
275+
Assert.AreEqual ((nuint) 0, result.Count, "CompleteOverlap Count");
276+
}
277+
}
278+
279+
[Test]
280+
public void OperatorSubtract_SecondIsSupersetOfFirst ()
281+
{
282+
using (var first = new NSMutableSet ("1", "2"))
283+
using (var second = new NSMutableSet ("1", "2", "3", "4"))
284+
using (var result = first - second) {
285+
Assert.IsNotNull (result, "Superset should return empty set");
286+
Assert.AreEqual ((nuint) 0, result.Count, "Superset Count");
287+
}
288+
}
67289
}
68290
}

0 commit comments

Comments
 (0)