Browse Source

Use "record" instead of "class" for C# 9 record class types.

pull/2251/head
Daniel Grunwald 5 years ago
parent
commit
f869756fed
  1. 4
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 3
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs
  3. 4
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  4. 8
      ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/TypeDeclaration.cs
  5. 2
      ICSharpCode.Decompiler/CSharp/Syntax/Roles.cs
  6. 9
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  7. 21
      ICSharpCode.Decompiler/DecompilerSettings.cs
  8. 5
      ICSharpCode.Decompiler/TypeSystem/ITypeDefinition.cs
  9. 36
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs
  10. 2
      ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs
  11. 2
      ILSpy/Languages/CSharpHighlightingTokenWriter.cs
  12. 9
      ILSpy/Properties/Resources.Designer.cs
  13. 3
      ILSpy/Properties/Resources.resx

4
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -18,14 +18,11 @@ @@ -18,14 +18,11 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;
using System.Threading;
using ICSharpCode.Decompiler.CSharp.OutputVisitor;
@ -415,6 +412,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -415,6 +412,7 @@ namespace ICSharpCode.Decompiler.CSharp
typeSystemAstBuilder.AddResolveResultAnnotations = true;
typeSystemAstBuilder.UseNullableSpecifierForValueTypes = settings.LiftNullables;
typeSystemAstBuilder.SupportInitAccessors = settings.InitAccessors;
typeSystemAstBuilder.SupportRecordClasses = settings.RecordClasses;
return typeSystemAstBuilder;
}

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

@ -80,6 +80,9 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -80,6 +80,9 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
case ClassType.Enum:
writer.WriteKeyword(Roles.EnumKeyword, "enum");
break;
case ClassType.RecordClass:
writer.WriteKeyword(Roles.RecordKeyword, "record");
break;
default:
throw new Exception("Invalid value for ClassType");
}

4
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs

@ -1480,6 +1480,10 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -1480,6 +1480,10 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
WriteKeyword(Roles.StructKeyword);
braceStyle = policy.StructBraceStyle;
break;
case ClassType.RecordClass:
WriteKeyword(Roles.RecordKeyword);
braceStyle = policy.ClassBraceStyle;
break;
default:
WriteKeyword(Roles.ClassKeyword);
braceStyle = policy.ClassBraceStyle;

8
ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/TypeDeclaration.cs

@ -33,7 +33,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -33,7 +33,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
Class,
Struct,
Interface,
Enum
Enum,
/// <summary>
/// C# 9 'record'
/// </summary>
RecordClass,
}
/// <summary>
@ -63,6 +67,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -63,6 +67,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return GetChildByRole(Roles.InterfaceKeyword);
case ClassType.Enum:
return GetChildByRole(Roles.EnumKeyword);
case ClassType.RecordClass:
return GetChildByRole(Roles.RecordKeyword);
default:
return CSharpTokenNode.Null;
}

2
ICSharpCode.Decompiler/CSharp/Syntax/Roles.cs

@ -87,7 +87,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -87,7 +87,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public static readonly TokenRole InterfaceKeyword = new TokenRole("interface");
public static readonly TokenRole StructKeyword = new TokenRole("struct");
public static readonly TokenRole ClassKeyword = new TokenRole("class");
public static readonly TokenRole RecordKeyword = new TokenRole("record");
}
}

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

@ -213,6 +213,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -213,6 +213,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// If disabled, emits "set /*init*/;" instead.
/// </summary>
public bool SupportInitAccessors { get; set; }
/// <summary>
/// Controls whether C# 9 "record" class types are supported.
/// </summary>
public bool SupportRecordClasses { get; set; }
#endregion
#region Convert Type
@ -1744,6 +1749,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1744,6 +1749,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
default:
classType = ClassType.Class;
if (SupportRecordClasses && typeDefinition.IsRecord)
{
classType = ClassType.RecordClass;
}
break;
}

21
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -135,12 +135,13 @@ namespace ICSharpCode.Decompiler @@ -135,12 +135,13 @@ namespace ICSharpCode.Decompiler
initAccessors = false;
functionPointers = false;
forEachWithGetEnumeratorExtension = false;
recordClasses = false;
}
}
public CSharp.LanguageVersion GetMinimumRequiredVersion()
{
if (nativeIntegers || initAccessors || functionPointers || forEachWithGetEnumeratorExtension)
if (nativeIntegers || initAccessors || functionPointers || forEachWithGetEnumeratorExtension || recordClasses)
return CSharp.LanguageVersion.Preview;
if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement
|| staticLocalFunctions || ranges || switchExpressions)
@ -206,6 +207,24 @@ namespace ICSharpCode.Decompiler @@ -206,6 +207,24 @@ namespace ICSharpCode.Decompiler
}
}
bool recordClasses = true;
/// <summary>
/// Use C# 9 <c>init;</c> property accessors.
/// </summary>
[Category("C# 9.0 (experimental)")]
[Description("DecompilerSettings.RecordClasses")]
public bool RecordClasses {
get { return recordClasses; }
set {
if (recordClasses != value)
{
recordClasses = value;
OnPropertyChanged();
}
}
}
bool functionPointers = true;
/// <summary>

5
ICSharpCode.Decompiler/TypeSystem/ITypeDefinition.cs

@ -73,5 +73,10 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -73,5 +73,10 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// This serves as default nullability for members of the type that do not have a [Nullable] attribute.
/// </summary>
Nullability NullableContext { get; }
/// <summary>
/// Gets whether the type has the necessary members to be considered a C# 9 record type.
/// </summary>
bool IsRecord { get; }
}
}

36
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs

@ -24,11 +24,8 @@ using System.Reflection; @@ -24,11 +24,8 @@ using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Semantics;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.TypeSystem.Implementation
@ -738,5 +735,38 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -738,5 +735,38 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return false;
}
#endregion
#region IsRecord
byte isRecord = ThreeState.Unknown;
public bool IsRecord {
get {
if (isRecord == ThreeState.Unknown)
{
isRecord = ThreeState.From(ComputeIsRecord());
}
return isRecord == ThreeState.True;
}
}
private bool ComputeIsRecord()
{
if (Kind != TypeKind.Class)
return false;
var metadata = module.metadata;
var typeDef = metadata.GetTypeDefinition(handle);
bool opEquality = false;
bool opInequality = false;
bool clone = false;
foreach (var methodHandle in typeDef.GetMethods())
{
var method = metadata.GetMethodDefinition(methodHandle);
opEquality |= metadata.StringComparer.Equals(method.Name, "op_Equality");
opInequality |= metadata.StringComparer.Equals(method.Name, "op_Inequality");
clone |= metadata.StringComparer.Equals(method.Name, "<Clone>$");
}
return opEquality & opInequality & clone;
}
#endregion
}
}

2
ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs

@ -302,6 +302,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -302,6 +302,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return EmptyList<IProperty>.Instance;
}
bool ITypeDefinition.IsRecord => false;
ITypeDefinition IType.GetDefinition() => this;
TypeParameterSubstitution IType.GetSubstitution() => TypeParameterSubstitution.Identity;

2
ILSpy/Languages/CSharpHighlightingTokenWriter.cs

@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
@ -228,6 +227,7 @@ namespace ICSharpCode.ILSpy @@ -228,6 +227,7 @@ namespace ICSharpCode.ILSpy
case "class":
case "interface":
case "delegate":
case "record":
color = referenceTypeKeywordsColor;
break;
case "select":

9
ILSpy/Properties/Resources.Designer.cs generated

@ -1064,6 +1064,15 @@ namespace ICSharpCode.ILSpy.Properties { @@ -1064,6 +1064,15 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Records.
/// </summary>
public static string DecompilerSettings_RecordClasses {
get {
return ResourceManager.GetString("DecompilerSettings.RecordClasses", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Remove dead and side effect free code (use with caution!).
/// </summary>

3
ILSpy/Properties/Resources.resx

@ -384,6 +384,9 @@ Are you sure you want to continue?</value> @@ -384,6 +384,9 @@ Are you sure you want to continue?</value>
<data name="DecompilerSettings.ReadOnlyMethods" xml:space="preserve">
<value>Read-only methods</value>
</data>
<data name="DecompilerSettings.RecordClasses" xml:space="preserve">
<value>Records</value>
</data>
<data name="DecompilerSettings.RemoveDeadAndSideEffectFreeCodeUseWithCaution" xml:space="preserve">
<value>Remove dead and side effect free code (use with caution!)</value>
</data>

Loading…
Cancel
Save