Browse Source

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

pull/1425/head
Daniel Grunwald 6 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. 22
      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. 16
      ICSharpCode.Decompiler/TypeSystem/ArrayType.cs

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

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

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

@ -245,7 +245,10 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -245,7 +245,10 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
var preprocessorSymbols = GetPreprocessorSymbols(flags);
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 references = defaultReferences.Value;
if (flags.HasFlag(CompilerOptions.ReferenceVisualBasic)) {

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

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

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

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

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

@ -0,0 +1,31 @@ @@ -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;
}
}
}

22
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1777,11 +1777,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1777,11 +1777,7 @@ namespace ICSharpCode.Decompiler.CSharp
.WithRR(new ResolveResult(NullableType.GetUnderlyingType(translatedTarget.Type)))
.WithoutILInstruction();
}
if (translatedTarget.Type.Nullability == Nullability.Nullable) {
translatedTarget = new UnaryOperatorExpression(UnaryOperatorType.SuppressNullableWarning, translatedTarget)
.WithRR(new ResolveResult(translatedTarget.Type.ChangeNullability(Nullability.Oblivious)))
.WithoutILInstruction();
}
translatedTarget = EnsureTargetNotNullable(translatedTarget);
return translatedTarget;
}
} else {
@ -1790,7 +1786,20 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1790,7 +1786,20 @@ namespace ICSharpCode.Decompiler.CSharp
.WithRR(new TypeResolveResult(memberDeclaringType));
}
}
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)
{
var target = Translate(inst.Target);
@ -1875,6 +1884,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1875,6 +1884,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (arrayExpr.Type.Kind != TypeKind.Array) {
arrayExpr = arrayExpr.ConvertTo(compilation.FindType(KnownTypeCode.Array), this);
}
arrayExpr = EnsureTargetNotNullable(arrayExpr);
if (inst.ResultType == StackType.I4) {
return new MemberReferenceExpression(arrayExpr.Expression, "Length")
.WithILInstruction(inst)

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

@ -243,7 +243,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -243,7 +243,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
if (typeWithElementType is PointerType) {
return ConvertType(typeWithElementType.ElementType).MakePointerType();
} 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) {
return ConvertType(typeWithElementType.ElementType).MakeRefType();
} else {

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

@ -210,6 +210,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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
if (ArgumentsAfterFirstMayUnwrapNull(call.Arguments))
return false;
} else if (inst is LdLen ldLen) {
inst = ldLen.Array;
} else if (inst is NullableUnwrap unwrap) {
inst = unwrap.Argument;
} else if (inst is DynamicGetMemberInstruction dynGetMember) {

2
ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs

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

16
ICSharpCode.Decompiler/TypeSystem/ArrayType.cs

@ -89,7 +89,19 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -89,7 +89,19 @@ namespace ICSharpCode.Decompiler.TypeSystem
ArrayType a = other as ArrayType;
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 {
get {
List<IType> baseTypes = new List<IType>();
@ -125,7 +137,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -125,7 +137,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
else
return compilation.FindType(KnownTypeCode.Array).GetMethods(typeArguments, filter, options);
}
public override IEnumerable<IMethod> GetAccessors(Predicate<IMethod> filter = null, GetMemberOptions options = GetMemberOptions.None)
{
if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers)

Loading…
Cancel
Save