Skip to content

Commit b6d23fc

Browse files
committed
Improve Objective-C support and add tests
1 parent b75e842 commit b6d23fc

File tree

9 files changed

+426
-222
lines changed

9 files changed

+426
-222
lines changed

src/CppAst.Tests/TestObjectiveC.cs

Lines changed: 294 additions & 64 deletions
Large diffs are not rendered by default.

src/CppAst/CppClass.cs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Alexandre Mutel. All rights reserved.
1+
// Copyright (c) Alexandre Mutel. All rights reserved.
22
// Licensed under the BSD-Clause 2 license.
33
// See license.txt file in the project root for full license information.
44

@@ -11,7 +11,7 @@ namespace CppAst
1111
/// <summary>
1212
/// A C++ class, struct or union.
1313
/// </summary>
14-
public sealed class CppClass : CppTypeDeclaration, ICppMemberWithVisibility, ICppDeclarationContainer, ICppTemplateOwner
14+
public class CppClass : CppTypeDeclaration, ICppMemberWithVisibility, ICppDeclarationContainer, ICppTemplateOwner
1515
{
1616
/// <summary>
1717
/// Creates a new instance.
@@ -33,7 +33,8 @@ public CppClass(string name) : base(CppTypeKind.StructOrClass)
3333
TokenAttributes = new List<CppAttribute>();
3434
ObjCImplementedProtocols = new List<CppClass>();
3535
Properties = new CppContainerList<CppProperty>(this);
36-
ObjCCategories = new List<CppObjCCategory>();
36+
ObjCCategories = new List<CppClass>();
37+
ObjCCategoryName = string.Empty;
3738
}
3839

3940
/// <summary>
@@ -45,6 +46,16 @@ public CppClass(string name) : base(CppTypeKind.StructOrClass)
4546

4647
/// <inheritdoc />
4748
public string Name { get; set; }
49+
50+
/// <summary>
51+
/// Gets or sets the target of the Objective-C category. Null if this class is not an <see cref="CppClassKind.ObjCInterfaceCategory"/>.
52+
/// </summary>
53+
public CppClass ObjCCategoryTargetClass { get; set; }
54+
55+
/// <summary>
56+
/// Gets or sets the name of the Objective-C category. Empty if this class is not an <see cref="CppClassKind.ObjCInterfaceCategory"/>
57+
/// </summary>
58+
public string ObjCCategoryName { get; set; }
4859

4960
public override string FullName
5061
{
@@ -161,7 +172,7 @@ public override string FullName
161172
/// <summary>
162173
/// Gets the Objective-C categories of this instance.
163174
/// </summary>
164-
public List<CppObjCCategory> ObjCCategories { get; }
175+
public List<CppClass> ObjCCategories { get; }
165176

166177
/// <inheritdoc />
167178
public List<CppType> TemplateParameters { get; }
@@ -209,6 +220,7 @@ public override string ToString()
209220
builder.Append("union ");
210221
break;
211222
case CppClassKind.ObjCInterface:
223+
case CppClassKind.ObjCInterfaceCategory:
212224
builder.Append("@interface ");
213225
break;
214226
case CppClassKind.ObjCProtocol:
@@ -258,6 +270,11 @@ public override string ToString()
258270
builder.Append(baseType);
259271
}
260272
}
273+
274+
if (!string.IsNullOrEmpty(ObjCCategoryName))
275+
{
276+
builder.Append(" (").Append(ObjCCategoryName).Append(')');
277+
}
261278

262279
if (ObjCImplementedProtocols.Count > 0)
263280
{

src/CppAst/CppClassKind.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Alexandre Mutel. All rights reserved.
1+
// Copyright (c) Alexandre Mutel. All rights reserved.
22
// Licensed under the BSD-Clause 2 license.
33
// See license.txt file in the project root for full license information.
44

@@ -29,5 +29,9 @@ public enum CppClassKind
2929
/// An Objective-C `@protocol`
3030
/// </summary>
3131
ObjCProtocol,
32+
/// <summary>
33+
/// An Objective-C `@interface` with a category.
34+
/// </summary>
35+
ObjCInterfaceCategory,
3236
}
3337
}

src/CppAst/CppModelBuilder.cs

Lines changed: 56 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@
66
using System.Collections.Generic;
77
using System.Diagnostics;
88
using System.Linq;
9-
using System.Linq.Expressions;
109
using System.Runtime.InteropServices;
1110
using System.Text;
12-
using ClangSharp;
1311
using ClangSharp.Interop;
1412

1513
namespace CppAst
@@ -156,29 +154,6 @@ private CppContainerContext GetOrCreateDeclarationContainer(CXCursor cursor, voi
156154
symbol = cppEnum;
157155
break;
158156

159-
case CXCursorKind.CXCursor_ObjCCategoryDecl:
160-
{
161-
// Fetch the target class for the category
162-
CXCursor parentCursor = default;
163-
cursor.VisitChildren((cxCursor, parent, clientData) =>
164-
{
165-
if (cxCursor.Kind == CXCursorKind.CXCursor_ObjCClassRef)
166-
{
167-
parentCursor = cxCursor.Referenced;
168-
return CXChildVisitResult.CXChildVisit_Break;
169-
}
170-
171-
return CXChildVisitResult.CXChildVisit_Continue;
172-
}, default);
173-
174-
var parentContainer = GetOrCreateDeclarationContainer(parentCursor, data).Container;
175-
var targetClass = (CppClass)parentContainer;
176-
177-
var category = new CppObjCCategory(targetClass, CXUtil.GetCursorSpelling(cursor));
178-
targetClass.ObjCCategories.Add(category);
179-
symbol = category;
180-
break;
181-
}
182157

183158
case CXCursorKind.CXCursor_ClassTemplate:
184159
case CXCursorKind.CXCursor_ClassTemplatePartialSpecialization:
@@ -187,6 +162,7 @@ private CppContainerContext GetOrCreateDeclarationContainer(CXCursor cursor, voi
187162
case CXCursorKind.CXCursor_UnionDecl:
188163
case CXCursorKind.CXCursor_ObjCInterfaceDecl:
189164
case CXCursorKind.CXCursor_ObjCProtocolDecl:
165+
case CXCursorKind.CXCursor_ObjCCategoryDecl:
190166
var cppClass = new CppClass(CXUtil.GetCursorSpelling(cursor));
191167
parentDeclarationContainer.Classes.Add(cppClass);
192168
symbol = cppClass;
@@ -210,6 +186,33 @@ private CppContainerContext GetOrCreateDeclarationContainer(CXCursor cursor, voi
210186
case CXCursorKind.CXCursor_ObjCProtocolDecl:
211187
cppClass.ClassKind = CppClassKind.ObjCProtocol;
212188
break;
189+
case CXCursorKind.CXCursor_ObjCCategoryDecl:
190+
{
191+
cppClass.ClassKind = CppClassKind.ObjCInterfaceCategory;
192+
193+
// Fetch the target class for the category
194+
CXCursor parentCursor = default;
195+
cursor.VisitChildren((cxCursor, parent, clientData) =>
196+
{
197+
if (cxCursor.Kind == CXCursorKind.CXCursor_ObjCClassRef)
198+
{
199+
parentCursor = cxCursor.Referenced;
200+
return CXChildVisitResult.CXChildVisit_Break;
201+
}
202+
203+
return CXChildVisitResult.CXChildVisit_Continue;
204+
}, default);
205+
206+
var parentContainer = GetOrCreateDeclarationContainer(parentCursor, data).Container;
207+
var targetClass = (CppClass)parentContainer;
208+
cppClass.ObjCCategoryName = cppClass.Name;
209+
cppClass.Name = targetClass.Name;
210+
cppClass.ObjCCategoryTargetClass = targetClass;
211+
212+
// Link back
213+
targetClass.ObjCCategories.Add(cppClass);
214+
break;
215+
}
213216
}
214217

215218
cppClass.IsAbstract = cursor.CXXRecord_IsAbstract;
@@ -274,24 +277,7 @@ private CppContainerContext GetOrCreateDeclarationContainer(CXCursor cursor, voi
274277
}
275278
else
276279
{
277-
cursor.VisitChildren((childCursor, classCursor, clientData) =>
278-
{
279-
var tmplParam = TryToCreateTemplateParameters(childCursor, clientData);
280-
if (tmplParam != null)
281-
{
282-
cppClass.TemplateParameters.Add(tmplParam);
283-
if (cppClass.ClassKind == CppClassKind.ObjCInterface ||
284-
cppClass.ClassKind == CppClassKind.ObjCProtocol)
285-
{
286-
cppClass.TemplateKind = CppTemplateKind.ObjCGenericClass;
287-
}
288-
else
289-
{
290-
cppClass.TemplateKind = CppTemplateKind.TemplateClass;
291-
}
292-
}
293-
return CXChildVisitResult.CXChildVisit_Continue;
294-
}, new CXClientData((IntPtr)data));
280+
AddTemplateParameters(cursor, cppClass);
295281
}
296282

297283
defaultContainerVisibility = cursor.Kind == CXCursorKind.CXCursor_ClassDecl ? CppVisibility.Private : CppVisibility.Public;
@@ -320,6 +306,32 @@ private CppContainerContext GetOrCreateDeclarationContainer(CXCursor cursor, voi
320306
return containerContext;
321307
}
322308

309+
private void AddTemplateParameters(CXCursor cursor, ICppTemplateOwner templateOwner)
310+
{
311+
cursor.VisitChildren((childCursor, classCursor, clientData) =>
312+
{
313+
var tmplParam = TryToCreateTemplateParameters(childCursor, clientData);
314+
if (tmplParam != null)
315+
{
316+
templateOwner.TemplateParameters.Add(tmplParam);
317+
if (templateOwner is CppClass cppClass)
318+
{
319+
if (cppClass.ClassKind == CppClassKind.ObjCInterface ||
320+
cppClass.ClassKind == CppClassKind.ObjCProtocol)
321+
{
322+
cppClass.TemplateKind = CppTemplateKind.ObjCGenericClass;
323+
}
324+
else
325+
{
326+
cppClass.TemplateKind = CppTemplateKind.TemplateClass;
327+
}
328+
}
329+
}
330+
331+
return CXChildVisitResult.CXChildVisit_Continue;
332+
}, default);
333+
}
334+
323335
private TCppElement GetOrCreateDeclarationContainer<TCppElement>(CXCursor cursor, void* data, out CppContainerContext context) where TCppElement : CppElement, ICppContainer
324336
{
325337
context = GetOrCreateDeclarationContainer(cursor, data);
@@ -406,6 +418,7 @@ private CXChildVisitResult VisitMember(CXCursor cursor, CXCursor parent, void* d
406418
case CXCursorKind.CXCursor_UnionDecl:
407419
case CXCursorKind.CXCursor_ObjCInterfaceDecl:
408420
case CXCursorKind.CXCursor_ObjCProtocolDecl:
421+
case CXCursorKind.CXCursor_ObjCCategoryDecl:
409422
{
410423
bool isAnonymous = cursor.IsAnonymous;
411424
var cppClass = VisitClassDecl(cursor, data);
@@ -553,9 +566,6 @@ private CXChildVisitResult VisitMember(CXCursor cursor, CXCursor parent, void* d
553566
cursor.VisitChildren(VisitMember, new CXClientData((IntPtr)data));
554567
break;
555568

556-
case CXCursorKind.CXCursor_ObjCCategoryDecl:
557-
element = VisitObjCCategory(cursor, data);
558-
break;
559569
case CXCursorKind.CXCursor_ObjCPropertyDecl:
560570
{
561571
var containerContext = GetOrCreateDeclarationContainer(parent, data);
@@ -599,16 +609,6 @@ private CXChildVisitResult VisitMember(CXCursor cursor, CXCursor parent, void* d
599609
return CXChildVisitResult.CXChildVisit_Continue;
600610
}
601611

602-
private CppElement VisitObjCCategory(CXCursor cursor, void* data)
603-
{
604-
var categoryContainer = GetOrCreateDeclarationContainer(cursor, data).Container;
605-
var cppCategory = (CppObjCCategory)categoryContainer;
606-
_currentClassBeingVisited = cppCategory.TargetClass;
607-
cursor.VisitChildren(VisitMember, new CXClientData((IntPtr)data));
608-
_currentClassBeingVisited = null;
609-
return cppCategory;
610-
}
611-
612612
private CppComment GetComment(CXCursor cursor)
613613
{
614614
var cxComment = cursor.ParsedComment;

src/CppAst/CppObjCCategory.cs

Lines changed: 0 additions & 88 deletions
This file was deleted.

src/CppAst/CppParser.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Alexandre Mutel. All rights reserved.
1+
// Copyright (c) Alexandre Mutel. All rights reserved.
22
// Licensed under the BSD-Clause 2 license.
33
// See license.txt file in the project root for full license information.
44

@@ -94,9 +94,21 @@ private static unsafe CppCompilation ParseInternal(List<CppFileOrString> cppFile
9494
arguments.Add("-dM");
9595
arguments.Add("-E");
9696

97-
if (options.ParseAsCpp && !arguments.Contains("-xc++"))
97+
switch (options.ParserKind)
9898
{
99-
arguments.Add("-xc++");
99+
case CppParserKind.None:
100+
break;
101+
case CppParserKind.Cpp:
102+
arguments.Add("-xc++");
103+
break;
104+
case CppParserKind.C:
105+
arguments.Add("-xc");
106+
break;
107+
case CppParserKind.ObjC:
108+
arguments.Add("-xobjective-c");
109+
break;
110+
default:
111+
throw new ArgumentOutOfRangeException();
100112
}
101113

102114
if (!arguments.Any(x => x.StartsWith("--target=")))

0 commit comments

Comments
 (0)