Skip to content

Commit

Permalink
adds nullability attributes to record defintion (#295)
Browse files Browse the repository at this point in the history
  • Loading branch information
adrianoc committed Jun 13, 2024
1 parent 7625cd7 commit 7c7ecc5
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 10 deletions.
14 changes: 11 additions & 3 deletions Cecilifier.Core.Tests/Tests/Unit/Miscellaneous.cs
Original file line number Diff line number Diff line change
Expand Up @@ -249,13 +249,21 @@ public void TestImplicitObjectCreation(string code, params string[] expectations

public class RecordTests : CecilifierUnitTestBase
{
[TestCase("class", "struct", TestName = "NullableContext and NullableAttribute are added to the type definition")]
[TestCase("class", TestName = "NullableContext and NullableAttribute are added to the type definition - class")]
[TestCase("struct", TestName = "NullableContext and NullableAttribute are added to the type definition - struct")]
public void NullableContextAndNullableAttributes(string kind)
{
var r = RunCecilifier($"public record {kind} RecordTest;");
var cecilifiedCode = r.GeneratedCode.ReadToEnd();
Assert.That(cecilifiedCode, Does.Match(@"NullableContext\(1\)"));
Assert.That(cecilifiedCode, Does.Match(@"Nullable\(0\)"));
Assert.That(cecilifiedCode, Does.Match("""
(attr_nullableContext_\d+).ConstructorArguments.Add\(new CustomAttributeArgument\(assembly.MainModule.TypeSystem.Int32, 1\)\);
\s+rec_recordTest_\d+.CustomAttributes.Add\(\1\);
"""));

Assert.That(cecilifiedCode, Does.Match("""
(attr_nullable_\d+).ConstructorArguments.Add\(new CustomAttributeArgument\(assembly.MainModule.TypeSystem.Int32, 0\)\);
\s+rec_recordTest_\d+.CustomAttributes.Add\(\1\);
"""));
}
}
}
Expand Down
1 change: 1 addition & 0 deletions Cecilifier.Core/AST/TypeDeclarationVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public override void VisitRecordDeclaration(RecordDeclarationSyntax node)
base.VisitRecordDeclaration(node);

RecordGenerator generator = new(Context, definitionVar, node);
generator.AddNullabilityAttributesToTypeDefinition(definitionVar);
generator.AddSyntheticMembers();
}

Expand Down
2 changes: 1 addition & 1 deletion Cecilifier.Core/CodeGeneration/Property.Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ private void AddBackingFieldIfNeeded(ref readonly PropertyGenerationData propert
private void AddCompilerGeneratedAttributeTo(IVisitorContext context, string memberVariable)
{
var compilerGeneratedAttributeCtor = context.RoslynTypeSystem.SystemRuntimeCompilerServicesCompilerGeneratedAttribute.Ctor();
var exps = CecilDefinitionsFactory.Attribute(memberVariable, context, compilerGeneratedAttributeCtor.MethodResolverExpression(context));
var exps = CecilDefinitionsFactory.Attribute("compilerGenerated", memberVariable, context, compilerGeneratedAttributeCtor.MethodResolverExpression(context));
context.WriteCecilExpressions(exps);
}

Expand Down
32 changes: 29 additions & 3 deletions Cecilifier.Core/CodeGeneration/Record.Generator.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using Cecilifier.Core.AST;
using Cecilifier.Core.CodeGeneration.Extensions;
Expand Down Expand Up @@ -33,6 +34,31 @@ public RecordGenerator(IVisitorContext context, string recordTypeDefinitionVaria
this.record = record;
}

public void AddNullabilityAttributesToTypeDefinition(string definitionVar)
{
// https://github.com/dotnet/roslyn/blob/main/docs/features/nullable-metadata.md
const string NullableOblivious = "0";
const string NonNullable = "1";

var nullableAttributeCtor = context.RoslynTypeSystem.ForType<NullableAttribute>()
.GetMembers(".ctor")
.OfType<IMethodSymbol>()
.Single(ctor => ctor.Parameters.Length == 1 && ctor.Parameters[0].Type.SpecialType == SpecialType.System_Byte)
.MethodResolverExpression(context);

var nullableAttrExps = CecilDefinitionsFactory.Attribute("nullable", definitionVar, context, nullableAttributeCtor, [(context.TypeResolver.Bcl.System.Int32, NullableOblivious)]);
context.WriteCecilExpressions(nullableAttrExps);

var nullableContextAttributeCtor = context.RoslynTypeSystem.ForType<NullableContextAttribute>()
.GetMembers(".ctor")
.OfType<IMethodSymbol>()
.Single(ctor => ctor.Parameters.Length == 1)
.MethodResolverExpression(context);

var nullableContextAttrExps = CecilDefinitionsFactory.Attribute("nullableContext", definitionVar, context, nullableContextAttributeCtor, [(context.TypeResolver.Bcl.System.Int32, NonNullable)]);
context.WriteCecilExpressions(nullableContextAttrExps);
}

internal void AddSyntheticMembers()
{
_recordSymbol = context.SemanticModel.GetDeclaredSymbol(record).EnsureNotNull<ISymbol, ITypeSymbol>();
Expand Down Expand Up @@ -907,7 +933,7 @@ candidate is RecordDeclarationSyntax candidateBase
private void AddCompilerGeneratedAttributeTo(IVisitorContext context, string memberVariable)
{
var compilerGeneratedAttributeCtor = context.RoslynTypeSystem.SystemRuntimeCompilerServicesCompilerGeneratedAttribute.Ctor();
var exps = CecilDefinitionsFactory.Attribute(memberVariable, context, compilerGeneratedAttributeCtor.MethodResolverExpression(context));
var exps = CecilDefinitionsFactory.Attribute("compilerGenerated", memberVariable, context, compilerGeneratedAttributeCtor.MethodResolverExpression(context));
context.WriteNewLine();
context.WriteCecilExpressions(exps);
}
Expand All @@ -923,8 +949,8 @@ private static void RecordClassIsReadOnlyAttributeHandler(IVisitorContext contex

private void RecordStructIsReadOnlyAttributeHandler(string memberVar)
{
var compilerGeneratedAttributeCtor = context.RoslynTypeSystem.IsReadOnlyAttribute.Ctor();
var exps = CecilDefinitionsFactory.Attribute(memberVar, context, compilerGeneratedAttributeCtor.MethodResolverExpression(context));
var isReadOnlyAttributeCtor = context.RoslynTypeSystem.IsReadOnlyAttribute.Ctor();
var exps = CecilDefinitionsFactory.Attribute("isReadOnly", memberVar, context, isReadOnlyAttributeCtor.MethodResolverExpression(context));
context.WriteNewLine();
context.WriteCecilExpressions(exps);
}
Expand Down
6 changes: 3 additions & 3 deletions Cecilifier.Core/Misc/CecilDefinitionsFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -367,9 +367,9 @@ void ProcessAttributeNamedArguments(SymbolKind symbolKind, string container)
}
}

public static string[] Attribute(string memberVariable, IVisitorContext context, string resolvedCtor, params (string ResolvedType, string Value)[] parameters)
public static string[] Attribute(string attributeVarBaseName, string attributeTargetVar, IVisitorContext context, string resolvedCtor, params (string ResolvedType, string Value)[] parameters)
{
var attributeVar = context.Naming.SyntheticVariable("compilerGenerated", ElementKind.Attribute);
var attributeVar = context.Naming.SyntheticVariable(attributeVarBaseName, ElementKind.Attribute);

var exps = new string[2 + parameters.Length];
int expIndex = 0;
Expand All @@ -380,7 +380,7 @@ public static string[] Attribute(string memberVariable, IVisitorContext context,
var attributeArgument = $"new CustomAttributeArgument({parameters[i].ResolvedType}, {parameters[i].Value})";
exps[expIndex++] = $"{attributeVar}.ConstructorArguments.Add({attributeArgument});";
}
exps[expIndex] = $"{memberVariable}.CustomAttributes.Add({attributeVar});";
exps[expIndex] = $"{attributeTargetVar}.CustomAttributes.Add({attributeVar});";

return exps;
}
Expand Down
6 changes: 6 additions & 0 deletions Cecilifier.Core/TypeSystem/RoslynTypeSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ internal struct RoslynTypeSystem
{
public RoslynTypeSystem(IVisitorContext ctx)
{
_context = ctx;

SystemIndex = ctx.SemanticModel.Compilation.GetTypeByMetadataName(typeof(Index).FullName!);
SystemRange = ctx.SemanticModel.Compilation.GetTypeByMetadataName(typeof(Range).FullName!);
SystemType = ctx.SemanticModel.Compilation.GetTypeByMetadataName(typeof(Type).FullName!);
Expand Down Expand Up @@ -74,4 +76,8 @@ public RoslynTypeSystem(IVisitorContext ctx)
public ITypeSymbol SystemNullableOfT { get; }
public ITypeSymbol SystemRuntimeCompilerServicesUnsafe { get; }
public ITypeSymbol SystemRuntimeInteropServicesMemoryMarshal { get; }

public readonly ITypeSymbol ForType<TType>() => _context.SemanticModel.Compilation.GetTypeByMetadataName(typeof(TType).FullName!);

private readonly IVisitorContext _context;
}

0 comments on commit 7c7ecc5

Please sign in to comment.