Skip to content

Commit 08b0287

Browse files
committed
support nullable reference type
1 parent 213756c commit 08b0287

File tree

6 files changed

+63
-11
lines changed

6 files changed

+63
-11
lines changed

ExpressionDebugger.Tests/DebugInfoInjectorTest.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -733,8 +733,9 @@ public void TestProperties()
733733
translator.Properties.Add(new PropertyDefinitions
734734
{
735735
Name = "Prop2",
736-
Type = typeof(string),
737-
IsReadOnly = true
736+
Type = typeof(List<Dictionary<int, string[]>>),
737+
IsReadOnly = true,
738+
Nullable = new byte[] { 1, 2, 1, 2 }
738739
});
739740
translator.Properties.Add(new PropertyDefinitions
740741
{
@@ -744,15 +745,17 @@ public void TestProperties()
744745
});
745746
var str = translator.ToString();
746747
Assert.AreEqual(@"
748+
using System.Collections.Generic;
749+
747750
namespace ExpressionDebugger.Tests
748751
{
749752
public partial class MockClass
750753
{
751754
public string Prop1 { get; set; }
752-
public string Prop2 { get; }
755+
public List<Dictionary<int, string?[]>?> Prop2 { get; }
753756
public string Prop3 { get; init; }
754757
755-
public MockClass(string prop2)
758+
public MockClass(List<Dictionary<int, string?[]>?> prop2)
756759
{
757760
this.Prop2 = prop2;
758761
}

ExpressionDebugger.Tests/ExpressionDebugger.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFramework>netcoreapp2.0</TargetFramework>

ExpressionTranslator/ExpressionTranslator.cs

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System;
2+
using System.Collections;
23
using System.Collections.Generic;
4+
using System.Diagnostics;
35
using System.Globalization;
46
#if !NETSTANDARD1_3
57
using System.Dynamic;
@@ -399,7 +401,32 @@ protected override Expression VisitBinary(BinaryExpression node)
399401
return node.Update(left, node.Conversion, right);
400402
}
401403

404+
private byte? _nilCtx;
405+
private byte[]? _nil;
406+
private int _nilIndex;
407+
private string TranslateNullable(Type type, byte? nullableContext, byte[]? nullable)
408+
{
409+
try
410+
{
411+
_nilCtx = nullableContext;
412+
_nil = nullable;
413+
_nilIndex = 0;
414+
return Translate(type);
415+
}
416+
finally
417+
{
418+
_nilCtx = null;
419+
_nil = null;
420+
}
421+
}
402422
public string Translate(Type type)
423+
{
424+
var refNullable = !type.GetTypeInfo().IsValueType &&
425+
(_nilCtx == 2 || _nilIndex < _nil?.Length && _nil[_nilIndex++] == 2);
426+
var typeName = TranslateInner(type);
427+
return refNullable ? $"{typeName}?" : typeName;
428+
}
429+
private string TranslateInner(Type type)
403430
{
404431
if (type == typeof(bool))
405432
return "bool";
@@ -452,7 +479,15 @@ public string Translate(Type type)
452479
return Translate(underlyingType) + "?";
453480

454481
_usings ??= new HashSet<string>();
455-
if (!this.TypeNames.TryGetValue(type, out var name))
482+
483+
string name;
484+
if (_nilCtx != null || _nil != null)
485+
{
486+
name = GetTypeName(type);
487+
if (Definitions?.PrintFullTypeName != true && !string.IsNullOrEmpty(type.Namespace))
488+
_usings.Add(type.Namespace);
489+
}
490+
else if (!this.TypeNames.TryGetValue(type, out name))
456491
{
457492
name = GetTypeName(type);
458493

@@ -461,6 +496,8 @@ public string Translate(Type type)
461496
var count = this.TypeNames.Count(kvp => GetTypeName(kvp.Key) == name);
462497
if (count > 0)
463498
{
499+
// NOTE: type alias cannot solve all name conflicted case, user should use PrintFullTypeName
500+
// keep logic here for compatability
464501
if (!type.GetTypeInfo().IsGenericType)
465502
name += count + 1;
466503
else if (!string.IsNullOrEmpty(type.Namespace))
@@ -489,7 +526,7 @@ private string GetTypeName(Type type)
489526
if (type.DeclaringType == null)
490527
return name;
491528

492-
return Translate(type.DeclaringType) + "." + name;
529+
return TranslateInner(type.DeclaringType) + "." + name;
493530
}
494531

495532
private string GetSingleTypeName(Type type)
@@ -1841,9 +1878,8 @@ public override string ToString()
18411878
.ToList();
18421879
var properties = _properties?
18431880
.ToDictionary(it => it.Name,
1844-
it => $"{Translate(it.Type)} {it.Name} {{ get; {(it.IsReadOnly ? "" : it.IsInitOnly ? "init; " : "set; ")}}}");
1881+
it => $"{TranslateNullable(it.Type, it.NullableContext ?? Definitions?.NullableContext, it.Nullable)} {it.Name} {{ get; {(it.IsReadOnly ? "" : it.IsInitOnly ? "init; " : "set; ")}}}");
18451882
var ctorParams = _properties?.Where(it => it.IsReadOnly).ToList();
1846-
18471883
if (Definitions?.TypeName != null)
18481884
{
18491885
if (_usings != null)
@@ -1858,6 +1894,9 @@ public override string ToString()
18581894
}
18591895
WriteLine();
18601896
}
1897+
1898+
// NOTE: type alias cannot solve all name conflicted case, user should use PrintFullTypeName
1899+
// keep logic here for compatability
18611900
if (_typeNames != null)
18621901
{
18631902
var names = _typeNames
@@ -1871,6 +1910,7 @@ public override string ToString()
18711910
if (names.Count > 0)
18721911
WriteLine();
18731912
}
1913+
18741914
if (Definitions.Namespace != null)
18751915
{
18761916
WriteNextLine("namespace ", Definitions.Namespace);
@@ -1972,7 +2012,7 @@ private void WriteCtorParams(List<PropertyDefinitions> ctorParams)
19722012
var parameter = ctorParams[i];
19732013
if (i > 0)
19742014
Write(", ");
1975-
Write($"{Translate(parameter.Type)} {char.ToLower(parameter.Name[0]) + parameter.Name.Substring(1)}");
2015+
Write($"{TranslateNullable(parameter.Type, parameter.NullableContext ?? Definitions?.NullableContext, parameter.Nullable)} {char.ToLower(parameter.Name[0]) + parameter.Name.Substring(1)}");
19762016
}
19772017
Write(")");
19782018
}

ExpressionTranslator/ExpressionTranslator.csproj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<SignAssembly>True</SignAssembly>
1313
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
1414
<AssemblyOriginatorKeyFile>ExpressionTranslator.snk</AssemblyOriginatorKeyFile>
15-
<Version>2.3.4</Version>
15+
<Version>2.4.2</Version>
1616
<RootNamespace>ExpressionDebugger</RootNamespace>
1717
<PackageLicenseExpression>MIT</PackageLicenseExpression>
1818
<PackageIcon>icon.png</PackageIcon>
@@ -33,6 +33,10 @@
3333
<PackageReference Include="System.Reflection.TypeExtensions" Version="4.3.0" />
3434
</ItemGroup>
3535

36+
<ItemGroup>
37+
<None Remove="ExpressionTranslator.cs~RFe80e7f7.TMP" />
38+
</ItemGroup>
39+
3640
<ItemGroup>
3741
<None Include="icon.png" Pack="true" PackagePath="\" />
3842
</ItemGroup>

ExpressionTranslator/PropertyDefinitions.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System;
2+
using System.Collections.Generic;
3+
using System.Reflection;
24

35
namespace ExpressionDebugger
46
{
@@ -8,5 +10,7 @@ public class PropertyDefinitions
810
public string Name { get; set; }
911
public bool IsReadOnly { get; set; }
1012
public bool IsInitOnly { get; set; }
13+
public byte? NullableContext { get; set; }
14+
public byte[]? Nullable { get; set; }
1115
}
1216
}

ExpressionTranslator/TypeDefinitions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ public class TypeDefinitions
1212
public IEnumerable<Type>? Implements { get; set; }
1313
public bool PrintFullTypeName { get; set; }
1414
public bool IsRecordType { get; set; }
15+
public byte? NullableContext { get; set; }
1516
}
1617
}

0 commit comments

Comments
 (0)