Skip to content

External Type Reference #970

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 8, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ protected override FieldSymbol VisitField(FieldSymbol fieldSymbol)
{
AssertClearState();

VisitIdentifier(fieldSymbol.Type.Identifier);
VisitExternalTypeReference(fieldSymbol.Type);
if (_syntax is not IdentifierNameSyntax typeIdentifierSyntax)
throw new InvalidOperationException("Field type Identifier was not visited correctly");
ClearState();
Expand Down
12 changes: 7 additions & 5 deletions src/generators/Silk.NET.SilkTouch.Scraper/XmlVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,14 @@ private IEnumerable<Symbol> VisitField(XmlElement field)
throw new InvalidOperationException("Field requires a name");
}

var type = new StructSymbol
var type = new ExternalTypeReference
(
new IdentifierSymbol(
field.ChildNodes.Cast<XmlNode>().SingleOrDefault(x => x.Name == "type")?.InnerText ??
throw new InvalidOperationException("Could not decode Field Type")),
ImmutableArray<FieldSymbol>.Empty
null,
new IdentifierSymbol
(
field.ChildNodes.Cast<XmlNode>().SingleOrDefault(x => x.Name == "type")?.InnerText ??
throw new InvalidOperationException("Could not decode Field Type")
)
);

return new[]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Silk.NET.SilkTouch.Symbols;

/// <summary>
/// Represents a reference to an external type
/// </summary>
public record ExternalTypeReference(IdentifierSymbol? Namespace, IdentifierSymbol TypeIdentifier) : Symbol
{
/// <summary>
/// Gets the full unique name in C# global:: format.
/// </summary>
public string FullType => (Namespace is not null ? "global::" + Namespace.ToString() + "." : "") + TypeIdentifier.ToString();
}
4 changes: 2 additions & 2 deletions src/generators/Silk.NET.SilkTouch.Symbols/FieldSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Silk.NET.SilkTouch.Symbols;
/// <summary>
/// A <see cref="FieldSymbol"/>. A field is simply a named location that can hold some type.
/// </summary>
/// <param name="Type">The <see cref="TypeSymbol"/> of the data stored in this field</param>
/// <param name="Type">The <see cref="ExternalTypeReference"/> of the data stored in this field</param>
/// <param name="Identifier">The Identifier of this field</param>
/// <seealso cref="MemberSymbol"/>
public sealed record FieldSymbol(TypeSymbol Type, IdentifierSymbol Identifier) : MemberSymbol;
public sealed record FieldSymbol(ExternalTypeReference Type, IdentifierSymbol Identifier) : MemberSymbol;
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@ namespace Silk.NET.SilkTouch.Symbols;
/// </summary>
/// <param name="Value">The String Value of this identifier</param>
/// <seealso cref="TypeSymbol"/>
public sealed record IdentifierSymbol(string Value) : Symbol;
public sealed record IdentifierSymbol(string Value) : Symbol
{
/// <inheritdoc cref="object.ToString"/>
public override string ToString() => Value;
}
20 changes: 19 additions & 1 deletion src/generators/Silk.NET.SilkTouch.Symbols/SymbolVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public virtual Symbol Visit(Symbol symbol)
if (symbol is NamespaceSymbol ns) return VisitNamespace(ns);

if (symbol is IdentifierSymbol @is) return VisitIdentifier(@is);
if (symbol is ExternalTypeReference etr) return VisitExternalTypeReference(etr);

return ThrowUnknownSymbol<Symbol>(symbol);
}
Expand Down Expand Up @@ -49,7 +50,24 @@ protected virtual MemberSymbol VisitMember(MemberSymbol memberSymbol)
/// </remarks>
protected virtual FieldSymbol VisitField(FieldSymbol fieldSymbol)
{
return new FieldSymbol(VisitType(fieldSymbol.Type), VisitIdentifier(fieldSymbol.Identifier));
return new FieldSymbol(VisitExternalTypeReference(fieldSymbol.Type), VisitIdentifier(fieldSymbol.Identifier));
}

/// <summary>
/// Visit an <see cref="ExternalTypeReference"/>. Will call the appropriate methods to visit the different parts of the reference.
/// </summary>
/// <param name="typeReference">The type reference to visit</param>
/// <returns>The rewritten symbol</returns>
/// <remarks>
/// The order in which the parts of the struct are visited is kept as an implementation detail. Do not rely on this order.
/// </remarks>
protected virtual ExternalTypeReference VisitExternalTypeReference(ExternalTypeReference typeReference)
{
return new ExternalTypeReference
(
typeReference.Namespace is null ? null : VisitIdentifier(typeReference.Namespace),
VisitIdentifier(typeReference.TypeIdentifier)
);
}

/// <summary>
Expand Down
8 changes: 4 additions & 4 deletions tests/Silk.NET.SilkTouch.Emitter.Tests/EmitterFieldTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public sealed class EmitterFieldIntegrationTests : EmitterTest
Trait("Target Language", "C#")]
public void FieldIntegration()
{
var syntax = Transform(new FieldSymbol(new StructSymbol(new IdentifierSymbol("int"),ImmutableArray<FieldSymbol>.Empty), new IdentifierSymbol("Test")));
var syntax = Transform(new FieldSymbol(new ExternalTypeReference(null, new IdentifierSymbol("int")), new IdentifierSymbol("Test")));

var result = syntax.ToFullString();
Assert.Equal("public int Test;", result);
Expand All @@ -35,7 +35,7 @@ public void FieldIsPublic()
(
new FieldSymbol
(
new StructSymbol(new IdentifierSymbol("int"), ImmutableArray<FieldSymbol>.Empty),
new ExternalTypeReference(null, new IdentifierSymbol("")),
new IdentifierSymbol("Test")
)
) as FieldDeclarationSyntax;
Expand All @@ -54,7 +54,7 @@ public void CorrectTypeIdentifier()
(
new FieldSymbol
(
new StructSymbol(new IdentifierSymbol("int"), ImmutableArray<FieldSymbol>.Empty),
new ExternalTypeReference(null, new IdentifierSymbol("int")),
new IdentifierSymbol("Test")
)
) as FieldDeclarationSyntax;
Expand All @@ -75,7 +75,7 @@ public void CorrectIdentifier()
(
new FieldSymbol
(
new StructSymbol(new IdentifierSymbol("int"), ImmutableArray<FieldSymbol>.Empty),
new ExternalTypeReference(null, new IdentifierSymbol("")),
new IdentifierSymbol("Test")
)
) as FieldDeclarationSyntax;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public void SingleFieldIntegration()
{
new FieldSymbol
(
new StructSymbol(new IdentifierSymbol("int"), ImmutableArray<FieldSymbol>.Empty),
new ExternalTypeReference(null, new IdentifierSymbol("int")),
new IdentifierSymbol("F1")
)
}.ToImmutableArray()
Expand Down Expand Up @@ -57,17 +57,17 @@ public void MultipleFieldsIntegration()
{
new FieldSymbol
(
new StructSymbol(new IdentifierSymbol("int"), ImmutableArray<FieldSymbol>.Empty),
new ExternalTypeReference(null, new IdentifierSymbol("int")),
new IdentifierSymbol("F1")
),
new FieldSymbol
(
new StructSymbol(new IdentifierSymbol("int"), ImmutableArray<FieldSymbol>.Empty),
new ExternalTypeReference(null, new IdentifierSymbol("int")),
new IdentifierSymbol("F2")
),
new FieldSymbol
(
new StructSymbol(new IdentifierSymbol("int"), ImmutableArray<FieldSymbol>.Empty),
new ExternalTypeReference(null, new IdentifierSymbol("int")),
new IdentifierSymbol("F3")
)
}.ToImmutableArray()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,6 @@ public void CorrectType()

var symbol = Assert.Single(symbols);
var field = Assert.IsType<FieldSymbol>(symbol);
Assert.Equal("int", field.Type.Identifier.Value);
Assert.Equal("int", field.Type.FullType);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Xunit;

namespace Silk.NET.SilkTouch.Symbols.Tests;

public class ExternalTypeReferenceTests
{
[Theory]
[InlineData("N1", "T1", "global::N1.T1"), InlineData(null, "T1", "T1"), InlineData(null, "int", "int"),
InlineData("System", "Int32", "global::System.Int32")]
[Trait("Category", "Symbols")]
public void ProducesCorrectFullType(string? @namespace, string type, string expected)
{
Assert.Equal
(
expected,
new ExternalTypeReference
(@namespace is null ? null : new IdentifierSymbol(@namespace), new IdentifierSymbol(type)).FullType
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Moq;
using Moq.Protected;
using Xunit;

namespace Silk.NET.SilkTouch.Symbols.Tests.SymbolVisitorTests;

public class ExternalTypeReferenceTests
{
[Fact,
Trait("Category", "Symbols")]
public void RefIsVisitedAsRef()
{
var symbol = new ExternalTypeReference(null, new IdentifierSymbol(""));
var visitor = new Mock<SymbolVisitor>
{
CallBase = true
};

visitor.Object.Visit(symbol);

visitor.Protected()
.Verify<ExternalTypeReference>("VisitExternalTypeReference", Times.Once(), ItExpr.IsAny<ExternalTypeReference>());
}

[Fact,
Trait("Category", "Symbols")]
public void RefTypeIdentifierIsVisitedAsIdentifier()
{
var symbol = new ExternalTypeReference(null, new IdentifierSymbol(""));
var visitor = new Mock<SymbolVisitor>
{
CallBase = true
};

visitor.Object.Visit(symbol);

visitor.Protected()
.Verify<IdentifierSymbol>("VisitIdentifier", Times.Once(), ItExpr.IsAny<IdentifierSymbol>());
}

[Fact,
Trait("Category", "Symbols")]
public void RefNamespaceIsVisitedAsIdentifier()
{
var symbol = new ExternalTypeReference(new IdentifierSymbol(""), new IdentifierSymbol(""));
var visitor = new Mock<SymbolVisitor>
{
CallBase = true
};

visitor.Object.Visit(symbol);

visitor.Protected()
.Verify<IdentifierSymbol>("VisitIdentifier", Times.Exactly(2), ItExpr.IsAny<IdentifierSymbol>());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class FieldTests
Trait("Feature", "Fields")]
public void FieldIsVisitedAsField()
{
var symbol = new FieldSymbol(new StructSymbol(new IdentifierSymbol(""), ImmutableArray<FieldSymbol>.Empty), new IdentifierSymbol(""));
var symbol = new FieldSymbol(new ExternalTypeReference(null, new IdentifierSymbol("")), new IdentifierSymbol(""));
var visitor = new Mock<SymbolVisitor>
{
CallBase = true
Expand All @@ -32,7 +32,7 @@ public void FieldIsVisitedAsField()
Trait("Feature", "Fields")]
public void FieldIsVisitedAsMember()
{
var symbol = new FieldSymbol(new StructSymbol(new IdentifierSymbol(""), ImmutableArray<FieldSymbol>.Empty), new IdentifierSymbol(""));
var symbol = new FieldSymbol(new ExternalTypeReference(null, new IdentifierSymbol("")), new IdentifierSymbol(""));
var visitor = new Mock<SymbolVisitor>
{
CallBase = true
Expand All @@ -49,7 +49,7 @@ public void FieldIsVisitedAsMember()
Trait("Feature", "Fields")]
public void FieldTypeIsVisited()
{
var symbol = new FieldSymbol(new StructSymbol(new IdentifierSymbol(""), ImmutableArray<FieldSymbol>.Empty), new IdentifierSymbol(""));
var symbol = new FieldSymbol(new ExternalTypeReference(null, new IdentifierSymbol("")), new IdentifierSymbol(""));
var visitor = new Mock<SymbolVisitor>
{
CallBase = true
Expand All @@ -58,15 +58,15 @@ public void FieldTypeIsVisited()
visitor.Object.Visit(symbol);

visitor.Protected()
.Verify<TypeSymbol>("VisitType", Times.Once(), ItExpr.IsAny<TypeSymbol>());
.Verify<IdentifierSymbol>("VisitIdentifier", Times.Exactly(2), ItExpr.IsAny<IdentifierSymbol>());
}

[Fact,
Trait("Category", "Symbols"),
Trait("Feature", "Fields")]
public void FieldIdentifierIsVisited()
{
var symbol = new FieldSymbol(new StructSymbol(new IdentifierSymbol(""), ImmutableArray<FieldSymbol>.Empty), new IdentifierSymbol(""));
var symbol = new FieldSymbol(new ExternalTypeReference(null, new IdentifierSymbol("")), new IdentifierSymbol(""));
var visitor = new Mock<SymbolVisitor>
{
CallBase = true
Expand All @@ -76,6 +76,6 @@ public void FieldIdentifierIsVisited()

// note that this also tests whether the struct identifier is visited, there's just no good way of testing JUST the field identifier
visitor.Protected()
.Verify<IdentifierSymbol>("VisitIdentifier", Times.Exactly(2), ItExpr.IsAny<IdentifierSymbol>());
.Verify<ExternalTypeReference>("VisitExternalTypeReference", Times.Exactly(1), ItExpr.IsAny<ExternalTypeReference>());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public void StructIdentifierIsVisitedAsIdentifier()
Trait("Feature", "Fields")]
public void StructFieldIsVisited()
{
var member = new FieldSymbol(new StructSymbol(new IdentifierSymbol("int"), ImmutableArray<FieldSymbol>.Empty), new IdentifierSymbol("Test1"));
var member = new FieldSymbol(new ExternalTypeReference(null, new IdentifierSymbol("int")), new IdentifierSymbol("Test1"));
var symbol = new StructSymbol(new IdentifierSymbol("Test"), new[]
{
member
Expand All @@ -90,8 +90,8 @@ public void StructFieldIsVisited()
Trait("Feature", "Fields")]
public void StructFieldsAreVisited()
{
var member1 = new FieldSymbol(new StructSymbol(new IdentifierSymbol("int"), ImmutableArray<FieldSymbol>.Empty), new IdentifierSymbol("Test1"));
var member2 = new FieldSymbol(new StructSymbol(new IdentifierSymbol("int"), ImmutableArray<FieldSymbol>.Empty), new IdentifierSymbol("Test2"));
var member1 = new FieldSymbol(new ExternalTypeReference(null, new IdentifierSymbol("int")), new IdentifierSymbol("Test1"));
var member2 = new FieldSymbol(new ExternalTypeReference(null, new IdentifierSymbol("int")), new IdentifierSymbol("Test2"));
var symbol = new StructSymbol(new IdentifierSymbol("Test"), new[]
{
member1, member2
Expand Down