Skip to content

Fields #880

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 10 commits into from
Apr 18, 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
23 changes: 21 additions & 2 deletions src/generators/Silk.NET.SilkTouch.Emitter/CSharpEmitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ private class Visitor : Silk.NET.SilkTouch.Symbols.SymbolVisitor
public CSharpSyntaxNode? Syntax => _syntax;
private CSharpSyntaxNode? _syntax = null;

protected override Symbol VisitStruct(StructSymbol structSymbol)
protected override StructSymbol VisitStruct(StructSymbol structSymbol)
{
var members = List<MemberDeclarationSyntax>();
var modifiers = TokenList(Token(SyntaxTriviaList.Empty, SyntaxKind.PublicKeyword, TriviaList(Space)));
Expand All @@ -61,7 +61,26 @@ protected override Symbol VisitStruct(StructSymbol structSymbol)
List<TypeParameterConstraintClauseSyntax>(), members
)
.WithKeyword(Token(SyntaxTriviaList.Empty, SyntaxKind.StructKeyword, TriviaList(Space)));
return base.VisitStruct(structSymbol);
return structSymbol;
}

protected override FieldSymbol VisitField(FieldSymbol fieldSymbol)
{
_syntax = FieldDeclaration
(
List<AttributeListSyntax>(),
TokenList(Token(SyntaxTriviaList.Empty, SyntaxKind.PublicKeyword, TriviaList(Space))),
VariableDeclaration
(
IdentifierName(fieldSymbol.Type.Identifier.Value),
SingletonSeparatedList
(
VariableDeclarator
(Identifier(TriviaList(Space), fieldSymbol.Identifier.Value, SyntaxTriviaList.Empty))
)
)
);
return fieldSymbol;
}
}
}
32 changes: 32 additions & 0 deletions src/generators/Silk.NET.SilkTouch.Symbols/FieldSymbol.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 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>
/// A <see cref="FieldSymbol"/>. A field is simply a named location that can hold some type.
/// </summary>
/// <seealso cref="MemberSymbol"/>
public sealed class FieldSymbol : MemberSymbol
{
/// <summary>
/// Create a field symbol from the parent <see cref="TypeSymbol"/>, the type of the field and it's identifier
/// </summary>
/// <param name="type">The type of the data stored in this field</param>
/// <param name="identifier">The identifier of this field</param>
public FieldSymbol(TypeSymbol type, IdentifierSymbol identifier)
{
Type = type;
Identifier = identifier;
}

/// <summary>
/// The <see cref="TypeSymbol"/> of the data stored in this field
/// </summary>
public TypeSymbol Type { get; }

/// <summary>
/// The Identifier of this field
/// </summary>
public IdentifierSymbol Identifier { get; }
}
12 changes: 12 additions & 0 deletions src/generators/Silk.NET.SilkTouch.Symbols/MemberSymbol.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// 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>
/// A <see cref="MemberSymbol"/>, representing a generic member of some <see cref="TypeSymbol"/>
/// </summary>
/// <seealso cref="FieldSymbol"/>
public abstract class MemberSymbol : Symbol
{
}
45 changes: 37 additions & 8 deletions src/generators/Silk.NET.SilkTouch.Symbols/SymbolVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,33 +16,62 @@ public abstract class SymbolVisitor
public virtual Symbol Visit(Symbol symbol)
{
if (symbol is TypeSymbol ts) return VisitType(ts);
if (symbol is MemberSymbol ms) return VisitMember(ms);

return ThrowUnknownSymbol(nameof(Symbol), symbol);
if (symbol is IdentifierSymbol @is) return VisitIdentifier(@is);

return ThrowUnknownSymbol<Symbol>(symbol);
}

/// <summary>
/// Visit a <see cref="MemberSymbol"/>. This will call the appropriate method based on the actual type of the <paramref name="memberSymbol"/>
/// </summary>
/// <param name="memberSymbol">The member symbol to visit</param>
/// <returns>The rewritten symbol</returns>
/// <seealso cref="VisitField"/>
protected virtual MemberSymbol VisitMember(MemberSymbol memberSymbol)
{
if (memberSymbol is FieldSymbol fs) return VisitField(fs);

return ThrowUnknownSymbol<MemberSymbol>(memberSymbol);
}

/// <summary>
/// Visit a <see cref="FieldSymbol"/>. Will call the appropriate methods to visit the different parts of the field.
/// </summary>
/// <param name="fieldSymbol">The field symbol 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 FieldSymbol VisitField(FieldSymbol fieldSymbol)
{
return new FieldSymbol(VisitType(fieldSymbol.Type), VisitIdentifier(fieldSymbol.Identifier));
}

/// <summary>
/// Visit a <see cref="TypeSymbol"/>. This will call the appropriate method based on the actual type of the <paramref name="typeSymbol"/>
/// </summary>
/// <param name="typeSymbol">The type symbol to visit</param>
/// <returns>The rewritten symbol. May or may not be a <see cref="TypeSymbol"/></returns>
/// <returns>The rewritten symbol</returns>
/// <seealso cref="VisitStruct"/>
protected virtual Symbol VisitType(TypeSymbol typeSymbol)
protected virtual TypeSymbol VisitType(TypeSymbol typeSymbol)
{
if (typeSymbol is StructSymbol @struct) return VisitStruct(@struct);

return ThrowUnknownSymbol(nameof(TypeSymbol), typeSymbol);
return ThrowUnknownSymbol<TypeSymbol>(typeSymbol);
}

/// <summary>
/// Visit a <see cref="StructSymbol"/>. Will call the appropriate methods to visit the different parts of the struct.
/// </summary>
/// <param name="structSymbol">The struct symbol to visit</param>
/// <returns>The rewritten symbol. May or may not be a <see cref="StructSymbol"/></returns>
/// <returns>The rewritten symbol</returns>
/// <seealso cref="VisitType"/>
/// <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 Symbol VisitStruct(StructSymbol structSymbol)
protected virtual StructSymbol VisitStruct(StructSymbol structSymbol)
{
return new StructSymbol(VisitIdentifier(structSymbol.Identifier));
}
Expand All @@ -57,8 +86,8 @@ protected virtual IdentifierSymbol VisitIdentifier(IdentifierSymbol identifierSy
return identifierSymbol;
}

private static Symbol ThrowUnknownSymbol(string type, Symbol symbol)
private static T ThrowUnknownSymbol<T>(Symbol symbol)
{
throw new NotImplementedException($"Unknown symbol of type {symbol.GetType().FullName} subclass of {type}");
throw new NotImplementedException($"Unknown symbol of type {symbol.GetType().FullName} subclass of {typeof(T).Name}");
}
}
23 changes: 23 additions & 0 deletions tests/Silk.NET.SilkTouch.Emitter.Tests/EmitterFieldTests.cs
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 Silk.NET.SilkTouch.Symbols;
using Xunit;

namespace Silk.NET.SilkTouch.Emitter.Tests;

public sealed class EmitterFieldIntegrationTests : EmitterTest
{
[Fact]
public void StructHasStructKeyword()
{
var emitter = CreateEmitter();

var symbol = new FieldSymbol(new StructSymbol(new IdentifierSymbol("int")), new IdentifierSymbol("Test"));

var syntax = emitter.Transform(symbol);

var result = syntax.ToFullString();
Assert.Equal("public int Test;", result);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// 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 FieldTests
{
[Fact]
public void FieldIsVisitedAsField()
{
var symbol = new FieldSymbol(new StructSymbol(new IdentifierSymbol("")), new IdentifierSymbol(""));
var visitor = new Mock<SymbolVisitor>
{
CallBase = true
};

visitor.Object.Visit(symbol);

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

[Fact]
public void FieldIsVisitedAsMember()
{
var symbol = new FieldSymbol(new StructSymbol(new IdentifierSymbol("")), new IdentifierSymbol(""));
var visitor = new Mock<SymbolVisitor>
{
CallBase = true
};

visitor.Object.Visit(symbol);

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

[Fact]
public void FieldTypeIsVisited()
{
var symbol = new FieldSymbol(new StructSymbol(new IdentifierSymbol("")), new IdentifierSymbol(""));
var visitor = new Mock<SymbolVisitor>
{
CallBase = true
};

visitor.Object.Visit(symbol);

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

[Fact]
public void FieldIdentifierIsVisited()
{
var symbol = new FieldSymbol(new StructSymbol(new IdentifierSymbol("")), new IdentifierSymbol(""));
var visitor = new Mock<SymbolVisitor>
{
CallBase = true
};

visitor.Object.Visit(symbol);

// 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>());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// 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 IdentifierTests
{
[Fact]
public void IdentifierIsVisitedAsIdentifier()
{
var symbol = new IdentifierSymbol("");
var visitor = new Mock<SymbolVisitor>
{
CallBase = true
};

visitor.Object.Visit(symbol);

visitor.Protected()
.Verify<IdentifierSymbol>("VisitIdentifier", Times.Once(), ItExpr.IsAny<IdentifierSymbol>());
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// 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;
namespace Silk.NET.SilkTouch.Symbols.Tests.SymbolVisitorTests;

public sealed class SymbolVisitorTests
public class StructTests
{
[Fact]
public void StructSymbolIsVisitedAsType()
Expand All @@ -21,7 +21,7 @@ public void StructSymbolIsVisitedAsType()
visitor.Object.Visit(symbol);

visitor.Protected()
.Verify<Symbol>("VisitType", Times.Once(), ItExpr.IsAny<TypeSymbol>());
.Verify<TypeSymbol>("VisitType", Times.Once(), ItExpr.IsAny<TypeSymbol>());
}

[Fact]
Expand All @@ -36,7 +36,7 @@ public void StructSymbolIsVisitedAsStruct()
visitor.Object.Visit(symbol);

visitor.Protected()
.Verify<Symbol>("VisitStruct", Times.Once(), ItExpr.IsAny<StructSymbol>());
.Verify<StructSymbol>("VisitStruct", Times.Once(), ItExpr.IsAny<StructSymbol>());
}

[Fact]
Expand All @@ -51,6 +51,6 @@ public void StructIdentifierIsVisitedAsIdentifier()
visitor.Object.Visit(symbol);

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