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
27 changes: 27 additions & 0 deletions sources/ClangSharp.Interop/Extensions/CXCursor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1043,6 +1043,8 @@ public readonly CXCursor DefaultArg

public readonly bool HasNonTrivialDestructor => clangsharp.Cursor_getHasNonTrivialDestructor(this) != 0;

public readonly bool HasObjCAvailabilityVersion => clangsharp.Cursor_getHasObjCAvailabilityVersion(this) != 0;

public readonly bool HasPlaceholderTypeConstraint => clangsharp.Cursor_getHasPlaceholderTypeConstraint(this) != 0;

public readonly bool HasPrivateFields => clangsharp.Cursor_getHasPrivateFields(this) != 0;
Expand Down Expand Up @@ -1165,6 +1167,8 @@ public readonly CXCursor DefaultArg

public readonly bool IsDefinition => clang.isCursorDefinition(this) != 0;

public readonly bool IsDelegateInitCall => clangsharp.Cursor_getIsDelegateInitCall(this) != 0;

public readonly bool IsDelegatingConstructor => clangsharp.Cursor_getIsDelegatingConstructor(this) != 0;

public readonly bool IsDeleted => clangsharp.Cursor_getIsDeleted(this) != 0;
Expand Down Expand Up @@ -1233,6 +1237,10 @@ public readonly CXCursor DefaultArg

public readonly bool IsMemberSpecialization => clangsharp.Cursor_getIsMemberSpecialization(this) != 0;

public readonly bool IsMessagingGetter => clangsharp.Cursor_getIsMessagingGetter(this) != 0;

public readonly bool IsMessagingSetter => clangsharp.Cursor_getIsMessagingSetter(this) != 0;

public readonly bool IsNegative => clangsharp.Cursor_getIsNegative(this) != 0;

public readonly bool IsNonNegative => clangsharp.Cursor_getIsNonNegative(this) != 0;
Expand Down Expand Up @@ -1425,12 +1433,29 @@ public readonly CXCursor DefaultArg

public readonly int NumVBases => clangsharp.Cursor_getNumVBases(this);

public readonly VersionTuple? ObjCAvailabilityVersion
{
get
{
VersionTuple rv = default;
if (clangsharp.Cursor_getObjCAvailabilityVersion(this, &rv) == 1)
{
return rv;
}
return null;
}
}

public readonly CXObjCDeclQualifierKind ObjCDeclQualifiers => (CXObjCDeclQualifierKind)clang.Cursor_getObjCDeclQualifiers(this);

public readonly CXStringSet* ObjCManglings => clang.Cursor_getObjCManglings(this);

public readonly CX_ObjCMessageReceiverKind ObjCMessageReceiverKind => clangsharp.Cursor_getObjCMessageReceiverKind(this);

public readonly CXString ObjCPropertyGetterName => clang.Cursor_getObjCPropertyGetterName(this);

public readonly CX_ObjCPropertyRefReceiverKind ObjCPropertyRefReceiverKind => clangsharp.Cursor_getObjCPropertyRefReceiverKind(this);

public readonly CXString ObjCPropertySetterName => clang.Cursor_getObjCPropertySetterName(this);

public readonly CXString ObjCRuntimeNameAttrMetadataName => clangsharp.Cursor_getObjCRuntimeNameAttrMetadataName(this);
Expand Down Expand Up @@ -1513,6 +1538,8 @@ public readonly ReadOnlySpan<CXCursor> OverriddenCursors

public readonly CXCursor SemanticParent => clang.getCursorSemanticParent(this);

public readonly CXCursor SetAtIndexMethodDecl => clangsharp.Cursor_getSetAtIndexMethodDecl(this);

public readonly bool ShouldCopy => clangsharp.Cursor_getShouldCopy(this) != 0;

public readonly CXSourceRange SourceRange => clangsharp.Cursor_getSourceRange(this);
Expand Down
4 changes: 4 additions & 0 deletions sources/ClangSharp/Cursors/Exprs/ObjCAvailabilityCheckExpr.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,8 @@ public sealed class ObjCAvailabilityCheckExpr : Expr
internal ObjCAvailabilityCheckExpr(CXCursor handle) : base(handle, CXCursor_ObjCAvailabilityCheckExpr, CX_StmtClass_ObjCAvailabilityCheckExpr)
{
}

public bool HasVersion => Handle.HasObjCAvailabilityVersion;

public VersionTuple? Version => Handle.ObjCAvailabilityVersion;
}
4 changes: 4 additions & 0 deletions sources/ClangSharp/Cursors/Exprs/ObjCMessageExpr.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@ internal unsafe ObjCMessageExpr(CXCursor handle) : base(handle, CXCursor_ObjCMes

public bool IsImplicit => Handle.IsImplicit;

public bool IsDelegateInitCall => Handle.IsDelegateInitCall;

public Expr InstanceReceiver => _instanceReceiver.GetValue(this);

public CX_ObjCMessageReceiverKind ReceiverKind => Handle.ObjCMessageReceiverKind;

public Type ReceiverType => _receiverType.GetValue(this);

public Type SuperType => _superType.GetValue(this);
Expand Down
6 changes: 6 additions & 0 deletions sources/ClangSharp/Cursors/Exprs/ObjCPropertyRefExpr.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,16 @@ internal unsafe ObjCPropertyRefExpr(CXCursor handle) : base(handle, CXCursor_Mem

public bool IsImplicitProperty => Handle.IsImplicit;

public bool IsMessagingGetter => Handle.IsMessagingGetter;

public bool IsMessagingSetter => Handle.IsMessagingSetter;

public ObjCMethodDecl ImplicitPropertyGetter => _implicitPropertyGetter.GetValue(this);

public ObjCMethodDecl ImplicitPropertySetter => _implicitPropertySetter.GetValue(this);

public CX_ObjCPropertyRefReceiverKind ReceiverKind => Handle.ObjCPropertyRefReceiverKind;

public Type SuperReceiverType => _superReceiverType.GetValue(this);

private static unsafe Type SuperReceiverTypeFactory(ObjCPropertyRefExpr self) => self.TranslationUnit.GetOrCreate<Type>(self.Handle.TypeOperand);
Expand Down
6 changes: 6 additions & 0 deletions sources/ClangSharp/Cursors/Exprs/ObjCSubscriptRefExpr.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ namespace ClangSharp;
public sealed class ObjCSubscriptRefExpr : Expr
{
private ValueLazy<ObjCSubscriptRefExpr, ObjCMethodDecl> _atIndexMethodDecl;
private ValueLazy<ObjCSubscriptRefExpr, ObjCMethodDecl> _setAtIndexMethodDecl;

internal unsafe ObjCSubscriptRefExpr(CXCursor handle) : base(handle, CXCursor_UnexposedExpr, CX_StmtClass_ObjCSubscriptRefExpr)
{
Debug.Assert(NumChildren is 2);
_atIndexMethodDecl = new ValueLazy<ObjCSubscriptRefExpr, ObjCMethodDecl>(&AtIndexMethodDeclFactory);
_setAtIndexMethodDecl = new ValueLazy<ObjCSubscriptRefExpr, ObjCMethodDecl>(&SetAtIndexMethodDeclFactory);
}

public ObjCMethodDecl AtIndexMethodDecl => _atIndexMethodDecl.GetValue(this);
Expand All @@ -25,5 +27,9 @@ internal unsafe ObjCSubscriptRefExpr(CXCursor handle) : base(handle, CXCursor_Un

public bool IsArraySubscriptRefExpr => KeyExpr.Type.IsIntegralOrEnumerationType;

public ObjCMethodDecl SetAtIndexMethodDecl => _setAtIndexMethodDecl.GetValue(this);

private static unsafe ObjCMethodDecl AtIndexMethodDeclFactory(ObjCSubscriptRefExpr self) => self.TranslationUnit.GetOrCreate<ObjCMethodDecl>(self.Handle.Referenced);

private static unsafe ObjCMethodDecl SetAtIndexMethodDeclFactory(ObjCSubscriptRefExpr self) => self.TranslationUnit.GetOrCreate<ObjCMethodDecl>(self.Handle.SetAtIndexMethodDecl);
}
96 changes: 96 additions & 0 deletions tests/ClangSharp.UnitTests/CursorTests/ObjCExprTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.

using System;
using System.Collections.Generic;
using System.Linq;
using ClangSharp.Interop;
using NUnit.Framework;
using static ClangSharp.Interop.CX_ObjCMessageReceiverKind;
using static ClangSharp.Interop.CX_ObjCPropertyRefReceiverKind;

namespace ClangSharp.UnitTests;

public sealed class ObjCExprTest : TranslationUnitTest
{
// Objective-C expression semantics (property messaging, object subscripting, @available) require a
// modern runtime, so an Apple target triple is pinned to keep the parse deterministic cross-platform.
private static readonly string[] s_commandLineArgs =
[
"-std=c++17",
"-target",
"x86_64-apple-macosx10.15.0",
"-fobjc-runtime=macosx",
];

[Test]
public void ObjCExprAccessors()
{
var inputContents = """
@interface Widget
- (int)compute;
@property int value;
- (id)objectAtIndexedSubscript:(unsigned long)idx;
- (void)setObject:(id)obj atIndexedSubscript:(unsigned long)idx;
@end

@implementation Widget
- (int)compute { return 0; }
- (id)objectAtIndexedSubscript:(unsigned long)idx { return self; }
- (void)setObject:(id)obj atIndexedSubscript:(unsigned long)idx { }
- (void)run
{
[self compute];
self.value = 7;
int x = self.value;
Widget* w = self;
w[0] = self;
id y = w[1];
if (__builtin_available(macos 10.15, *)) { }
}
@end
""";

using var translationUnit = CreateTranslationUnit(inputContents, "objective-c++", s_commandLineArgs);

var impl = translationUnit.TranslationUnitDecl.CursorChildren.OfType<ObjCImplDecl>().Single();
var run = impl.CursorChildren.OfType<ObjCMethodDecl>().Single((method) => method.Name.Contains("run", StringComparison.Ordinal));

var message = Descendants(run).OfType<ObjCMessageExpr>().Single();
Assert.That(message.ReceiverKind, Is.EqualTo(CX_OMRK_Instance));
Assert.That(message.IsDelegateInitCall, Is.False);

var propertyRefs = Descendants(run).OfType<ObjCPropertyRefExpr>().ToList();
Assert.That(propertyRefs, Has.Count.EqualTo(2));
Assert.That(propertyRefs.All((propertyRef) => propertyRef.ReceiverKind == CX_OPRK_Object), Is.True);
Assert.That(propertyRefs.Any((propertyRef) => propertyRef.IsMessagingGetter), Is.True);
Assert.That(propertyRefs.Any((propertyRef) => propertyRef.IsMessagingSetter), Is.True);

var subscripts = Descendants(run).OfType<ObjCSubscriptRefExpr>().ToList();
Assert.That(subscripts, Has.Count.EqualTo(2));

// Binding the underlying accessor methods requires a real Foundation collection, so here we only
// exercise that the new SetAtIndexMethodDecl accessor is reachable alongside the existing one.
foreach (var subscript in subscripts)
{
Assert.That(() => subscript.AtIndexMethodDecl, Throws.Nothing);
Assert.That(() => subscript.SetAtIndexMethodDecl, Throws.Nothing);
}

var availability = Descendants(run).OfType<ObjCAvailabilityCheckExpr>().Single();
Assert.That(availability.HasVersion, Is.True);
Assert.That(availability.Version?.ToString(), Is.EqualTo("10.15"));
}

private static IEnumerable<Cursor> Descendants(Cursor cursor)
{
foreach (var child in cursor.CursorChildren)
{
yield return child;

foreach (var descendant in Descendants(child))
{
yield return descendant;
}
}
}
}
Loading