Browse Source

Add test for C# 8 nullable reference types; and fix some bugs.

pull/1425/head
Daniel Grunwald 7 years ago
parent
commit
72508b5777
  1. 1
      ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs
  2. 5
      ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
  3. 1
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  4. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  5. 31
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs
  6. 20
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  7. 6
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  8. 2
      ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs
  9. 2
      ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs
  10. 12
      ICSharpCode.Decompiler/TypeSystem/ArrayType.cs

1
ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs

@ -39,6 +39,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
"System.Runtime.CompilerServices.IsReadOnlyAttribute", "System.Runtime.CompilerServices.IsReadOnlyAttribute",
"System.Runtime.CompilerServices.IsByRefLikeAttribute", "System.Runtime.CompilerServices.IsByRefLikeAttribute",
"System.Runtime.CompilerServices.IsUnmanagedAttribute", "System.Runtime.CompilerServices.IsUnmanagedAttribute",
"System.Runtime.CompilerServices.NullableAttribute",
"Microsoft.CodeAnalysis.EmbeddedAttribute", "Microsoft.CodeAnalysis.EmbeddedAttribute",
}; };

5
ICSharpCode.Decompiler.Tests/Helpers/Tester.cs

@ -245,7 +245,10 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
var preprocessorSymbols = GetPreprocessorSymbols(flags); var preprocessorSymbols = GetPreprocessorSymbols(flags);
if (flags.HasFlag(CompilerOptions.UseRoslyn)) { if (flags.HasFlag(CompilerOptions.UseRoslyn)) {
var parseOptions = new CSharpParseOptions(preprocessorSymbols: preprocessorSymbols.ToArray(), languageVersion: Microsoft.CodeAnalysis.CSharp.LanguageVersion.Latest); var parseOptions = new CSharpParseOptions(
preprocessorSymbols: preprocessorSymbols.ToArray(),
languageVersion: Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8
);
var syntaxTrees = sourceFileNames.Select(f => SyntaxFactory.ParseSyntaxTree(File.ReadAllText(f), parseOptions, path: f)); var syntaxTrees = sourceFileNames.Select(f => SyntaxFactory.ParseSyntaxTree(File.ReadAllText(f), parseOptions, path: f));
var references = defaultReferences.Value; var references = defaultReferences.Value;
if (flags.HasFlag(CompilerOptions.ReferenceVisualBasic)) { if (flags.HasFlag(CompilerOptions.ReferenceVisualBasic)) {

1
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -85,6 +85,7 @@
<Compile Include="TestCases\ILPretty\Issue1323.cs" /> <Compile Include="TestCases\ILPretty\Issue1323.cs" />
<Compile Include="TestCases\Pretty\CustomAttributes2.cs" /> <Compile Include="TestCases\Pretty\CustomAttributes2.cs" />
<Compile Include="TestCases\Pretty\EnumTests.cs" /> <Compile Include="TestCases\Pretty\EnumTests.cs" />
<None Include="TestCases\Pretty\NullableRefTypes.cs" />
<Compile Include="TestCases\Pretty\TypeMemberTests.cs" /> <Compile Include="TestCases\Pretty\TypeMemberTests.cs" />
<Compile Include="TestCases\Pretty\ValueTypes.cs" /> <Compile Include="TestCases\Pretty\ValueTypes.cs" />
<None Include="TestCases\ILPretty\Issue1389.il" /> <None Include="TestCases\ILPretty\Issue1389.il" />

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -289,6 +289,12 @@ namespace ICSharpCode.Decompiler.Tests
Run(cscOptions: cscOptions); Run(cscOptions: cscOptions);
} }
[Test]
public void NullableRefTypes([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test] [Test]
public void NullPropagation([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions) public void NullPropagation([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{ {

31
ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs

@ -0,0 +1,31 @@
#nullable enable
using System.Collections.Generic;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public class NullableRefTypes
{
private string field_string;
private string? field_nullable_string;
private Dictionary<string?, string> field_generic;
private (string, string?, string) field_tuple;
private string[]?[] field_array;
private Dictionary<(string, string?), (int, string[]?, string?[])> field_complex;
public int GetLength1(string[] arr)
{
return field_string.Length + arr.Length;
}
public int GetLength2(string[]? arr)
{
return field_nullable_string!.Length + arr!.Length;
}
public int? GetLength3(string[]? arr)
{
return field_nullable_string?.Length + arr?.Length;
}
}
}

20
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1777,11 +1777,7 @@ namespace ICSharpCode.Decompiler.CSharp
.WithRR(new ResolveResult(NullableType.GetUnderlyingType(translatedTarget.Type))) .WithRR(new ResolveResult(NullableType.GetUnderlyingType(translatedTarget.Type)))
.WithoutILInstruction(); .WithoutILInstruction();
} }
if (translatedTarget.Type.Nullability == Nullability.Nullable) { translatedTarget = EnsureTargetNotNullable(translatedTarget);
translatedTarget = new UnaryOperatorExpression(UnaryOperatorType.SuppressNullableWarning, translatedTarget)
.WithRR(new ResolveResult(translatedTarget.Type.ChangeNullability(Nullability.Oblivious)))
.WithoutILInstruction();
}
return translatedTarget; return translatedTarget;
} }
} else { } else {
@ -1791,6 +1787,19 @@ namespace ICSharpCode.Decompiler.CSharp
} }
} }
private TranslatedExpression EnsureTargetNotNullable(TranslatedExpression expr)
{
if (expr.Type.Nullability == Nullability.Nullable) {
if (expr.Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.NullConditional) {
return expr;
}
return new UnaryOperatorExpression(UnaryOperatorType.SuppressNullableWarning, expr)
.WithRR(new ResolveResult(expr.Type.ChangeNullability(Nullability.Oblivious)))
.WithoutILInstruction();
}
return expr;
}
protected internal override TranslatedExpression VisitLdObj(LdObj inst, TranslationContext context) protected internal override TranslatedExpression VisitLdObj(LdObj inst, TranslationContext context)
{ {
var target = Translate(inst.Target); var target = Translate(inst.Target);
@ -1875,6 +1884,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (arrayExpr.Type.Kind != TypeKind.Array) { if (arrayExpr.Type.Kind != TypeKind.Array) {
arrayExpr = arrayExpr.ConvertTo(compilation.FindType(KnownTypeCode.Array), this); arrayExpr = arrayExpr.ConvertTo(compilation.FindType(KnownTypeCode.Array), this);
} }
arrayExpr = EnsureTargetNotNullable(arrayExpr);
if (inst.ResultType == StackType.I4) { if (inst.ResultType == StackType.I4) {
return new MemberReferenceExpression(arrayExpr.Expression, "Length") return new MemberReferenceExpression(arrayExpr.Expression, "Length")
.WithILInstruction(inst) .WithILInstruction(inst)

6
ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

@ -243,7 +243,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
if (typeWithElementType is PointerType) { if (typeWithElementType is PointerType) {
return ConvertType(typeWithElementType.ElementType).MakePointerType(); return ConvertType(typeWithElementType.ElementType).MakePointerType();
} else if (typeWithElementType is ArrayType) { } else if (typeWithElementType is ArrayType) {
return ConvertType(typeWithElementType.ElementType).MakeArrayType(((ArrayType)type).Dimensions); var astType = ConvertType(typeWithElementType.ElementType).MakeArrayType(((ArrayType)type).Dimensions);
if (type.Nullability == Nullability.Nullable)
return astType.MakeNullableType();
else
return astType;
} else if (typeWithElementType is ByReferenceType) { } else if (typeWithElementType is ByReferenceType) {
return ConvertType(typeWithElementType.ElementType).MakeRefType(); return ConvertType(typeWithElementType.ElementType).MakeRefType();
} else { } else {

2
ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs

@ -210,6 +210,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// ensure the access chain does not contain any 'nullable.unwrap' that aren't directly part of the chain // ensure the access chain does not contain any 'nullable.unwrap' that aren't directly part of the chain
if (ArgumentsAfterFirstMayUnwrapNull(call.Arguments)) if (ArgumentsAfterFirstMayUnwrapNull(call.Arguments))
return false; return false;
} else if (inst is LdLen ldLen) {
inst = ldLen.Array;
} else if (inst is NullableUnwrap unwrap) { } else if (inst is NullableUnwrap unwrap) {
inst = unwrap.Argument; inst = unwrap.Argument;
} else if (inst is DynamicGetMemberInstruction dynGetMember) { } else if (inst is DynamicGetMemberInstruction dynGetMember) {

2
ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs

@ -186,11 +186,13 @@ namespace ICSharpCode.Decompiler.TypeSystem
int normalArgCount = Math.Min(type.TypeArguments.Count, TupleType.RestPosition - 1); int normalArgCount = Math.Min(type.TypeArguments.Count, TupleType.RestPosition - 1);
for (int i = 0; i < normalArgCount; i++) { for (int i = 0; i < normalArgCount; i++) {
dynamicTypeIndex++; dynamicTypeIndex++;
nullabilityTypeIndex++;
elementTypes.Add(type.TypeArguments[i].AcceptVisitor(this)); elementTypes.Add(type.TypeArguments[i].AcceptVisitor(this));
} }
if (type.TypeArguments.Count == TupleType.RestPosition) { if (type.TypeArguments.Count == TupleType.RestPosition) {
type = type.TypeArguments.Last() as ParameterizedType; type = type.TypeArguments.Last() as ParameterizedType;
dynamicTypeIndex++; dynamicTypeIndex++;
nullabilityTypeIndex++;
if (type != null && TupleType.IsTupleCompatible(type, out int nestedCardinality)) { if (type != null && TupleType.IsTupleCompatible(type, out int nestedCardinality)) {
tupleTypeIndex += nestedCardinality; tupleTypeIndex += nestedCardinality;
} else { } else {

12
ICSharpCode.Decompiler/TypeSystem/ArrayType.cs

@ -90,6 +90,18 @@ namespace ICSharpCode.Decompiler.TypeSystem
return a != null && elementType.Equals(a.elementType) && a.dimensions == dimensions && a.nullability == nullability; return a != null && elementType.Equals(a.elementType) && a.dimensions == dimensions && a.nullability == nullability;
} }
public override string ToString()
{
switch (nullability) {
case Nullability.Nullable:
return elementType.ToString() + NameSuffix + "?";
case Nullability.NotNullable:
return elementType.ToString() + NameSuffix + "!";
default:
return elementType.ToString() + NameSuffix;
}
}
public override IEnumerable<IType> DirectBaseTypes { public override IEnumerable<IType> DirectBaseTypes {
get { get {
List<IType> baseTypes = new List<IType>(); List<IType> baseTypes = new List<IType>();

Loading…
Cancel
Save