Browse Source

Support params keyword on non-array collections

pull/3444/head
ds5678 3 months ago
parent
commit
a1b9aa12e7
  1. 3
      ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
  2. 4
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  3. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  4. 21
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ParamsCollections.cs
  5. 3
      ICSharpCode.Decompiler/CSharp/CSharpLanguageVersion.cs
  6. 1
      ICSharpCode.Decompiler/CSharp/Transforms/EscapeInvalidIdentifiers.cs
  7. 24
      ICSharpCode.Decompiler/DecompilerSettings.cs
  8. 11
      ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
  9. 3
      ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs
  10. 2
      ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs
  11. 16
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs
  12. 1
      ILSpy/Languages/CSharpLanguage.cs
  13. 19
      ILSpy/Properties/Resources.Designer.cs
  14. 3
      ILSpy/Properties/Resources.resx
  15. 3
      ILSpy/Properties/Resources.zh-Hans.resx

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

@ -421,6 +421,7 @@ namespace System.Runtime.CompilerServices
preprocessorSymbols.Add("CS100"); preprocessorSymbols.Add("CS100");
preprocessorSymbols.Add("CS110"); preprocessorSymbols.Add("CS110");
preprocessorSymbols.Add("CS120"); preprocessorSymbols.Add("CS120");
preprocessorSymbols.Add("CS130");
} }
} }
else if ((flags & CompilerOptions.UseMcsMask) != 0) else if ((flags & CompilerOptions.UseMcsMask) != 0)
@ -639,7 +640,7 @@ namespace System.Runtime.CompilerServices
CompilerOptions.UseRoslyn1_3_2 => CSharp.LanguageVersion.CSharp6, CompilerOptions.UseRoslyn1_3_2 => CSharp.LanguageVersion.CSharp6,
CompilerOptions.UseRoslyn2_10_0 => CSharp.LanguageVersion.CSharp7_3, CompilerOptions.UseRoslyn2_10_0 => CSharp.LanguageVersion.CSharp7_3,
CompilerOptions.UseRoslyn3_11_0 => CSharp.LanguageVersion.CSharp9_0, CompilerOptions.UseRoslyn3_11_0 => CSharp.LanguageVersion.CSharp9_0,
_ => cscOptions.HasFlag(CompilerOptions.Preview) ? CSharp.LanguageVersion.Latest : CSharp.LanguageVersion.CSharp12_0, _ => cscOptions.HasFlag(CompilerOptions.Preview) ? CSharp.LanguageVersion.Latest : CSharp.LanguageVersion.CSharp13_0,
}; };
DecompilerSettings settings = new(langVersion) { DecompilerSettings settings = new(langVersion) {
// Never use file-scoped namespaces // Never use file-scoped namespaces

4
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -8,6 +8,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework> <TargetFramework>net8.0-windows</TargetFramework>
<LangVersion>13</LangVersion>
<RuntimeIdentifier Condition="$(IsWindowsX64) == true">win-x64</RuntimeIdentifier> <RuntimeIdentifier Condition="$(IsWindowsX64) == true">win-x64</RuntimeIdentifier>
<RuntimeIdentifier Condition="$(IsWindowsARM64) == true">win-arm64</RuntimeIdentifier> <RuntimeIdentifier Condition="$(IsWindowsARM64) == true">win-arm64</RuntimeIdentifier>
@ -17,7 +18,7 @@
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<NoWarn>1701;1702;1705,67,169,1058,728,1720,649,168,251,660,661,675;1998;162;8632;626;8618;8714;8602;8981</NoWarn> <NoWarn>1701;1702;1705,67,169,1058,728,1720,649,168,251,660,661,675;1998;162;8632;626;8618;8714;8602;8981</NoWarn>
<DefineConstants>ROSLYN;ROSLYN2;ROSLYN3;ROSLYN4;NET60;CS60;CS70;CS71;CS72;CS73;CS80;CS90;CS100;CS110;CS120</DefineConstants> <DefineConstants>ROSLYN;ROSLYN2;ROSLYN3;ROSLYN4;NET60;CS60;CS70;CS71;CS72;CS73;CS80;CS90;CS100;CS110;CS120;CS130</DefineConstants>
<GenerateAssemblyVersionAttribute>False</GenerateAssemblyVersionAttribute> <GenerateAssemblyVersionAttribute>False</GenerateAssemblyVersionAttribute>
<GenerateAssemblyFileVersionAttribute>False</GenerateAssemblyFileVersionAttribute> <GenerateAssemblyFileVersionAttribute>False</GenerateAssemblyFileVersionAttribute>
@ -141,6 +142,7 @@
<Compile Include="TestCases\Pretty\PointerArithmetic.cs" /> <Compile Include="TestCases\Pretty\PointerArithmetic.cs" />
<Compile Include="TestCases\Pretty\Issue3439.cs" /> <Compile Include="TestCases\Pretty\Issue3439.cs" />
<Compile Include="TestCases\Pretty\Issue3442.cs" /> <Compile Include="TestCases\Pretty\Issue3442.cs" />
<Compile Include="TestCases\Pretty\ParamsCollections.cs" />
<None Include="TestCases\VBPretty\VBAutomaticEvents.vb" /> <None Include="TestCases\VBPretty\VBAutomaticEvents.vb" />
<Compile Include="TestCases\VBPretty\VBAutomaticEvents.cs" /> <Compile Include="TestCases\VBPretty\VBAutomaticEvents.cs" />
<Compile Include="TestCases\VBPretty\VBNonGenericForEach.cs" /> <Compile Include="TestCases\VBPretty\VBNonGenericForEach.cs" />

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -598,6 +598,12 @@ namespace ICSharpCode.Decompiler.Tests
await RunForLibrary(cscOptions: cscOptions); await RunForLibrary(cscOptions: cscOptions);
} }
[Test]
public async Task ParamsCollections([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions)
{
await RunForLibrary(cscOptions: cscOptions);
}
[Test] [Test]
public async Task Issue1080([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions) public async Task Issue1080([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{ {

21
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ParamsCollections.cs

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public static class ParamsCollections
{
public static void ParamsEnumerable(params IEnumerable<int> values)
{
}
public static void ParamsList(params List<int> values)
{
}
public static void ParamsReadOnlySpan(params ReadOnlySpan<int> values)
{
}
public static void ParamsSpan(params Span<int> values)
{
}
}
}

3
ICSharpCode.Decompiler/CSharp/CSharpLanguageVersion.cs

@ -35,7 +35,8 @@ namespace ICSharpCode.Decompiler.CSharp
CSharp10_0 = 1000, CSharp10_0 = 1000,
CSharp11_0 = 1100, CSharp11_0 = 1100,
CSharp12_0 = 1200, CSharp12_0 = 1200,
Preview = 1100, CSharp13_0 = 1300,
Preview = 1300,
Latest = 0x7FFFFFFF Latest = 0x7FFFFFFF
} }
} }

1
ICSharpCode.Decompiler/CSharp/Transforms/EscapeInvalidIdentifiers.cs

@ -166,6 +166,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
"System.Runtime.CompilerServices.NullableAttribute", "System.Runtime.CompilerServices.NullableAttribute",
"System.Runtime.CompilerServices.NullableContextAttribute", "System.Runtime.CompilerServices.NullableContextAttribute",
"System.Runtime.CompilerServices.NativeIntegerAttribute", "System.Runtime.CompilerServices.NativeIntegerAttribute",
"System.Runtime.CompilerServices.ParamCollectionAttribute",
"System.Runtime.CompilerServices.RefSafetyRulesAttribute", "System.Runtime.CompilerServices.RefSafetyRulesAttribute",
"System.Runtime.CompilerServices.ScopedRefAttribute", "System.Runtime.CompilerServices.ScopedRefAttribute",
"System.Runtime.CompilerServices.RequiresLocationAttribute", "System.Runtime.CompilerServices.RequiresLocationAttribute",

24
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -165,10 +165,16 @@ namespace ICSharpCode.Decompiler
refReadOnlyParameters = false; refReadOnlyParameters = false;
usePrimaryConstructorSyntaxForNonRecordTypes = false; usePrimaryConstructorSyntaxForNonRecordTypes = false;
} }
if (languageVersion < CSharp.LanguageVersion.CSharp13_0)
{
paramsCollections = false;
}
} }
public CSharp.LanguageVersion GetMinimumRequiredVersion() public CSharp.LanguageVersion GetMinimumRequiredVersion()
{ {
if (paramsCollections)
return CSharp.LanguageVersion.CSharp13_0;
if (refReadOnlyParameters || usePrimaryConstructorSyntaxForNonRecordTypes) if (refReadOnlyParameters || usePrimaryConstructorSyntaxForNonRecordTypes)
return CSharp.LanguageVersion.CSharp12_0; return CSharp.LanguageVersion.CSharp12_0;
if (scopedRef || requiredMembers || numericIntPtr || utf8StringLiterals || unsignedRightShift || checkedOperators) if (scopedRef || requiredMembers || numericIntPtr || utf8StringLiterals || unsignedRightShift || checkedOperators)
@ -848,6 +854,24 @@ namespace ICSharpCode.Decompiler
} }
} }
bool paramsCollections = true;
/// <summary>
/// Support params collections.
/// </summary>
[Category("C# 13.0 / VS 2022.12")]
[Description("DecompilerSettings.DecompileParamsCollections")]
public bool ParamsCollections {
get { return paramsCollections; }
set {
if (paramsCollections != value)
{
paramsCollections = value;
OnPropertyChanged();
}
}
}
bool lockStatement = true; bool lockStatement = true;
/// <summary> /// <summary>

11
ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs

@ -138,12 +138,19 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary> /// </summary>
RefReadOnlyParameters = 0x10000, RefReadOnlyParameters = 0x10000,
/// <summary> /// <summary>
/// If this option is active, [ParamCollectionAttribute] on parameters is removed
/// and parameters are marked as params.
/// Otherwise, the attribute is preserved but the parameters are not marked
/// as if it was a normal parameter without any attributes.
/// </summary>
ParamsCollections = 0x20000,
/// <summary>
/// Default settings: typical options for the decompiler, with all C# languages features enabled. /// Default settings: typical options for the decompiler, with all C# languages features enabled.
/// </summary> /// </summary>
Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants | ReadOnlyStructsAndParameters Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants | ReadOnlyStructsAndParameters
| RefStructs | UnmanagedConstraints | NullabilityAnnotations | ReadOnlyMethods | RefStructs | UnmanagedConstraints | NullabilityAnnotations | ReadOnlyMethods
| NativeIntegers | FunctionPointers | ScopedRef | NativeIntegersWithoutAttribute | NativeIntegers | FunctionPointers | ScopedRef | NativeIntegersWithoutAttribute
| RefReadOnlyParameters | RefReadOnlyParameters | ParamsCollections
} }
/// <summary> /// <summary>
@ -185,6 +192,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
typeSystemOptions |= TypeSystemOptions.NativeIntegersWithoutAttribute; typeSystemOptions |= TypeSystemOptions.NativeIntegersWithoutAttribute;
if (settings.RefReadOnlyParameters) if (settings.RefReadOnlyParameters)
typeSystemOptions |= TypeSystemOptions.RefReadOnlyParameters; typeSystemOptions |= TypeSystemOptions.RefReadOnlyParameters;
if (settings.ParamsCollections)
typeSystemOptions |= TypeSystemOptions.ParamsCollections;
return typeSystemOptions; return typeSystemOptions;
} }

3
ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs

@ -258,6 +258,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
case "RequiresLocationAttribute": case "RequiresLocationAttribute":
return (options & TypeSystemOptions.RefReadOnlyParameters) != 0 return (options & TypeSystemOptions.RefReadOnlyParameters) != 0
&& (target == SymbolKind.Parameter); && (target == SymbolKind.Parameter);
case "ParamCollectionAttribute":
return (options & TypeSystemOptions.ParamsCollections) != 0
&& (target == SymbolKind.Parameter);
default: default:
return false; return false;
} }

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

@ -87,6 +87,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
// Parameter attributes: // Parameter attributes:
ParamArray, ParamArray,
ParamCollection,
In, In,
Out, Out,
Optional, Optional,
@ -166,6 +167,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
new TopLevelTypeName("System.Runtime.CompilerServices", nameof(IndexerNameAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(IndexerNameAttribute)),
// Parameter attributes: // Parameter attributes:
new TopLevelTypeName("System", nameof(ParamArrayAttribute)), new TopLevelTypeName("System", nameof(ParamArrayAttribute)),
new TopLevelTypeName("System.Runtime.CompilerServices", "ParamCollectionAttribute"),
new TopLevelTypeName("System.Runtime.InteropServices", nameof(InAttribute)), new TopLevelTypeName("System.Runtime.InteropServices", nameof(InAttribute)),
new TopLevelTypeName("System.Runtime.InteropServices", nameof(OutAttribute)), new TopLevelTypeName("System.Runtime.InteropServices", nameof(OutAttribute)),
new TopLevelTypeName("System.Runtime.InteropServices", nameof(OptionalAttribute)), new TopLevelTypeName("System.Runtime.InteropServices", nameof(OptionalAttribute)),

16
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs

@ -125,6 +125,12 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
var metadata = module.metadata; var metadata = module.metadata;
var parameterDef = metadata.GetParameter(handle); var parameterDef = metadata.GetParameter(handle);
if ((module.TypeSystemOptions & TypeSystemOptions.ParamsCollections) != 0
&& parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.ParamCollection))
{
// params collections are implicitly scoped
return default;
}
if (parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.ScopedRef)) if (parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.ScopedRef))
{ {
return new LifetimeAnnotation { ScopedRef = true }; return new LifetimeAnnotation { ScopedRef = true };
@ -135,12 +141,18 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public bool IsParams { public bool IsParams {
get { get {
if (Type.Kind != TypeKind.Array)
return false;
var metadata = module.metadata; var metadata = module.metadata;
var parameterDef = metadata.GetParameter(handle); var parameterDef = metadata.GetParameter(handle);
if (Type.Kind == TypeKind.Array)
{
return parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.ParamArray); return parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.ParamArray);
} }
if (module.TypeSystemOptions.HasFlag(TypeSystemOptions.ParamsCollections))
{
return parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.ParamCollection);
}
return false;
}
} }
public string Name { public string Name {

1
ILSpy/Languages/CSharpLanguage.cs

@ -116,6 +116,7 @@ namespace ICSharpCode.ILSpy
new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp10_0.ToString(), "C# 10.0 / VS 2022"), new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp10_0.ToString(), "C# 10.0 / VS 2022"),
new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp11_0.ToString(), "C# 11.0 / VS 2022.4"), new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp11_0.ToString(), "C# 11.0 / VS 2022.4"),
new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp12_0.ToString(), "C# 12.0 / VS 2022.8"), new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp12_0.ToString(), "C# 12.0 / VS 2022.8"),
new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp13_0.ToString(), "C# 13.0 / VS 2022.12"),
}; };
} }
return versions; return versions;

19
ILSpy/Properties/Resources.Designer.cs generated

@ -909,6 +909,15 @@ namespace ICSharpCode.ILSpy.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Decompile params collections.
/// </summary>
public static string DecompilerSettings_DecompileParamsCollections {
get {
return ResourceManager.GetString("DecompilerSettings.DecompileParamsCollections", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Decompile use of the &apos;dynamic&apos; type. /// Looks up a localized string similar to Decompile use of the &apos;dynamic&apos; type.
/// </summary> /// </summary>
@ -1127,16 +1136,6 @@ namespace ICSharpCode.ILSpy.Properties {
} }
} }
/// <summary>
/// Looks up a localized resource of type System.Object.
/// </summary>
public static object DecompilerSettings_LifetimeAnnotations {
get {
object obj = ResourceManager.GetObject("DecompilerSettings.LifetimeAnnotations", resourceCulture);
return ((object)(obj));
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Use nint/nuint types. /// Looks up a localized string similar to Use nint/nuint types.
/// </summary> /// </summary>

3
ILSpy/Properties/Resources.resx

@ -324,6 +324,9 @@ Are you sure you want to continue?</value>
<data name="DecompilerSettings.DecompileForEachWithGetEnumeratorExtension" xml:space="preserve"> <data name="DecompilerSettings.DecompileForEachWithGetEnumeratorExtension" xml:space="preserve">
<value>Decompile foreach statements with GetEnumerator extension methods</value> <value>Decompile foreach statements with GetEnumerator extension methods</value>
</data> </data>
<data name="DecompilerSettings.DecompileParamsCollections" xml:space="preserve">
<value>Decompile params collections</value>
</data>
<data name="DecompilerSettings.DecompileUseOfTheDynamicType" xml:space="preserve"> <data name="DecompilerSettings.DecompileUseOfTheDynamicType" xml:space="preserve">
<value>Decompile use of the 'dynamic' type</value> <value>Decompile use of the 'dynamic' type</value>
</data> </data>

3
ILSpy/Properties/Resources.zh-Hans.resx

@ -312,6 +312,9 @@
<data name="DecompilerSettings.DecompileForEachWithGetEnumeratorExtension" xml:space="preserve"> <data name="DecompilerSettings.DecompileForEachWithGetEnumeratorExtension" xml:space="preserve">
<value>反编译使用 GetEnumerator 扩展方法的 foreach 语句</value> <value>反编译使用 GetEnumerator 扩展方法的 foreach 语句</value>
</data> </data>
<data name="DecompilerSettings.DecompileParamsCollections" xml:space="preserve">
<value />
</data>
<data name="DecompilerSettings.DecompileUseOfTheDynamicType" xml:space="preserve"> <data name="DecompilerSettings.DecompileUseOfTheDynamicType" xml:space="preserve">
<value>反编译 dynamic 类型</value> <value>反编译 dynamic 类型</value>
</data> </data>

Loading…
Cancel
Save