Skip to content

Commit 61c44ba

Browse files
Merge pull request #671 from rolfbjarne/objcmethoddecl-methodfamily
Add ObjCMethodDecl.MethodFamily.
2 parents 24a70e5 + fac8e65 commit 61c44ba

File tree

7 files changed

+196
-0
lines changed

7 files changed

+196
-0
lines changed

sources/ClangSharp.Interop/Extensions/CXCursor.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,6 +1201,8 @@ public readonly CXCursor DefaultArg
12011201

12021202
public readonly uint MaxAlignment => clangsharp.Cursor_getMaxAlignment(this);
12031203

1204+
public readonly ObjCMethodFamily MethodFamily => (ObjCMethodFamily)clangsharp.Cursor_getMethodFamily(this);
1205+
12041206
public readonly CXModule Module => (CXModule)clang.Cursor_getModule(this);
12051207

12061208
public readonly CXCursor MostRecentDecl => clangsharp.Cursor_getMostRecentDecl(this);
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
2+
3+
// Ported from https://github.com/llvm/llvm-project/tree/llvmorg-21.1.8/clang/include/clang-c
4+
// Original source is Copyright (c) the LLVM Project and Contributors. Licensed under the Apache License v2.0 with LLVM Exceptions. See NOTICE.txt in the project root for license information.
5+
6+
using System;
7+
8+
namespace ClangSharp.Interop;
9+
10+
public enum ObjCMethodFamily
11+
{
12+
None,
13+
Alloc,
14+
Copy,
15+
Init,
16+
MutableCopy,
17+
New,
18+
Autorelease,
19+
Dealloc,
20+
Finalize,
21+
Release,
22+
Retain,
23+
RetainCount,
24+
Self,
25+
Initialize,
26+
PerformSelector,
27+
}

sources/ClangSharp.Interop/clangsharp/clangsharp.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,10 @@ public static partial class @clangsharp
659659
[DllImport("libClangSharp", CallingConvention = CallingConvention.Cdecl, EntryPoint = "clangsharp_Cursor_getMethod", ExactSpelling = true)]
660660
public static extern CXCursor Cursor_getMethod(CXCursor C, [NativeTypeName("unsigned int")] uint i);
661661

662+
[DllImport("libClangSharp", CallingConvention = CallingConvention.Cdecl, EntryPoint = "clangsharp_Cursor_getMethodFamily", ExactSpelling = true)]
663+
[return: NativeTypeName("unsigned int")]
664+
public static extern uint Cursor_getMethodFamily(CXCursor C);
665+
662666
[DllImport("libClangSharp", CallingConvention = CallingConvention.Cdecl, EntryPoint = "clangsharp_Cursor_getMostRecentDecl", ExactSpelling = true)]
663667
public static extern CXCursor Cursor_getMostRecentDecl(CXCursor C);
664668

sources/ClangSharp/Cursors/Decls/ObjCMethodDecl.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ internal ObjCMethodDecl(CXCursor handle) : base(handle, handle.Kind, CX_DeclKind
4444

4545
public bool IsThisDeclarationADefinition => Handle.IsThisDeclarationADefinition;
4646

47+
public ObjCMethodFamily MethodFamily => Handle.MethodFamily;
48+
4749
public CXObjCDeclQualifierKind ObjCDeclQualifier => Handle.ObjCDeclQualifiers;
4850

4951
public IReadOnlyList<ParmVarDecl> Parameters => _parameters;

sources/libClangSharp/ClangSharp.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,6 +1331,19 @@ CX_ExprDependence clangsharp_Cursor_getExprDependence(CXCursor C) {
13311331
return CX_ED_None;
13321332
}
13331333

1334+
CLANGSHARP_LINKAGE unsigned clangsharp_Cursor_getMethodFamily(CXCursor C)
1335+
{
1336+
if (isDeclOrTU(C.kind)) {
1337+
const Decl* D = getCursorDecl(C);
1338+
1339+
if (const ObjCMethodDecl* OCMD = dyn_cast<ObjCMethodDecl>(D)) {
1340+
return OCMD->getMethodFamily();
1341+
}
1342+
}
1343+
1344+
return 0 /* OMF_None */;
1345+
}
1346+
13341347
int clangsharp_Cursor_getFieldIndex(CXCursor C) {
13351348
if (isDeclOrTU(C.kind)) {
13361349
const Decl* D = getCursorDecl(C);

sources/libClangSharp/ClangSharp.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,8 @@ CLANGSHARP_LINKAGE unsigned clangsharp_Cursor_getMaxAlignment(CXCursor C);
597597

598598
CLANGSHARP_LINKAGE CXCursor clangsharp_Cursor_getMethod(CXCursor C, unsigned i);
599599

600+
CLANGSHARP_LINKAGE unsigned clangsharp_Cursor_getMethodFamily(CXCursor C);
601+
600602
CLANGSHARP_LINKAGE CXCursor clangsharp_Cursor_getMostRecentDecl(CXCursor C);
601603

602604
CLANGSHARP_LINKAGE CXString clangsharp_Cursor_getName(CXCursor C);

tests/ClangSharp.UnitTests/ObjectiveCTest.cs

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
22

33
using System;
4+
using System.Collections.Generic;
45
using System.Linq;
56
using ClangSharp.Interop;
67
using NUnit.Framework;
@@ -111,6 +112,151 @@ @interface MyClass
111112
Assert.That(methodStaticMethod.Selector, Is.EqualTo("staticMethod"), "methodStaticMethod.Selector");
112113
}
113114

115+
[Test]
116+
public void Method_Family()
117+
{
118+
AssertNeedNewClangSharp();
119+
120+
var inputContents = $@"
121+
@interface NSObject
122+
-(void) none;
123+
-(void) noneWithArgument:(int)arg;
124+
-(void) something;
125+
-(void) autoreleaseNone;
126+
-(void) deallocNone;
127+
-(void) finalizeNone;
128+
-(void) releaseNone;
129+
-(void) retainNone;
130+
-(unsigned long) retainCountNone;
131+
-(instancetype) selfNone;
132+
-(instancetype) initializeNone;
133+
-(void) performSelectorNone;
134+
135+
+(instancetype) alloc;
136+
+(instancetype) allocWithZone:(void*)zone;
137+
+(instancetype) _alloc;
138+
+(instancetype) __alloc;
139+
+(instancetype) _allocWithZone:(void*)zone;
140+
141+
-(instancetype) copy;
142+
-(instancetype) copyWithZone:(void*)zone;
143+
-(instancetype) _copy;
144+
-(instancetype) __copyWithZone:(void*)zone;
145+
146+
-(instancetype) init;
147+
-(instancetype) initWithValue:(int)value;
148+
-(instancetype) _init;
149+
-(instancetype) __initWithValue:(int)value;
150+
151+
-(instancetype) mutableCopy;
152+
-(instancetype) mutableCopyWithZone:(void*)zone;
153+
-(instancetype) _mutableCopy;
154+
-(instancetype) __mutableCopyWithZone:(void*)zone;
155+
156+
+(instancetype) new;
157+
+(instancetype) newWithValue:(int)value;
158+
+(instancetype) _new;
159+
+(instancetype) __newWithValue:(int)value;
160+
161+
-(void) autorelease;
162+
-(void) dealloc;
163+
-(void) finalize;
164+
-(void) release;
165+
-(void) retain;
166+
-(unsigned long) retainCount;
167+
-(instancetype) self;
168+
+(void) initialize;
169+
-(id) performSelector:(SEL)selector;
170+
@end
171+
172+
@interface MyClass
173+
-(instancetype) instanceMethod;
174+
+(MyClass*) staticMethod;
175+
@end
176+
";
177+
178+
using var translationUnit = CreateTranslationUnit(inputContents, "objective-c++");
179+
180+
var classes = translationUnit.TranslationUnitDecl.Decls.OfType<ObjCInterfaceDecl>().ToList();
181+
Assert.That(classes.Count, Is.GreaterThanOrEqualTo(2), $"At least two classes");
182+
183+
foreach (var cls in classes)
184+
{
185+
var methods = cls.Methods.ToList();
186+
if (methods.Count == 0)
187+
{
188+
continue;
189+
}
190+
191+
var take = new Func<Func<string, bool>, ObjCMethodFamily, IEnumerable<ObjCMethodDecl>>((Func<string, bool> filter, ObjCMethodFamily family) => {
192+
var taken = new List<ObjCMethodDecl>();
193+
for (var v = methods.Count - 1; v >= 0; v--)
194+
{
195+
var method = methods[v];
196+
if (filter(method.Selector))
197+
{
198+
methods.RemoveAt(v);
199+
taken.Add(method);
200+
}
201+
}
202+
203+
Assert.That(taken.Count, Is.GreaterThanOrEqualTo(0), $"family {family} not found, methods remaining: {string.Join(", ", methods.Select(p => p.Selector))}");
204+
return taken;
205+
});
206+
207+
var assertFamily = new Action<Func<string, bool>, ObjCMethodFamily>((Func<string, bool> filter, ObjCMethodFamily family) => {
208+
var methodsInFamily = take(filter, family);
209+
foreach (var method in methodsInFamily)
210+
{
211+
Assert.That(method.MethodFamily, Is.EqualTo(family), $"MethodFamily for {method.Selector}");
212+
}
213+
});
214+
215+
var isSelectorFamily = new Func<string, string, bool>((string selector, string family) => {
216+
selector = selector.TrimStart('_');
217+
if (selector == family)
218+
{
219+
return true;
220+
}
221+
if (!selector.StartsWith(family, StringComparison.Ordinal))
222+
{
223+
return false;
224+
}
225+
var nextChar = selector[family.Length];
226+
if (nextChar == ':' || char.IsUpper(nextChar))
227+
{
228+
return true;
229+
}
230+
return false;
231+
});
232+
233+
Assert.Multiple(() => {
234+
assertFamily(s => isSelectorFamily(s, "alloc"), ObjCMethodFamily.Alloc);
235+
assertFamily(s => isSelectorFamily(s, "copy"), ObjCMethodFamily.Copy);
236+
assertFamily(s => isSelectorFamily(s, "init"), ObjCMethodFamily.Init);
237+
assertFamily(s => isSelectorFamily(s, "mutableCopy"), ObjCMethodFamily.MutableCopy);
238+
assertFamily(s => isSelectorFamily(s, "new"), ObjCMethodFamily.New);
239+
assertFamily(s => s == "autorelease", ObjCMethodFamily.Autorelease);
240+
assertFamily(s => s == "dealloc", ObjCMethodFamily.Dealloc);
241+
assertFamily(s => s == "finalize", ObjCMethodFamily.Finalize);
242+
assertFamily(s => s == "release", ObjCMethodFamily.Release);
243+
assertFamily(s => s == "retain", ObjCMethodFamily.Retain);
244+
assertFamily(s => s == "retainCount", ObjCMethodFamily.RetainCount);
245+
assertFamily(s => s == "self", ObjCMethodFamily.Self);
246+
assertFamily(s => s == "initialize", ObjCMethodFamily.Initialize);
247+
assertFamily(s => s.StartsWith("performSelector:", StringComparison.Ordinal), ObjCMethodFamily.PerformSelector);
248+
249+
Assert.That(methods.Count, Is.GreaterThan(0), $"No remaining methods in {cls.Name}");
250+
251+
// None of the remaining methods should belong to a family
252+
foreach (var method in methods)
253+
{
254+
Assert.That(method.MethodFamily, Is.EqualTo(ObjCMethodFamily.None), $"MethodFamily for {method.Selector}");
255+
}
256+
});
257+
}
258+
}
259+
114260
[Test]
115261
public void Category_TypeParamList()
116262
{

0 commit comments

Comments
 (0)