1+ // Copyright (c) Alexandre Mutel. All rights reserved.
2+ // Licensed under the BSD-Clause 2 license.
3+ // See license.txt file in the project root for full license information.
4+
5+ using System ;
6+
7+ namespace CppAst . Tests ;
8+
9+ public class TestObjectiveC : InlineTestBase
10+ {
11+ [ Test ]
12+ public void TestFoundationIncludes ( )
13+ {
14+ if ( ! OperatingSystem . IsMacOS ( ) )
15+ {
16+ NUnit . Framework . Assert . Ignore ( "Only on MacOS" ) ;
17+ return ;
18+ }
19+
20+ ParseAssert ( """
21+ #import <Foundation/Foundation.h>
22+ """ ,
23+ compilation =>
24+ {
25+ Assert . False ( compilation . HasErrors ) ;
26+ Assert . AreEqual ( 0 , compilation . Diagnostics . Messages . Count , "Parsing foundation headers should not generate warnings" ) ; // No warnings
27+ Assert . IsTrue ( compilation . System . Classes . Count > 1000 ) ;
28+ } , new ( )
29+ {
30+ ParserKind = CppParserKind . ObjC ,
31+ TargetCpu = CppTargetCpu . ARM64 ,
32+ TargetVendor = "apple" ,
33+ TargetSystem = "darwin" ,
34+ ParseMacros = false ,
35+ ParseSystemIncludes = true ,
36+ AdditionalArguments =
37+ {
38+ "-std=gnu11" ,
39+ "-isysroot" , "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk" ,
40+ "-F" , "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks" ,
41+ "-isystem" , "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include" ,
42+ "-isystem" , "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/16/include" ,
43+ }
44+ }
45+ ) ;
46+ }
47+
48+ [ Test ]
49+ public void TestInterfaceWithMethods ( )
50+ {
51+ ParseAssert ( """
52+ @interface MyInterface
53+ - (float)helloworld;
54+ - (void)doSomething:(int)index argSpecial:(float)arg1;
55+ @end
56+ """ ,
57+ compilation =>
58+ {
59+ Assert . False ( compilation . HasErrors ) ;
60+ Assert . AreEqual ( 1 , compilation . Classes . Count ) ;
61+ var myInterface = compilation . Classes [ 0 ] ;
62+ Assert . AreEqual ( CppClassKind . ObjCInterface , myInterface . ClassKind ) ;
63+ Assert . AreEqual ( "MyInterface" , myInterface . Name ) ;
64+ Assert . AreEqual ( 2 , myInterface . Functions . Count ) ;
65+
66+ Assert . AreEqual ( 0 , myInterface . Functions [ 0 ] . Parameters . Count ) ;
67+ Assert . AreEqual ( "helloworld" , myInterface . Functions [ 0 ] . Name ) ;
68+ Assert . IsTrue ( myInterface . Functions [ 0 ] . ReturnType is CppPrimitiveType primitive && primitive . Kind == CppPrimitiveKind . Float ) ;
69+
70+ Assert . AreEqual ( 2 , myInterface . Functions [ 1 ] . Parameters . Count ) ;
71+ Assert . AreEqual ( "index" , myInterface . Functions [ 1 ] . Parameters [ 0 ] . Name ) ;
72+ Assert . AreEqual ( "arg1" , myInterface . Functions [ 1 ] . Parameters [ 1 ] . Name ) ;
73+ Assert . AreEqual ( "doSomething:argSpecial:" , myInterface . Functions [ 1 ] . Name ) ;
74+ Assert . IsTrue ( myInterface . Functions [ 1 ] . ReturnType is CppPrimitiveType primitive2 && primitive2 . Kind == CppPrimitiveKind . Void ) ;
75+ Assert . IsTrue ( myInterface . Functions [ 1 ] . Parameters [ 1 ] . Type is CppPrimitiveType primitive3 && primitive3 . Kind == CppPrimitiveKind . Float ) ;
76+
77+ } , GetDefaultObjCOptions ( )
78+ ) ;
79+
80+ }
81+
82+ [ Test ]
83+ public void TestInterfaceWithProperties ( )
84+ {
85+ ParseAssert ( """
86+ @interface MyInterface
87+ @property int id;
88+ @property (readonly) float id2;
89+ @end
90+ """ ,
91+ compilation =>
92+ {
93+ Assert . False ( compilation . HasErrors ) ;
94+ Assert . AreEqual ( 1 , compilation . Classes . Count ) ;
95+ var myInterface = compilation . Classes [ 0 ] ;
96+ Assert . AreEqual ( CppClassKind . ObjCInterface , myInterface . ClassKind ) ;
97+ Assert . AreEqual ( "MyInterface" , myInterface . Name ) ;
98+ Assert . AreEqual ( 2 , myInterface . Properties . Count ) ;
99+ Assert . AreEqual ( "id" , myInterface . Properties [ 0 ] . Name ) ;
100+ Assert . AreEqual ( "id2" , myInterface . Properties [ 1 ] . Name ) ;
101+
102+ Assert . IsTrue ( myInterface . Properties [ 0 ] . Type is CppPrimitiveType primitive && primitive . Kind == CppPrimitiveKind . Int ) ;
103+ Assert . IsTrue ( myInterface . Properties [ 0 ] . Getter is not null ) ;
104+ Assert . IsTrue ( myInterface . Properties [ 0 ] . Setter is not null ) ;
105+
106+ Assert . IsTrue ( myInterface . Properties [ 1 ] . Type is CppPrimitiveType primitive2 && primitive2 . Kind == CppPrimitiveKind . Float ) ;
107+ Assert . IsTrue ( myInterface . Properties [ 1 ] . Setter is null ) ;
108+
109+ Assert . AreEqual ( 3 , myInterface . Functions . Count ) ;
110+ Assert . AreEqual ( "id" , myInterface . Functions [ 0 ] . Name ) ;
111+ Assert . IsTrue ( myInterface . Functions [ 0 ] . ReturnType is CppPrimitiveType primitive3 && primitive3 . Kind == CppPrimitiveKind . Int ) ;
112+
113+ Assert . AreEqual ( "setId:" , myInterface . Functions [ 1 ] . Name ) ;
114+ Assert . IsTrue ( myInterface . Functions [ 1 ] . ReturnType is CppPrimitiveType primitive4 && primitive4 . Kind == CppPrimitiveKind . Void ) ;
115+ Assert . AreEqual ( 1 , myInterface . Functions [ 1 ] . Parameters . Count ) ;
116+ Assert . AreEqual ( "id" , myInterface . Functions [ 1 ] . Parameters [ 0 ] . Name ) ;
117+ Assert . IsTrue ( myInterface . Functions [ 1 ] . Parameters [ 0 ] . Type is CppPrimitiveType primitive5 && primitive5 . Kind == CppPrimitiveKind . Int ) ;
118+
119+ Assert . AreEqual ( "id2" , myInterface . Functions [ 2 ] . Name ) ;
120+ Assert . IsTrue ( myInterface . Functions [ 2 ] . ReturnType is CppPrimitiveType primitive6 && primitive6 . Kind == CppPrimitiveKind . Float ) ;
121+ Assert . AreEqual ( 0 , myInterface . Functions [ 2 ] . Parameters . Count ) ;
122+ } , GetDefaultObjCOptions ( )
123+ ) ;
124+ }
125+
126+ [ Test ]
127+ public void TestInterfaceWithInstanceType ( )
128+ {
129+ ParseAssert ( """
130+ @interface MyInterface
131+ + (instancetype)getInstance;
132+ @end
133+ """ ,
134+ compilation =>
135+ {
136+ Assert . False ( compilation . HasErrors ) ;
137+ Assert . AreEqual ( 1 , compilation . Classes . Count ) ;
138+ var myInterface = compilation . Classes [ 0 ] ;
139+ Assert . AreEqual ( CppClassKind . ObjCInterface , myInterface . ClassKind ) ;
140+ Assert . AreEqual ( "MyInterface" , myInterface . Name ) ;
141+ Assert . AreEqual ( 1 , myInterface . Functions . Count ) ;
142+ Assert . AreEqual ( "getInstance" , myInterface . Functions [ 0 ] . Name ) ;
143+ Assert . IsTrue ( ( myInterface . Functions [ 0 ] . Flags & CppFunctionFlags . ClassMethod ) != 0 ) ;
144+ var pointerType = myInterface . Functions [ 0 ] . ReturnType as CppPointerType ;
145+ Assert . IsNotNull ( pointerType ) ;
146+ Assert . AreEqual ( myInterface , pointerType ! . ElementType ) ;
147+ } , GetDefaultObjCOptions ( )
148+ ) ;
149+ }
150+
151+
152+ [ Test ]
153+ public void TestInterfaceWithMultipleGenericParameters ( )
154+ {
155+ ParseAssert ( """
156+ @interface BaseInterface
157+ @end
158+
159+ // Generics require a base class
160+ @interface MyInterface<T1, T2> : BaseInterface
161+ - (T1)get_at:(int)index;
162+ - (T2)get_at2:(int)index;
163+ @end
164+ """ ,
165+ compilation =>
166+ {
167+ Assert . False ( compilation . HasErrors ) ;
168+ Assert . AreEqual ( 2 , compilation . Classes . Count ) ;
169+ var myInterface = compilation . Classes [ 1 ] ;
170+ Assert . AreEqual ( CppClassKind . ObjCInterface , myInterface . ClassKind ) ;
171+ Assert . AreEqual ( "MyInterface" , myInterface . Name ) ;
172+ Assert . AreEqual ( 2 , myInterface . TemplateParameters . Count ) ;
173+ Assert . IsTrue ( myInterface . TemplateParameters [ 0 ] is CppTemplateParameterType templateParam1 && templateParam1 . Name == "T1" ) ;
174+ Assert . IsTrue ( myInterface . TemplateParameters [ 1 ] is CppTemplateParameterType templateParam2 && templateParam2 . Name == "T2" ) ;
175+
176+ Assert . AreEqual ( 2 , myInterface . Functions . Count ) ;
177+ Assert . AreEqual ( "get_at:" , myInterface . Functions [ 0 ] . Name ) ;
178+ Assert . AreEqual ( "get_at2:" , myInterface . Functions [ 1 ] . Name ) ;
179+ Assert . IsTrue ( myInterface . Functions [ 0 ] . ReturnType is CppTemplateParameterType templateSpecialization && templateSpecialization . Name == "T1" ) ;
180+ Assert . IsTrue ( myInterface . Functions [ 1 ] . ReturnType is CppTemplateParameterType templateSpecialization2 && templateSpecialization2 . Name == "T2" ) ;
181+ } , GetDefaultObjCOptions ( )
182+ ) ;
183+ }
184+
185+ [ Test ]
186+ public void TestBlockFunctionPointer ( )
187+ {
188+ ParseAssert ( """
189+ typedef float (^MyBlock)(int a, int* b);
190+ """ ,
191+ compilation =>
192+ {
193+ Assert . False ( compilation . HasErrors ) ;
194+ Assert . AreEqual ( 1 , compilation . Typedefs . Count ) ;
195+
196+ var typedef = compilation . Typedefs [ 0 ] ;
197+ Assert . AreEqual ( "MyBlock" , typedef . Name ) ;
198+
199+ Assert . IsInstanceOf < CppBlockFunctionType > ( typedef . ElementType ) ;
200+ var blockType = ( CppBlockFunctionType ) typedef . ElementType ;
201+
202+ Assert . AreEqual ( CppTypeKind . ObjCBlockFunction , blockType . TypeKind ) ;
203+
204+ Assert . IsTrue ( blockType . ReturnType is CppPrimitiveType primitive && primitive . Kind == CppPrimitiveKind . Float ) ;
205+
206+ Assert . AreEqual ( 2 , blockType . Parameters . Count ) ;
207+ Assert . IsTrue ( blockType . Parameters [ 0 ] . Type is CppPrimitiveType primitive2 && primitive2 . Kind == CppPrimitiveKind . Int ) ;
208+ Assert . IsTrue ( blockType . Parameters [ 1 ] . Type is CppPointerType pointerType && pointerType . ElementType is CppPrimitiveType primitive3 && primitive3 . Kind == CppPrimitiveKind . Int ) ;
209+ } , GetDefaultObjCOptions ( )
210+ ) ;
211+ }
212+
213+ [ Test ]
214+ public void TestProtocol ( )
215+ {
216+ ParseAssert ( """
217+ @protocol MyProtocol
218+ @end
219+
220+ @protocol MyProtocol1
221+ @end
222+
223+ @protocol MyProtocol2 <MyProtocol, MyProtocol1>
224+ @end
225+
226+ @interface MyInterface <MyProtocol>
227+ @end
228+ """ ,
229+ compilation =>
230+ {
231+ Assert . False ( compilation . HasErrors ) ;
232+
233+ Assert . AreEqual ( 4 , compilation . Classes . Count ) ;
234+
235+ var myProtocol = compilation . Classes [ 0 ] ;
236+ Assert . AreEqual ( CppClassKind . ObjCProtocol , myProtocol . ClassKind ) ;
237+ Assert . AreEqual ( "MyProtocol" , myProtocol . Name ) ;
238+
239+ var myProtocol1 = compilation . Classes [ 1 ] ;
240+ Assert . AreEqual ( CppClassKind . ObjCProtocol , myProtocol1 . ClassKind ) ;
241+ Assert . AreEqual ( "MyProtocol1" , myProtocol1 . Name ) ;
242+
243+ var myProtocol2 = compilation . Classes [ 2 ] ;
244+ Assert . AreEqual ( CppClassKind . ObjCProtocol , myProtocol2 . ClassKind ) ;
245+ Assert . AreEqual ( "MyProtocol2" , myProtocol2 . Name ) ;
246+ Assert . AreEqual ( 2 , myProtocol2 . ObjCImplementedProtocols . Count ) ;
247+ Assert . AreEqual ( myProtocol , myProtocol2 . ObjCImplementedProtocols [ 0 ] ) ;
248+ Assert . AreEqual ( myProtocol1 , myProtocol2 . ObjCImplementedProtocols [ 1 ] ) ;
249+
250+ var myInterface = compilation . Classes [ 3 ] ;
251+ Assert . AreEqual ( CppClassKind . ObjCInterface , myInterface . ClassKind ) ;
252+ Assert . AreEqual ( "MyInterface" , myInterface . Name ) ;
253+ Assert . AreEqual ( 1 , myInterface . ObjCImplementedProtocols . Count ) ;
254+ Assert . AreEqual ( myProtocol , myInterface . ObjCImplementedProtocols [ 0 ] ) ;
255+
256+ } , GetDefaultObjCOptions ( )
257+ ) ;
258+ }
259+
260+ [ Test ]
261+ public void TestInterfaceBaseType ( )
262+ {
263+ ParseAssert ( """
264+ @interface InterfaceBase
265+ @end
266+
267+ @interface MyInterface : InterfaceBase
268+ @end
269+ """ ,
270+ compilation =>
271+ {
272+ Assert . False ( compilation . HasErrors ) ;
273+
274+ Assert . AreEqual ( 2 , compilation . Classes . Count ) ;
275+
276+ var myInterfaceBase = compilation . Classes [ 0 ] ;
277+ Assert . AreEqual ( CppClassKind . ObjCInterface , myInterfaceBase . ClassKind ) ;
278+ Assert . AreEqual ( "InterfaceBase" , myInterfaceBase . Name ) ;
279+
280+ var myInterface = compilation . Classes [ 1 ] ;
281+ Assert . AreEqual ( CppClassKind . ObjCInterface , myInterface . ClassKind ) ;
282+ Assert . AreEqual ( "MyInterface" , myInterface . Name ) ;
283+ Assert . AreEqual ( 1 , myInterface . BaseTypes . Count ) ;
284+ Assert . AreEqual ( myInterfaceBase , myInterface . BaseTypes [ 0 ] . Type ) ;
285+ } , GetDefaultObjCOptions ( )
286+ ) ;
287+ }
288+
289+ [ Test ]
290+ public void TestInterfaceWithCategory ( )
291+ {
292+ ParseAssert ( """
293+ @interface MyInterface
294+ @end
295+
296+ @interface MyInterface (MyCategory)
297+ @end
298+ """ ,
299+ compilation =>
300+ {
301+ Assert . False ( compilation . HasErrors ) ;
302+
303+ Assert . AreEqual ( 2 , compilation . Classes . Count ) ;
304+
305+ var myInterface = compilation . Classes [ 0 ] ;
306+ Assert . AreEqual ( CppClassKind . ObjCInterface , myInterface . ClassKind ) ;
307+ Assert . AreEqual ( "MyInterface" , myInterface . Name ) ;
308+
309+ var myInterfaceWithCategory = compilation . Classes [ 1 ] ;
310+ Assert . AreEqual ( CppClassKind . ObjCInterfaceCategory , myInterfaceWithCategory . ClassKind ) ;
311+ Assert . AreEqual ( "MyInterface" , myInterfaceWithCategory . Name ) ;
312+ Assert . AreEqual ( "MyCategory" , myInterfaceWithCategory . ObjCCategoryName ) ;
313+ Assert . AreEqual ( myInterface , myInterfaceWithCategory . ObjCCategoryTargetClass ) ;
314+ } , GetDefaultObjCOptions ( )
315+ ) ;
316+ }
317+
318+
319+ private static CppParserOptions GetDefaultObjCOptions ( )
320+ {
321+ return new CppParserOptions
322+ {
323+ ParserKind = CppParserKind . ObjC ,
324+ TargetCpu = CppTargetCpu . ARM64 ,
325+ TargetVendor = "apple" ,
326+ TargetSystem = "darwin" ,
327+ ParseMacros = false ,
328+ ParseSystemIncludes = false ,
329+ } ;
330+ }
331+ }
0 commit comments