From 20b41e85b8808f68ab1134fcefa039f8dae5f893 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 20 Nov 2025 15:41:24 +0100 Subject: [PATCH] Add IntegerLiteral.UnsignedValue. --- .../ClangSharp.Interop/Extensions/CXCursor.cs | 2 + .../clangsharp/clangsharp.cs | 4 ++ .../Cursors/Exprs/IntegerLiteral.cs | 2 + sources/libClangSharp/ClangSharp.cpp | 12 ++++ sources/libClangSharp/ClangSharp.h | 2 + .../CursorTests/DeclTest.cs | 59 +++++++++++++++++++ 6 files changed, 81 insertions(+) diff --git a/sources/ClangSharp.Interop/Extensions/CXCursor.cs b/sources/ClangSharp.Interop/Extensions/CXCursor.cs index 3af7012f..1035007b 100644 --- a/sources/ClangSharp.Interop/Extensions/CXCursor.cs +++ b/sources/ClangSharp.Interop/Extensions/CXCursor.cs @@ -1003,6 +1003,8 @@ public readonly CXCursor DefaultArg public readonly long IntegerLiteralValue => clangsharp.Cursor_getIntegerLiteralValue(this); + public readonly ulong UnsignedIntegerLiteralValue => clangsharp.Cursor_getUnsignedIntegerLiteralValue(this); + public readonly bool IsAllEnumCasesCovered => clangsharp.Cursor_getIsAllEnumCasesCovered(this) != 0; public readonly bool IsAlwaysNull => clangsharp.Cursor_getIsAlwaysNull(this) != 0; diff --git a/sources/ClangSharp.Interop/clangsharp/clangsharp.cs b/sources/ClangSharp.Interop/clangsharp/clangsharp.cs index f430268d..ed628810 100644 --- a/sources/ClangSharp.Interop/clangsharp/clangsharp.cs +++ b/sources/ClangSharp.Interop/clangsharp/clangsharp.cs @@ -380,6 +380,10 @@ public static partial class @clangsharp [return: NativeTypeName("int64_t")] public static extern long Cursor_getIntegerLiteralValue(CXCursor C); + [DllImport("libClangSharp", CallingConvention = CallingConvention.Cdecl, EntryPoint = "clangsharp_Cursor_getUnsignedIntegerLiteralValue", ExactSpelling = true)] + [return: NativeTypeName("uint64_t")] + public static extern ulong Cursor_getUnsignedIntegerLiteralValue(CXCursor C); + [DllImport("libClangSharp", CallingConvention = CallingConvention.Cdecl, EntryPoint = "clangsharp_Cursor_getIsAllEnumCasesCovered", ExactSpelling = true)] [return: NativeTypeName("unsigned int")] public static extern uint Cursor_getIsAllEnumCasesCovered(CXCursor C); diff --git a/sources/ClangSharp/Cursors/Exprs/IntegerLiteral.cs b/sources/ClangSharp/Cursors/Exprs/IntegerLiteral.cs index 35e42222..1c73bcd0 100644 --- a/sources/ClangSharp/Cursors/Exprs/IntegerLiteral.cs +++ b/sources/ClangSharp/Cursors/Exprs/IntegerLiteral.cs @@ -43,5 +43,7 @@ internal IntegerLiteral(CXCursor handle) : base(handle, CXCursor_IntegerLiteral, public long Value => Handle.IntegerLiteralValue; + public ulong UnsignedValue => Handle.UnsignedIntegerLiteralValue; + public string ValueString => _valueString.Value; } diff --git a/sources/libClangSharp/ClangSharp.cpp b/sources/libClangSharp/ClangSharp.cpp index 70389fb9..2ea369fb 100644 --- a/sources/libClangSharp/ClangSharp.cpp +++ b/sources/libClangSharp/ClangSharp.cpp @@ -1939,6 +1939,18 @@ int64_t clangsharp_Cursor_getIntegerLiteralValue(CXCursor C) { return 0; } +uint64_t clangsharp_Cursor_getUnsignedIntegerLiteralValue(CXCursor C) { + if (isStmtOrExpr(C.kind)) { + const Stmt* S = getCursorStmt(C); + + if (const IntegerLiteral* IL = dyn_cast(S)) { + return IL->getValue().getZExtValue(); + } + } + + return 0; +} + unsigned clangsharp_Cursor_getIsAllEnumCasesCovered(CXCursor C) { if (isStmtOrExpr(C.kind)) { const Stmt* S = getCursorStmt(C); diff --git a/sources/libClangSharp/ClangSharp.h b/sources/libClangSharp/ClangSharp.h index 4e5384b9..f2e4a734 100644 --- a/sources/libClangSharp/ClangSharp.h +++ b/sources/libClangSharp/ClangSharp.h @@ -455,6 +455,8 @@ CLANGSHARP_LINKAGE CXCursor clangsharp_Cursor_getInstantiatedFromMember(CXCursor CLANGSHARP_LINKAGE int64_t clangsharp_Cursor_getIntegerLiteralValue(CXCursor C); +CLANGSHARP_LINKAGE uint64_t clangsharp_Cursor_getUnsignedIntegerLiteralValue(CXCursor C); + CLANGSHARP_LINKAGE unsigned clangsharp_Cursor_getIsAllEnumCasesCovered(CXCursor C); CLANGSHARP_LINKAGE unsigned clangsharp_Cursor_getIsAlwaysNull(CXCursor C); diff --git a/tests/ClangSharp.UnitTests/CursorTests/DeclTest.cs b/tests/ClangSharp.UnitTests/CursorTests/DeclTest.cs index 399abb94..b3f6a0bb 100644 --- a/tests/ClangSharp.UnitTests/CursorTests/DeclTest.cs +++ b/tests/ClangSharp.UnitTests/CursorTests/DeclTest.cs @@ -125,4 +125,63 @@ struct B { Assert.That(structB, Is.Not.Null, "struct B not found"); Assert.That(structB.IsPOD, Is.False, "struct B should be not POD"); } + + [Test] + public void UnsignedValue() + { + ObjectiveCTest.AssertNeedNewClangSharp(); + + var inputContents = $$""" +enum E { + A = 1, + B = 4294967295U, + C = 4294967296U, + D = 18446744073709551615ULL, + E = -1, + F = -4294967295, + G = -4294967296, + H = -18446744073709551615LL, +}; +"""; + + using var translationUnit = CreateTranslationUnit(inputContents); + + var decls = translationUnit.TranslationUnitDecl.Decls.OfType().ToList(); + + var enumE = decls.SingleOrDefault(d => d.Name == "E")!; + Assert.That(enumE, Is.Not.Null, "enum E not found"); + + var checkField = (string fieldName, long expectedValue, ulong expectedUnsignedValue, bool negative) => { + var field = enumE.Enumerators.SingleOrDefault(e => e.Name == fieldName)!; + Assert.That(field, Is.Not.Null, $"enum E::{fieldName} not found"); + var initExpr = field.InitExpr; + Assert.That(initExpr, Is.Not.Null, $"enum E::{fieldName} InitExpr is null"); + + var isNegativeExpression = false; + var castExpr = (ImplicitCastExpr)initExpr!; + var subExpr = castExpr.SubExpr; + if (subExpr is UnaryOperator unaryOperator) + { + Assert.That(unaryOperator.Opcode, Is.EqualTo(CXUnaryOperatorKind.CXUnaryOperator_Minus), $"enum E::{fieldName} InitExpr is not a minus UnaryOperator"); + subExpr = unaryOperator.SubExpr; + isNegativeExpression = true; + } + var literalExpr = subExpr as IntegerLiteral; + Assert.That(literalExpr, Is.Not.Null, $"enum E::{fieldName} InitExpr is not IntegerLiteral {castExpr.SubExpr!.GetType().Name}"); + Assert.That(literalExpr!.Value, Is.EqualTo(expectedValue), $"enum E::{fieldName} value mismatch"); + Assert.That(literalExpr!.UnsignedValue, Is.EqualTo(expectedUnsignedValue), $"enum E::{fieldName} unsigned value mismatch"); + Assert.That(negative, Is.EqualTo(isNegativeExpression), $"enum E::{fieldName} negative mismatch"); + }; + + Assert.Multiple(() => { + checkField("A", 1, 1, false); + checkField("B", -1, 4294967295UL, false); + checkField("C", 4294967296, 4294967296UL, false); + checkField("D", -1, 18446744073709551615UL, false); + checkField("E", 1, 1, true); + checkField("F", 4294967295, 4294967295, true); + checkField("G", 4294967296, 4294967296, true); + checkField("H", -1, 18446744073709551615UL, true); + }); + } }