Skip to content

Commit d0ca6a6

Browse files
Fixing some low hanging bugs. (#256)
* Ensure ObjCProtocol creates an ObjCProtocolDecl * Ensure the number of indirections for function pointer typedefs is correct * Handle type prefixes in C * Add basic handling for VarDecl and OffsetOfExpr * Allow specifying just `-x c` on the command line * Fix the C test on Unix
1 parent 6865a2f commit d0ca6a6

17 files changed

Lines changed: 499 additions & 28 deletions

File tree

sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,12 @@ private void VisitDecl(Decl decl)
124124
// case CX_DeclKind.CX_DeclKind_ClassTemplatePartialSpecialization:
125125
// case CX_DeclKind.CX_DeclKind_TemplateTypeParm:
126126
// case CX_DeclKind.CX_DeclKind_ObjCTypeParam:
127-
// case CX_DeclKind.CX_DeclKind_TypeAlias:
127+
128+
case CX_DeclKind.CX_DeclKind_TypeAlias:
129+
{
130+
VisitTypeAliasDecl((TypeAliasDecl)decl);
131+
break;
132+
}
128133

129134
case CX_DeclKind.CX_DeclKind_Typedef:
130135
{
@@ -2411,6 +2416,11 @@ private void VisitTranslationUnitDecl(TranslationUnitDecl translationUnitDecl)
24112416
Visit(translationUnitDecl.CursorChildren, translationUnitDecl.Decls);
24122417
}
24132418

2419+
private void VisitTypeAliasDecl(TypeAliasDecl typeAliasDecl)
2420+
{
2421+
// Nothing to generate for type alias declarations
2422+
}
2423+
24142424
private void VisitTypedefDecl(TypedefDecl typedefDecl)
24152425
{
24162426
ForUnderlyingType(typedefDecl, typedefDecl.UnderlyingType);
@@ -3083,7 +3093,12 @@ private bool IsConstant(Expr initExpr)
30833093
// case CX_StmtClass.CX_StmtClass_ObjCSelectorExpr:
30843094
// case CX_StmtClass.CX_StmtClass_ObjCStringLiteral:
30853095
// case CX_StmtClass.CX_StmtClass_ObjCSubscriptRefExpr:
3086-
// case CX_StmtClass.CX_StmtClass_OffsetOfExpr:
3096+
3097+
case CX_StmtClass.CX_StmtClass_OffsetOfExpr:
3098+
{
3099+
return false;
3100+
}
3101+
30873102
// case CX_StmtClass.CX_StmtClass_OpaqueValueExpr:
30883103
// case CX_StmtClass.CX_StmtClass_UnresolvedLookupExpr:
30893104
// case CX_StmtClass.CX_StmtClass_UnresolvedMemberExpr:

sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitStmt.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ private void VisitCallExpr(CallExpr callExpr)
109109
Visit(callExpr.Callee);
110110
VisitArgs(callExpr);
111111
}
112+
else if (calleeDecl is VarDecl)
113+
{
114+
Visit(callExpr.Callee);
115+
VisitArgs(callExpr);
116+
}
112117
else
113118
{
114119
AddDiagnostic(DiagnosticLevel.Error, $"Unsupported callee declaration: '{calleeDecl?.DeclKindName}'. Generated bindings may be incomplete.", calleeDecl);
@@ -1642,7 +1647,13 @@ private void VisitStmt(Stmt stmt)
16421647
// case CX_StmtClass.CX_StmtClass_ObjCSelectorExpr:
16431648
// case CX_StmtClass.CX_StmtClass_ObjCStringLiteral:
16441649
// case CX_StmtClass.CX_StmtClass_ObjCSubscriptRefExpr:
1645-
// case CX_StmtClass.CX_StmtClass_OffsetOfExpr:
1650+
1651+
case CX_StmtClass.CX_StmtClass_OffsetOfExpr:
1652+
{
1653+
VisitOffsetOfExpr((OffsetOfExpr)stmt);
1654+
break;
1655+
}
1656+
16461657
// case CX_StmtClass.CX_StmtClass_OpaqueValueExpr:
16471658

16481659
case CX_StmtClass.CX_StmtClass_UnresolvedLookupExpr:
@@ -2055,6 +2066,20 @@ private void VisitUnaryOperator(UnaryOperator unaryOperator)
20552066
StopCSharpCode();
20562067
}
20572068

2069+
private void VisitOffsetOfExpr(OffsetOfExpr offsetOfExpr)
2070+
{
2071+
var outputBuilder = StartCSharpCode();
2072+
2073+
outputBuilder.AddUsingDirective("System.Runtime.InteropServices");
2074+
outputBuilder.Write("Marshal.OffsetOf<");
2075+
outputBuilder.Write(GetRemappedTypeName(offsetOfExpr, context: null, offsetOfExpr.TypeSourceInfoType, out var _));
2076+
outputBuilder.Write(">(\"");
2077+
Visit(offsetOfExpr.Referenced);
2078+
outputBuilder.Write("\")");
2079+
2080+
StopCSharpCode();
2081+
}
2082+
20582083
private void VisitUnresolvedLookupExpr(UnresolvedLookupExpr unresolvedLookupExpr)
20592084
{
20602085
var outputBuilder = StartCSharpCode();

sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,7 +1503,16 @@ private string GetTypeName(Cursor cursor, Cursor context, Type type, out string
15031503
}
15041504
else
15051505
{
1506-
// The default name should be correct
1506+
// The default name should be correct for C++, but C may have a prefix we need to strip
1507+
1508+
if (name.StartsWith("enum "))
1509+
{
1510+
name = name.Substring(5);
1511+
}
1512+
else if (name.StartsWith("struct "))
1513+
{
1514+
name = name.Substring(7);
1515+
}
15071516
}
15081517

15091518
if (name.Contains("::"))
@@ -1629,6 +1638,10 @@ private string GetTypeNameForPointeeType(Cursor cursor, Cursor context, Type poi
16291638
{
16301639
name = GetTypeNameForPointeeType(cursor, context, attributedType.ModifiedType, out var nativeModifiedTypeName);
16311640
}
1641+
else if (pointeeType is ElaboratedType elaboratedType)
1642+
{
1643+
name = GetTypeNameForPointeeType(cursor, context, elaboratedType.NamedType, out var nativeNamedTypeName);
1644+
}
16321645
else if (pointeeType is FunctionType functionType)
16331646
{
16341647
if (!_config.ExcludeFnptrCodegen && (functionType is FunctionProtoType functionProtoType))
@@ -1728,6 +1741,24 @@ private string GetTypeNameForPointeeType(Cursor cursor, Cursor context, Type poi
17281741
name = "IntPtr";
17291742
}
17301743
}
1744+
else if (pointeeType is TypedefType typedefType)
1745+
{
1746+
// We check remapped names here so that types that have variable sizes
1747+
// can be treated correctly. Otherwise, they will resolve to a particular
1748+
// platform size, based on whatever parameters were passed into clang.
1749+
1750+
var remappedName = GetRemappedName(name, cursor, tryRemapOperatorName: false, out var wasRemapped);
1751+
1752+
if (wasRemapped)
1753+
{
1754+
name = remappedName;
1755+
name += '*';
1756+
}
1757+
else
1758+
{
1759+
name = GetTypeNameForPointeeType(cursor, context, typedefType.Decl.UnderlyingType, out var nativeUnderlyingTypeName);
1760+
}
1761+
}
17311762
else
17321763
{
17331764
// Otherwise fields that point at anonymous structs get the wrong name
@@ -2983,7 +3014,12 @@ private bool IsUnchecked(string targetTypeName, Stmt stmt)
29833014
// case CX_StmtClass.CX_StmtClass_ObjCSelectorExpr:
29843015
// case CX_StmtClass.CX_StmtClass_ObjCStringLiteral:
29853016
// case CX_StmtClass.CX_StmtClass_ObjCSubscriptRefExpr:
2986-
// case CX_StmtClass.CX_StmtClass_OffsetOfExpr:
3017+
3018+
case CX_StmtClass.CX_StmtClass_OffsetOfExpr:
3019+
{
3020+
return false;
3021+
}
3022+
29873023
// case CX_StmtClass.CX_StmtClass_OpaqueValueExpr:
29883024
// case CX_StmtClass.CX_StmtClass_UnresolvedLookupExpr:
29893025
// case CX_StmtClass.CX_StmtClass_UnresolvedMemberExpr:

sources/ClangSharp/Cursors/Decls/Decl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ public bool IsStdNamespace
167167
CX_DeclKind.CX_DeclKind_ObjCCategoryImpl => new ObjCCategoryImplDecl(handle),
168168
CX_DeclKind.CX_DeclKind_ObjCImplementation => new ObjCImplementationDecl(handle),
169169
CX_DeclKind.CX_DeclKind_ObjCInterface => new ObjCInterfaceDecl(handle),
170-
CX_DeclKind.CX_DeclKind_ObjCProtocol => new ObjCPropertyDecl(handle),
170+
CX_DeclKind.CX_DeclKind_ObjCProtocol => new ObjCProtocolDecl(handle),
171171
CX_DeclKind.CX_DeclKind_ObjCMethod => new ObjCMethodDecl(handle),
172172
CX_DeclKind.CX_DeclKind_ObjCProperty => new ObjCPropertyDecl(handle),
173173
CX_DeclKind.CX_DeclKind_BuiltinTemplate => new BuiltinTemplateDecl(handle),

sources/ClangSharp/Cursors/Exprs/OffsetOfExpr.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,22 @@ namespace ClangSharp
1010
public sealed class OffsetOfExpr : Expr
1111
{
1212
private readonly Lazy<IReadOnlyList<Expr>> _indexExprs;
13+
private readonly Lazy<Cursor> _referenced;
1314
private readonly Lazy<Type> _typeSourceInfoType;
1415

1516
internal OffsetOfExpr(CXCursor handle) : base(handle, CXCursorKind.CXCursor_UnexposedExpr, CX_StmtClass.CX_StmtClass_OffsetOfExpr)
1617
{
1718
_indexExprs = new Lazy<IReadOnlyList<Expr>>(() => Children.Cast<Expr>().ToList());
19+
_referenced = new Lazy<Cursor>(() => TranslationUnit.GetOrCreate<Cursor>(Handle.Referenced));
1820
_typeSourceInfoType = new Lazy<Type>(() => TranslationUnit.GetOrCreate<Type>(Handle.TypeOperand));
1921
}
2022

2123
public IReadOnlyList<Expr> IndexExprs => _indexExprs.Value;
2224

2325
public uint NumExpressions => NumChildren;
2426

27+
public Cursor Referenced => _referenced.Value;
28+
2529
public Type TypeSourceInfoType => _typeSourceInfoType.Value;
2630
}
2731
}

sources/ClangSharpPInvokeGenerator/Program.cs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -440,12 +440,23 @@ public static int Run(InvocationContext context)
440440
return -1;
441441
}
442442

443-
var clangCommandLineArgs = new string[]
443+
string[] clangCommandLineArgs;
444+
445+
if (string.IsNullOrWhiteSpace(std))
444446
{
445-
$"--language={language}", // Treat subsequent input files as having type <language>
446-
$"--std={std}", // Language standard to compile for
447-
"-Wno-pragma-once-outside-header" // We are processing files which may be header files
448-
};
447+
clangCommandLineArgs = new string[] {
448+
$"--language={language}", // Treat subsequent input files as having type <language>
449+
"-Wno-pragma-once-outside-header" // We are processing files which may be header files
450+
};
451+
}
452+
else
453+
{
454+
clangCommandLineArgs = new string[] {
455+
$"--language={language}", // Treat subsequent input files as having type <language>
456+
$"--std={std}", // Language standard to compile for
457+
"-Wno-pragma-once-outside-header" // We are processing files which may be header files
458+
};
459+
}
449460

450461
clangCommandLineArgs = clangCommandLineArgs.Concat(includeDirectories.Select(x => "--include-directory=" + x)).ToArray();
451462
clangCommandLineArgs = clangCommandLineArgs.Concat(defineMacros.Select(x => "--define-macro=" + x)).ToArray();
@@ -806,7 +817,7 @@ private static void AddStdOption(RootCommand rootCommand)
806817
aliases: new string[] { "--std", "-std" },
807818
description: "Language standard to compile for.",
808819
argumentType: typeof(string),
809-
getDefaultValue: () => "c++17",
820+
getDefaultValue: () => "",
810821
arity: ArgumentArity.ExactlyOne
811822
);
812823

tests/ClangSharp.PInvokeGenerator.UnitTests/Base/FunctionPointerDeclarationTest.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,8 @@ public abstract class FunctionPointerDeclarationTest : PInvokeGeneratorTest
99
{
1010
[Fact]
1111
public abstract Task BasicTest();
12+
13+
[Fact]
14+
public abstract Task PointerlessTypedefTest();
1215
}
1316
}

tests/ClangSharp.PInvokeGenerator.UnitTests/CSharpCompatibleUnix/FunctionPointerDeclarationTest.cs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,54 @@ public sealed class CSharpCompatibleUnix_FunctionPointerDeclarationTest : Functi
88
{
99
public override Task BasicTest()
1010
{
11-
var inputContents = @"typedef void (*Callback)();";
11+
var inputContents = @"typedef void (*Callback)();
1212
13-
var expectedOutputContents = @"using System.Runtime.InteropServices;
13+
struct MyStruct {
14+
Callback _callback;
15+
};
16+
";
17+
18+
var expectedOutputContents = @"using System;
19+
using System.Runtime.InteropServices;
1420
1521
namespace ClangSharp.Test
1622
{
1723
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
1824
public delegate void Callback();
25+
26+
public partial struct MyStruct
27+
{
28+
[NativeTypeName(""Callback"")]
29+
public IntPtr _callback;
30+
}
31+
}
32+
";
33+
34+
return ValidateGeneratedCSharpCompatibleUnixBindingsAsync(inputContents, expectedOutputContents);
35+
}
36+
37+
public override Task PointerlessTypedefTest()
38+
{
39+
var inputContents = @"typedef void (Callback)();
40+
41+
struct MyStruct {
42+
Callback* _callback;
43+
};
44+
";
45+
46+
var expectedOutputContents = @"using System;
47+
using System.Runtime.InteropServices;
48+
49+
namespace ClangSharp.Test
50+
{
51+
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
52+
public delegate void Callback();
53+
54+
public partial struct MyStruct
55+
{
56+
[NativeTypeName(""Callback *"")]
57+
public IntPtr _callback;
58+
}
1959
}
2060
";
2161

tests/ClangSharp.PInvokeGenerator.UnitTests/CSharpCompatibleWindows/FunctionPointerDeclarationTest.cs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,54 @@ public sealed class CSharpCompatibleWindows_FunctionPointerDeclarationTest : Fun
88
{
99
public override Task BasicTest()
1010
{
11-
var inputContents = @"typedef void (*Callback)();";
11+
var inputContents = @"typedef void (*Callback)();
1212
13-
var expectedOutputContents = @"using System.Runtime.InteropServices;
13+
struct MyStruct {
14+
Callback _callback;
15+
};
16+
";
17+
18+
var expectedOutputContents = @"using System;
19+
using System.Runtime.InteropServices;
1420
1521
namespace ClangSharp.Test
1622
{
1723
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
1824
public delegate void Callback();
25+
26+
public partial struct MyStruct
27+
{
28+
[NativeTypeName(""Callback"")]
29+
public IntPtr _callback;
30+
}
31+
}
32+
";
33+
34+
return ValidateGeneratedCSharpCompatibleWindowsBindingsAsync(inputContents, expectedOutputContents);
35+
}
36+
37+
public override Task PointerlessTypedefTest()
38+
{
39+
var inputContents = @"typedef void (Callback)();
40+
41+
struct MyStruct {
42+
Callback* _callback;
43+
};
44+
";
45+
46+
var expectedOutputContents = @"using System;
47+
using System.Runtime.InteropServices;
48+
49+
namespace ClangSharp.Test
50+
{
51+
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
52+
public delegate void Callback();
53+
54+
public partial struct MyStruct
55+
{
56+
[NativeTypeName(""Callback *"")]
57+
public IntPtr _callback;
58+
}
1959
}
2060
";
2161

tests/ClangSharp.PInvokeGenerator.UnitTests/CSharpLatestUnix/FunctionPointerDeclarationTest.cs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,44 @@ public sealed class CSharpLatestUnix_FunctionPointerDeclarationTest : FunctionPo
88
{
99
public override Task BasicTest()
1010
{
11-
var inputContents = @"typedef void (*Callback)();";
11+
var inputContents = @"typedef void (*Callback)();
1212
13-
var expectedOutputContents = "";
13+
struct MyStruct {
14+
Callback _callback;
15+
};
16+
";
17+
18+
var expectedOutputContents = @"namespace ClangSharp.Test
19+
{
20+
public unsafe partial struct MyStruct
21+
{
22+
[NativeTypeName(""Callback"")]
23+
public delegate* unmanaged[Cdecl]<void> _callback;
24+
}
25+
}
26+
";
27+
28+
return ValidateGeneratedCSharpLatestUnixBindingsAsync(inputContents, expectedOutputContents);
29+
}
30+
31+
public override Task PointerlessTypedefTest()
32+
{
33+
var inputContents = @"typedef void (Callback)();
34+
35+
struct MyStruct {
36+
Callback* _callback;
37+
};
38+
";
39+
40+
var expectedOutputContents = @"namespace ClangSharp.Test
41+
{
42+
public unsafe partial struct MyStruct
43+
{
44+
[NativeTypeName(""Callback *"")]
45+
public delegate* unmanaged[Cdecl]<void> _callback;
46+
}
47+
}
48+
";
1449

1550
return ValidateGeneratedCSharpLatestUnixBindingsAsync(inputContents, expectedOutputContents);
1651
}

0 commit comments

Comments
 (0)