Browse Source

Fix #1882: Provide a setting to desugar X? into Nullable<X> for value types

pull/1939/head
Siegfried Pammer 6 years ago
parent
commit
df84ab8f6b
  1. 19
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 1
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  3. 1
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs
  4. 55
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  5. 7
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs
  6. 5
      ICSharpCode.Decompiler/Output/IAmbience.cs
  7. 11
      ILSpy/Languages/CSharpLanguage.cs

19
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -401,12 +401,13 @@ namespace ICSharpCode.Decompiler.CSharp @@ -401,12 +401,13 @@ namespace ICSharpCode.Decompiler.CSharp
return new DecompilerTypeSystem(file, resolver);
}
static TypeSystemAstBuilder CreateAstBuilder(ITypeResolveContext decompilationContext)
static TypeSystemAstBuilder CreateAstBuilder(DecompilerSettings settings)
{
var typeSystemAstBuilder = new TypeSystemAstBuilder();
typeSystemAstBuilder.ShowAttributes = true;
typeSystemAstBuilder.AlwaysUseShortTypeNames = true;
typeSystemAstBuilder.AddResolveResultAnnotations = true;
typeSystemAstBuilder.UseNullableSpecifierForValueTypes = settings.LiftNullables;
return typeSystemAstBuilder;
}
@ -421,7 +422,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -421,7 +422,7 @@ namespace ICSharpCode.Decompiler.CSharp
void RunTransforms(AstNode rootNode, DecompileRun decompileRun, ITypeResolveContext decompilationContext)
{
var typeSystemAstBuilder = CreateAstBuilder(decompilationContext);
var typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings);
var context = new TransformContext(typeSystem, decompileRun, decompilationContext, typeSystemAstBuilder);
foreach (var transform in astTransforms) {
CancellationToken.ThrowIfCancellationRequested();
@ -466,13 +467,13 @@ namespace ICSharpCode.Decompiler.CSharp @@ -466,13 +467,13 @@ namespace ICSharpCode.Decompiler.CSharp
{
try {
foreach (var a in typeSystem.MainModule.GetAssemblyAttributes()) {
var astBuilder = CreateAstBuilder(decompilationContext);
var astBuilder = CreateAstBuilder(decompileRun.Settings);
var attrSection = new AttributeSection(astBuilder.ConvertAttribute(a));
attrSection.AttributeTarget = "assembly";
syntaxTree.AddChild(attrSection, SyntaxTree.MemberRole);
}
foreach (var a in typeSystem.MainModule.GetModuleAttributes()) {
var astBuilder = CreateAstBuilder(decompilationContext);
var astBuilder = CreateAstBuilder(decompileRun.Settings);
var attrSection = new AttributeSection(astBuilder.ConvertAttribute(a));
attrSection.AttributeTarget = "module";
syntaxTree.AddChild(attrSection, SyntaxTree.MemberRole);
@ -1076,7 +1077,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1076,7 +1077,7 @@ namespace ICSharpCode.Decompiler.CSharp
{
Debug.Assert(decompilationContext.CurrentTypeDefinition == typeDef);
try {
var typeSystemAstBuilder = CreateAstBuilder(decompilationContext);
var typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings);
var entityDecl = typeSystemAstBuilder.ConvertEntity(typeDef);
var typeDecl = entityDecl as TypeDeclaration;
if (typeDecl == null) {
@ -1239,7 +1240,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1239,7 +1240,7 @@ namespace ICSharpCode.Decompiler.CSharp
EntityDeclaration DoDecompile(IMethod method, DecompileRun decompileRun, ITypeResolveContext decompilationContext)
{
Debug.Assert(decompilationContext.CurrentMember == method);
var typeSystemAstBuilder = CreateAstBuilder(decompilationContext);
var typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings);
var methodDecl = typeSystemAstBuilder.ConvertEntity(method);
int lastDot = method.Name.LastIndexOf('.');
if (method.IsExplicitInterfaceImplementation && lastDot >= 0) {
@ -1412,7 +1413,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1412,7 +1413,7 @@ namespace ICSharpCode.Decompiler.CSharp
{
Debug.Assert(decompilationContext.CurrentMember == field);
try {
var typeSystemAstBuilder = CreateAstBuilder(decompilationContext);
var typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings);
if (decompilationContext.CurrentTypeDefinition.Kind == TypeKind.Enum && field.IsConst) {
var enumDec = new EnumMemberDeclaration { Name = field.Name };
object constantValue = field.GetConstantValue();
@ -1483,7 +1484,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1483,7 +1484,7 @@ namespace ICSharpCode.Decompiler.CSharp
{
Debug.Assert(decompilationContext.CurrentMember == property);
try {
var typeSystemAstBuilder = CreateAstBuilder(decompilationContext);
var typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings);
EntityDeclaration propertyDecl = typeSystemAstBuilder.ConvertEntity(property);
if (property.IsExplicitInterfaceImplementation && !property.IsIndexer) {
int lastDot = property.Name.LastIndexOf('.');
@ -1518,7 +1519,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1518,7 +1519,7 @@ namespace ICSharpCode.Decompiler.CSharp
{
Debug.Assert(decompilationContext.CurrentMember == ev);
try {
var typeSystemAstBuilder = CreateAstBuilder(decompilationContext);
var typeSystemAstBuilder = CreateAstBuilder(decompileRun.Settings);
typeSystemAstBuilder.UseCustomEvents = ev.DeclaringTypeDefinition.Kind != TypeKind.Interface;
var eventDecl = typeSystemAstBuilder.ConvertEntity(ev);
int lastDot = ev.Name.LastIndexOf('.');

1
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -92,6 +92,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -92,6 +92,7 @@ namespace ICSharpCode.Decompiler.CSharp
this.astBuilder = new TypeSystemAstBuilder(resolver);
this.astBuilder.AlwaysUseShortTypeNames = true;
this.astBuilder.AddResolveResultAnnotations = true;
this.astBuilder.UseNullableSpecifierForValueTypes = settings.LiftNullables;
this.typeInference = new TypeInference(compilation) { Algorithm = TypeInferenceAlgorithm.Improved };
}

1
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs

@ -195,6 +195,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -195,6 +195,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
astBuilder.ShowAccessibility = (ConversionFlags & ConversionFlags.ShowAccessibility) == ConversionFlags.ShowAccessibility;
astBuilder.AlwaysUseShortTypeNames = (ConversionFlags & ConversionFlags.UseFullyQualifiedTypeNames) != ConversionFlags.UseFullyQualifiedTypeNames;
astBuilder.ShowParameterNames = (ConversionFlags & ConversionFlags.ShowParameterNames) == ConversionFlags.ShowParameterNames;
astBuilder.UseNullableSpecifierForValueTypes = (ConversionFlags & ConversionFlags.UseNullableSpecifierForValueTypes) != 0;
return astBuilder;
}

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

@ -64,7 +64,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -64,7 +64,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
#region Properties
void InitProperties()
{
this.AlwaysUseBuiltinTypeNames = true;
this.UseKeywordsForBuiltinTypes = true;
this.UseNullableSpecifierForValueTypes = true;
this.ShowAccessibility = true;
this.ShowModifiers = true;
this.ShowBaseTypes = true;
@ -78,82 +79,88 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -78,82 +79,88 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// <summary>
/// Specifies whether the ast builder should add annotations to type references.
/// The default value is <c>false</c>.
/// The default value is <see langword="false" />.
/// </summary>
public bool AddTypeReferenceAnnotations { get; set; }
/// <summary>
/// Specifies whether the ast builder should add ResolveResult annotations to AST nodes.
/// The default value is <c>false</c>.
/// The default value is <see langword="false" />.
/// </summary>
public bool AddResolveResultAnnotations { get; set; }
/// <summary>
/// Controls the accessibility modifiers are shown.
/// The default value is <c>true</c>.
/// The default value is <see langword="true" />.
/// </summary>
public bool ShowAccessibility { get; set; }
/// <summary>
/// Controls the non-accessibility modifiers are shown.
/// The default value is <c>true</c>.
/// The default value is <see langword="true" />.
/// </summary>
public bool ShowModifiers { get; set; }
/// <summary>
/// Controls whether base type references are shown.
/// The default value is <c>true</c>.
/// The default value is <see langword="true" />.
/// </summary>
public bool ShowBaseTypes { get; set; }
/// <summary>
/// Controls whether type parameter declarations are shown.
/// The default value is <c>true</c>.
/// The default value is <see langword="true" />.
/// </summary>
public bool ShowTypeParameters { get; set; }
/// <summary>
/// Controls whether type parameter names are shown for unbound types.
/// The default value is <c>false</c>.
/// The default value is <see langword="false" />.
/// </summary>
public bool ShowTypeParametersForUnboundTypes { get; set; }
/// <summary>
/// Controls whether constraints on type parameter declarations are shown.
/// Has no effect if ShowTypeParameters is false.
/// The default value is <c>true</c>.
/// The default value is <see langword="true" />.
/// </summary>
public bool ShowTypeParameterConstraints { get; set; }
/// <summary>
/// Controls whether the names of parameters are shown.
/// The default value is <c>true</c>.
/// The default value is <see langword="true" />.
/// </summary>
public bool ShowParameterNames { get; set; }
/// <summary>
/// Controls whether to show default values of optional parameters, and the values of constant fields.
/// The default value is <c>true</c>.
/// The default value is <see langword="true" />.
/// </summary>
public bool ShowConstantValues { get; set; }
/// <summary>
/// Controls whether to show attributes.
/// The default value is <c>false</c>.
/// The default value is <see langword="false" />.
/// </summary>
public bool ShowAttributes { get; set; }
/// <summary>
/// Controls whether to use fully-qualified type names or short type names.
/// The default value is <c>false</c>.
/// The default value is <see langword="false" />.
/// </summary>
public bool AlwaysUseShortTypeNames { get; set; }
/// <summary>
/// Controls whether to use fully-qualified type names or short type names.
/// The default value is <c>true</c>.
/// Controls whether to use keywords for builtin types.
/// The default value is <see langword="true" />.
/// </summary>
public bool UseKeywordsForBuiltinTypes { get; set; }
/// <summary>
/// Controls whether to use <c>T?</c> or <c>Nullable&lt;T&gt;</c> for nullable value types.
/// The default value is <see langword="true" />.
/// </summary>
public bool AlwaysUseBuiltinTypeNames { get; set; }
public bool UseNullableSpecifierForValueTypes { get; set; }
/// <summary>
/// Determines the name lookup mode for converting a type name.
@ -165,37 +172,37 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -165,37 +172,37 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// <summary>
/// Controls whether to generate a body that throws a <c>System.NotImplementedException</c>.
/// The default value is <c>false</c>.
/// The default value is <see langword="false" />.
/// </summary>
public bool GenerateBody { get; set; }
/// <summary>
/// Controls whether to generate custom events.
/// The default value is <c>false</c>.
/// The default value is <see langword="false" />.
/// </summary>
public bool UseCustomEvents { get; set; }
/// <summary>
/// Controls whether unbound type argument names are inserted in the ast or not.
/// The default value is <c>false</c>.
/// The default value is <see langword="false" />.
/// </summary>
public bool ConvertUnboundTypeArguments { get; set;}
/// <summary>
/// Controls whether aliases should be used inside the type name or not.
/// The default value is <c>true</c>.
/// The default value is <see langword="true" />.
/// </summary>
public bool UseAliases { get; set; }
/// <summary>
/// Controls whether constants like <c>int.MaxValue</c> are converted to a <see cref="MemberReferenceExpression"/> or <see cref="PrimitiveExpression" />.
/// The default value is <c>true</c>.
/// The default value is <see langword="true" />.
/// </summary>
public bool UseSpecialConstants { get; set; }
/// <summary>
/// Controls whether integral constants should be printed in hexadecimal format.
/// The default value is <c>false</c>.
/// The default value is <see langword="false" />.
/// </summary>
public bool PrintIntegralValuesAsHex { get; set; }
#endregion
@ -290,7 +297,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -290,7 +297,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
break;
case ParameterizedType pt:
if (AlwaysUseBuiltinTypeNames && pt.IsKnownType(KnownTypeCode.NullableOfT)) {
if (UseNullableSpecifierForValueTypes && pt.IsKnownType(KnownTypeCode.NullableOfT)) {
return ConvertType(pt.TypeArguments[0]).MakeNullableType();
}
astType = ConvertTypeHelper(pt.GenericType, pt.TypeArguments);
@ -313,7 +320,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -313,7 +320,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
Debug.Assert(typeDef != null || genericType.Kind == TypeKind.Unknown);
Debug.Assert(typeArguments.Count >= genericType.TypeParameterCount);
if (AlwaysUseBuiltinTypeNames && typeDef != null) {
if (UseKeywordsForBuiltinTypes && typeDef != null) {
string keyword = KnownTypeReference.GetCSharpNameByTypeCode(typeDef.KnownTypeCode);
if (keyword != null) {
return new PrimitiveType(keyword);

7
ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUsingDeclarations.cs

@ -116,12 +116,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -116,12 +116,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
{
readonly Stack<CSharpTypeResolveContext> context;
readonly bool ignoreUsingScope;
readonly DecompilerSettings settings;
TypeSystemAstBuilder astBuilder;
public FullyQualifyAmbiguousTypeNamesVisitor(TransformContext context, UsingScope usingScope)
{
this.ignoreUsingScope = !context.Settings.UsingDeclarations;
this.settings = context.Settings;
CSharpTypeResolveContext currentContext;
if (ignoreUsingScope) {
@ -139,7 +141,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -139,7 +141,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
this.astBuilder = CreateAstBuilder(currentContext);
}
static TypeSystemAstBuilder CreateAstBuilder(CSharpTypeResolveContext context, IL.ILFunction function = null)
TypeSystemAstBuilder CreateAstBuilder(CSharpTypeResolveContext context, IL.ILFunction function = null)
{
CSharpResolver resolver = new CSharpResolver(context);
if (function != null) {
@ -148,8 +150,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -148,8 +150,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
resolver = resolver.AddVariable(new DefaultVariable(v.Type, v.Name));
}
}
return new TypeSystemAstBuilder(resolver) {
UseNullableSpecifierForValueTypes = settings.LiftNullables,
AddResolveResultAnnotations = true,
UseAliases = true
};

5
ICSharpCode.Decompiler/Output/IAmbience.cs

@ -92,12 +92,17 @@ namespace ICSharpCode.Decompiler.Output @@ -92,12 +92,17 @@ namespace ICSharpCode.Decompiler.Output
/// Show default values of parameters.
/// </summary>
ShowParameterDefaultValues = 0x4000,
/// <summary>
/// Use <c>T?</c> instead of <c>Nullable&lt;T&gt;</c>.
/// </summary>
UseNullableSpecifierForValueTypes = 0x8000,
StandardConversionFlags = ShowParameterNames |
ShowAccessibility |
ShowParameterList |
ShowParameterModifiers |
ShowParameterDefaultValues |
UseNullableSpecifierForValueTypes |
ShowReturnType |
ShowModifiers |
ShowTypeParameterList |

11
ILSpy/Languages/CSharpLanguage.cs

@ -457,6 +457,9 @@ namespace ICSharpCode.ILSpy @@ -457,6 +457,9 @@ namespace ICSharpCode.ILSpy
CSharpAmbience ambience = new CSharpAmbience();
// Do not forget to update CSharpAmbienceTests.ILSpyMainTreeViewTypeFlags, if this ever changes.
ambience.ConversionFlags = ConversionFlags.ShowTypeParameterList | ConversionFlags.PlaceReturnTypeAfterParameterList;
if (new DecompilationOptions().DecompilerSettings.LiftNullables) {
ambience.ConversionFlags |= ConversionFlags.UseNullableSpecifierForValueTypes;
}
return ambience;
}
@ -635,7 +638,13 @@ namespace ICSharpCode.ILSpy @@ -635,7 +638,13 @@ namespace ICSharpCode.ILSpy
var output = new StringWriter();
var decoratedWriter = new TextWriterTokenWriter(output);
var writer = new CSharpHighlightingTokenWriter(TokenWriter.InsertRequiredSpaces(decoratedWriter), locatable: decoratedWriter);
new CSharpAmbience() { ConversionFlags = flags }.ConvertSymbol(entity, writer, new DecompilerSettings().CSharpFormattingOptions);
var settings = new DecompilationOptions().DecompilerSettings;
if (!settings.LiftNullables) {
flags &= ~ConversionFlags.UseNullableSpecifierForValueTypes;
}
new CSharpAmbience() {
ConversionFlags = flags,
}.ConvertSymbol(entity, writer, settings.CSharpFormattingOptions);
return new RichText(output.ToString(), writer.HighlightingModel);
}

Loading…
Cancel
Save