Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions sources/ClangSharp.Interop/Extensions/CXCursor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1785,6 +1785,8 @@ public readonly CXString UnaryOperatorKindSpelling

public readonly CXCursor UsedContext => clangsharp.Cursor_getUsedContext(this);

public readonly CXCursor UsingEnumDeclEnumDecl => clangsharp.Cursor_getUsingEnumDeclEnumDecl(this);

public readonly CXString Usr => clang.getCursorUSR(this);

public readonly CXVisibilityKind Visibility => clang.getCursorVisibility(this);
Expand Down
3 changes: 3 additions & 0 deletions sources/ClangSharp.Interop/clangsharp/clangsharp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,9 @@ public static unsafe partial class @clangsharp
[DllImport("libClangSharp", CallingConvention = CallingConvention.Cdecl, EntryPoint = "clangsharp_Cursor_getUsedContext", ExactSpelling = true)]
public static extern CXCursor Cursor_getUsedContext(CXCursor C);

[DllImport("libClangSharp", CallingConvention = CallingConvention.Cdecl, EntryPoint = "clangsharp_Cursor_getUsingEnumDeclEnumDecl", ExactSpelling = true)]
public static extern CXCursor Cursor_getUsingEnumDeclEnumDecl(CXCursor C);

[DllImport("libClangSharp", CallingConvention = CallingConvention.Cdecl, EntryPoint = "clangsharp_Cursor_getTypeParamHasExplicitBound", ExactSpelling = true)]
[return: NativeTypeName("unsigned int")]
public static extern uint Cursor_getTypeParamHasExplicitBound(CXCursor C);
Expand Down
6 changes: 4 additions & 2 deletions sources/ClangSharp/Cursors/Decls/UsingEnumDecl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ public sealed class UsingEnumDecl : BaseUsingDecl, IMergeable<UsingEnumDecl>
{
private ValueLazy<UsingEnumDecl, EnumDecl> _enumDecl;

internal unsafe UsingEnumDecl(CXCursor handle) : base(handle, CXCursor_UnexposedDecl, CX_DeclKind_UsingEnum)
// libClang surfaces a `using enum` declaration with `cursor.kind == CXCursor_EnumDecl`, even
// though its `DeclKind` is `CX_DeclKind_UsingEnum`, so that is the expected cursor kind here.
internal unsafe UsingEnumDecl(CXCursor handle) : base(handle, CXCursor_EnumDecl, CX_DeclKind_UsingEnum)
{
_enumDecl = new ValueLazy<UsingEnumDecl, EnumDecl>(&EnumDeclFactory);
}
Expand All @@ -19,5 +21,5 @@ internal unsafe UsingEnumDecl(CXCursor handle) : base(handle, CXCursor_Unexposed

public EnumDecl EnumDecl => _enumDecl.GetValue(this);

private static unsafe EnumDecl EnumDeclFactory(UsingEnumDecl self) => self.TranslationUnit.GetOrCreate<EnumDecl>(self.Handle.Definition);
private static unsafe EnumDecl EnumDeclFactory(UsingEnumDecl self) => self.TranslationUnit.GetOrCreate<EnumDecl>(self.Handle.UsingEnumDeclEnumDecl);
}
12 changes: 12 additions & 0 deletions sources/libClangSharp/ClangSharp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5049,6 +5049,18 @@ CXCursor clangsharp_Cursor_getUsedContext(CXCursor C) {
return clang_getNullCursor();
}

CXCursor clangsharp_Cursor_getUsingEnumDeclEnumDecl(CXCursor C) {
if (isDeclOrTU(C.kind)) {
const Decl* D = getCursorDecl(C);

if (const UsingEnumDecl* UED = dyn_cast<UsingEnumDecl>(D)) {
return MakeCXCursor(UED->getEnumDecl(), getCursorTU(C));
}
}

return clang_getNullCursor();
}

CLANGSHARP_LINKAGE unsigned clangsharp_Cursor_getTypeParamHasExplicitBound(CXCursor C)
{
if (isDeclOrTU(C.kind)) {
Expand Down
2 changes: 2 additions & 0 deletions sources/libClangSharp/ClangSharp.h
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,8 @@ CLANGSHARP_LINKAGE CXCursor clangsharp_Cursor_getUninstantiatedDefaultArg(CXCurs

CLANGSHARP_LINKAGE CXCursor clangsharp_Cursor_getUsedContext(CXCursor C);

CLANGSHARP_LINKAGE CXCursor clangsharp_Cursor_getUsingEnumDeclEnumDecl(CXCursor C);

CLANGSHARP_LINKAGE unsigned clangsharp_Cursor_getTypeParamHasExplicitBound(CXCursor C);

CLANGSHARP_LINKAGE unsigned clangsharp_Cursor_getTypeParamVariance(CXCursor C);
Expand Down
56 changes: 53 additions & 3 deletions tests/ClangSharp.UnitTests/CursorTests/DeclTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,55 @@ public void VarTemplateSpecializationArgsTest()
Assert.That(varTemplateSpecializationDecl.TemplateArgs[1].AsType.AsString, Is.EqualTo("float"));
}

[Test]
public void UsingEnumDeclTest()
{
// `using enum` requires C++20 and previously threw because UsingEnumDecl passed the wrong
// expectedCursorKind to the base Cursor ctor (libClang surfaces it as CXCursor_EnumDecl).
var inputContents = """
enum class E { A, B };

struct S {
using enum E;
};
""";

string[] commandLineArgs = ["-std=c++20", "-Wno-pragma-once-outside-header"];
using var translationUnit = CreateTranslationUnit(inputContents, commandLineArgs: commandLineArgs);

var structDecl = translationUnit.TranslationUnitDecl.Decls.OfType<CXXRecordDecl>().Single((recordDecl) => recordDecl.Name.Equals("S", StringComparison.Ordinal));
var usingEnumDecl = structDecl.Decls.OfType<UsingEnumDecl>().Single();

Assert.That(usingEnumDecl.Handle.kind, Is.EqualTo(CXCursorKind.CXCursor_EnumDecl));
Assert.That(usingEnumDecl.Handle.DeclKind, Is.EqualTo(CX_DeclKind.CX_DeclKind_UsingEnum));
}

[Test]
public void UsingEnumDeclEnumDeclTest()
{
// Resolving UsingEnumDecl.EnumDecl relies on the native clangsharp_Cursor_getUsingEnumDeclEnumDecl
// shim, which the pinned 21.1 prebuilt package predates; skip until the native lib is rebuilt.
SkipUntilNativeRebuild();

var inputContents = """
enum class E { A, B };

struct S {
using enum E;
};
""";

string[] commandLineArgs = ["-std=c++20", "-Wno-pragma-once-outside-header"];
using var translationUnit = CreateTranslationUnit(inputContents, commandLineArgs: commandLineArgs);

var structDecl = translationUnit.TranslationUnitDecl.Decls.OfType<CXXRecordDecl>().Single((recordDecl) => recordDecl.Name.Equals("S", StringComparison.Ordinal));
var usingEnumDecl = structDecl.Decls.OfType<UsingEnumDecl>().Single();

var enumDecl = usingEnumDecl.EnumDecl;
Assert.That(enumDecl, Is.Not.Null);
Assert.That(enumDecl.Name, Is.EqualTo("E"));
}

[Test]
public void IsPodTest()
{
Expand Down Expand Up @@ -268,9 +317,10 @@ enum E {
});
}

// The fix for these lives in the native libClangSharp shim (clangsharp_Cursor_getNumTemplateArguments
// and clangsharp_Cursor_getTemplateArgument). The pinned 21.1 prebuilt native package predates it, so
// skip until the native lib is rebuilt for a newer libClang. Rebuilding off 21.1 auto-unskips these.
// Some tests depend on native libClangSharp shims that the pinned 21.1 prebuilt package predates
// (e.g. clangsharp_Cursor_getNumTemplateArguments / getTemplateArgument and
// clangsharp_Cursor_getUsingEnumDeclEnumDecl). Skip those until the native lib is rebuilt for a
// newer libClang. Rebuilding off 21.1 auto-unskips them.
private static void SkipUntilNativeRebuild()
{
using var versionString = clang.getClangVersion();
Expand Down
4 changes: 2 additions & 2 deletions tests/ClangSharp.UnitTests/TranslationUnitTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ public abstract class TranslationUnitTest
"-Wno-pragma-once-outside-header" // We are processing files which may be header files
];

protected static TranslationUnit CreateTranslationUnit(string inputContents, string language = "c++" /* input files are C++ by default */)
protected static TranslationUnit CreateTranslationUnit(string inputContents, string language = "c++" /* input files are C++ by default */, string[]? commandLineArgs = null)
{
Assert.That(DefaultInputFileName, Does.Exist);

using var unsavedFile = CXUnsavedFile.Create(DefaultInputFileName, inputContents);
var unsavedFiles = new CXUnsavedFile[] { unsavedFile };

var index = CXIndex.Create();
var arguments = new List<string>(DefaultClangCommandLineArgs)
var arguments = new List<string>(commandLineArgs ?? DefaultClangCommandLineArgs)
{
"-x",
language,
Expand Down
Loading