Browse Source

Generate file-scoped namespace declarations, if possible.

pull/2560/head
Siegfried Pammer 4 years ago
parent
commit
d8bb40b45b
  1. 7
      ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
  2. 1
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  3. 17
      ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
  4. 20
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  5. 25
      ICSharpCode.Decompiler.Tests/TestCases/PdbGen/ForLoopTests.xml
  6. 15
      ICSharpCode.Decompiler.Tests/TestCases/PdbGen/HelloWorld.xml
  7. 23
      ICSharpCode.Decompiler.Tests/TestCases/PdbGen/LambdaCapturing.xml
  8. 9
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/FileScopedNamespaces.cs
  9. 12
      ICSharpCode.Decompiler.Tests/UglyTestRunner.cs
  10. 19
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  11. 2
      ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/NamespaceDeclaration.cs
  12. 25
      ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs
  13. 24
      ICSharpCode.Decompiler/DecompilerSettings.cs
  14. 3
      ILSpy/Options/DecompilerSettingsPanel.xaml.cs
  15. 9
      ILSpy/Properties/Resources.Designer.cs
  16. 3
      ILSpy/Properties/Resources.resx

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

@ -549,9 +549,12 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -549,9 +549,12 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
CompilerOptions.UseRoslyn1_3_2 => CSharp.LanguageVersion.CSharp6,
CompilerOptions.UseRoslyn2_10_0 => CSharp.LanguageVersion.CSharp7_3,
CompilerOptions.UseRoslyn3_11_0 => CSharp.LanguageVersion.CSharp9_0,
_ => cscOptions.HasFlag(CompilerOptions.Preview) ? CSharp.LanguageVersion.Latest : CSharp.LanguageVersion.CSharp9_0,
_ => cscOptions.HasFlag(CompilerOptions.Preview) ? CSharp.LanguageVersion.Latest : CSharp.LanguageVersion.CSharp10_0,
};
return new DecompilerSettings(langVersion) {
// Never use file-scoped namespaces
FileScopedNamespaces = false
};
return new DecompilerSettings(langVersion);
}
else
{

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

@ -108,6 +108,7 @@ @@ -108,6 +108,7 @@
<Compile Include="TestCases\Correctness\DeconstructionTests.cs" />
<Compile Include="TestCases\Correctness\DynamicTests.cs" />
<Compile Include="TestCases\Correctness\StringConcat.cs" />
<None Include="TestCases\Pretty\FileScopedNamespaces.cs" />
<Compile Include="TestCases\Pretty\PatternMatching.cs" />
<None Include="TestCases\Ugly\NoPropertiesAndEvents.Expected.cs" />
<Compile Include="TestCases\Ugly\NoPropertiesAndEvents.cs" />

17
ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs

@ -113,13 +113,13 @@ namespace ICSharpCode.Decompiler.Tests @@ -113,13 +113,13 @@ namespace ICSharpCode.Decompiler.Tests
[Test]
public void FSharpUsing_Debug()
{
Run(settings: new DecompilerSettings { RemoveDeadStores = true, UseEnhancedUsing = false });
Run(settings: new DecompilerSettings { RemoveDeadStores = true, UseEnhancedUsing = false, FileScopedNamespaces = false });
}
[Test]
public void FSharpUsing_Release()
{
Run(settings: new DecompilerSettings { RemoveDeadStores = true, UseEnhancedUsing = false });
Run(settings: new DecompilerSettings { RemoveDeadStores = true, UseEnhancedUsing = false, FileScopedNamespaces = false });
}
[Test]
@ -137,13 +137,13 @@ namespace ICSharpCode.Decompiler.Tests @@ -137,13 +137,13 @@ namespace ICSharpCode.Decompiler.Tests
[Test]
public void CS1xSwitch_Debug()
{
Run(settings: new DecompilerSettings { SwitchExpressions = false });
Run(settings: new DecompilerSettings { SwitchExpressions = false, FileScopedNamespaces = false });
}
[Test]
public void CS1xSwitch_Release()
{
Run(settings: new DecompilerSettings { SwitchExpressions = false });
Run(settings: new DecompilerSettings { SwitchExpressions = false, FileScopedNamespaces = false });
}
[Test]
@ -240,14 +240,14 @@ namespace ICSharpCode.Decompiler.Tests @@ -240,14 +240,14 @@ namespace ICSharpCode.Decompiler.Tests
public void FSharpLoops_Debug()
{
CopyFSharpCoreDll();
Run(settings: new DecompilerSettings { RemoveDeadStores = true });
Run(settings: new DecompilerSettings { RemoveDeadStores = true, FileScopedNamespaces = false });
}
[Test]
public void FSharpLoops_Release()
{
CopyFSharpCoreDll();
Run(settings: new DecompilerSettings { RemoveDeadStores = true });
Run(settings: new DecompilerSettings { RemoveDeadStores = true, FileScopedNamespaces = false });
}
[Test]
@ -259,6 +259,11 @@ namespace ICSharpCode.Decompiler.Tests @@ -259,6 +259,11 @@ namespace ICSharpCode.Decompiler.Tests
void Run([CallerMemberName] string testName = null, DecompilerSettings settings = null,
AssemblerOptions assemblerOptions = AssemblerOptions.Library)
{
if (settings == null)
{
// never use file-scoped namespaces, unless explicitly specified
settings = new DecompilerSettings { FileScopedNamespaces = false };
}
var ilFile = Path.Combine(TestCasePath, testName + ".il");
var csFile = Path.Combine(TestCasePath, testName + ".cs");

20
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -180,7 +180,8 @@ namespace ICSharpCode.Decompiler.Tests @@ -180,7 +180,8 @@ namespace ICSharpCode.Decompiler.Tests
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings {
NullPropagation = false,
// legacy csc generates a dead store in debug builds
RemoveDeadStores = (cscOptions == CompilerOptions.None)
RemoveDeadStores = (cscOptions == CompilerOptions.None),
FileScopedNamespaces = false,
});
}
@ -191,6 +192,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -191,6 +192,7 @@ namespace ICSharpCode.Decompiler.Tests
// legacy csc generates a dead store in debug builds
RemoveDeadStores = (cscOptions == CompilerOptions.None),
SwitchExpressions = false,
FileScopedNamespaces = false,
});
}
@ -235,7 +237,10 @@ namespace ICSharpCode.Decompiler.Tests @@ -235,7 +237,10 @@ namespace ICSharpCode.Decompiler.Tests
{
RunForLibrary(
cscOptions: cscOptions,
decompilerSettings: new DecompilerSettings { UseEnhancedUsing = false }
decompilerSettings: new DecompilerSettings {
UseEnhancedUsing = false,
FileScopedNamespaces = false,
}
);
}
@ -263,7 +268,8 @@ namespace ICSharpCode.Decompiler.Tests @@ -263,7 +268,8 @@ namespace ICSharpCode.Decompiler.Tests
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings {
// legacy csc generates a dead store in debug builds
RemoveDeadStores = (cscOptions == CompilerOptions.None),
UseExpressionBodyForCalculatedGetterOnlyProperties = false
UseExpressionBodyForCalculatedGetterOnlyProperties = false,
FileScopedNamespaces = false,
});
}
@ -407,7 +413,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -407,7 +413,7 @@ namespace ICSharpCode.Decompiler.Tests
{
RunForLibrary(
cscOptions: cscOptions,
decompilerSettings: new DecompilerSettings { UseEnhancedUsing = false }
decompilerSettings: new DecompilerSettings { UseEnhancedUsing = false, FileScopedNamespaces = false }
);
}
@ -429,6 +435,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -429,6 +435,12 @@ namespace ICSharpCode.Decompiler.Tests
RunForLibrary(cscOptions: cscOptions | CompilerOptions.Preview);
}
[Test]
public void FileScopedNamespaces([ValueSource(nameof(roslynLatestOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings());
}
[Test]
public void FunctionPointers([ValueSource(nameof(roslynLatestOnlyOptions))] CompilerOptions cscOptions)
{

25
ICSharpCode.Decompiler.Tests/TestCases/PdbGen/ForLoopTests.xml

@ -3,26 +3,25 @@ @@ -3,26 +3,25 @@
<files>
<file id="1" name="ICSharpCode\Decompiler\Tests\TestCases\PdbGen\ForLoopTests.cs" language="C#" checksumAlgorithm="SHA256"><![CDATA[using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.PdbGen
namespace ICSharpCode.Decompiler.Tests.TestCases.PdbGen;
public class ForLoopTests
{
public class ForLoopTests
public static void SimplePrintLoop(string[] args)
{
public static void SimplePrintLoop(string[] args)
for (int i = 0; i < args.Length; i++)
{
for (int i = 0; i < args.Length; i++)
{
Console.WriteLine(args[i]);
}
Console.WriteLine(args[i]);
}
}
public static void SimplePrintLoopWithCondition(string[] args)
public static void SimplePrintLoopWithCondition(string[] args)
{
for (int i = 0; i < args.Length; i++)
{
for (int i = 0; i < args.Length; i++)
if (i % 2 != 0)
{
if (i % 2 != 0)
{
Console.WriteLine(args[i]);
}
Console.WriteLine(args[i]);
}
}
}

15
ICSharpCode.Decompiler.Tests/TestCases/PdbGen/HelloWorld.xml

@ -3,16 +3,15 @@ @@ -3,16 +3,15 @@
<files>
<file id="1" name="ICSharpCode\Decompiler\Tests\TestCases\PdbGen\HelloWorld.cs" language="C#" checksumAlgorithm="SHA256"><![CDATA[using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.PdbGen
namespace ICSharpCode.Decompiler.Tests.TestCases.PdbGen;
public class HelloWorld
{
public class HelloWorld
public static void Main(string[] args)
{
public static void Main(string[] args)
{
Console.ReadKey();
Console.WriteLine("Hello World!");
Console.ReadKey();
}
Console.ReadKey();
Console.WriteLine("Hello World!");
Console.ReadKey();
}
}
]]></file>

23
ICSharpCode.Decompiler.Tests/TestCases/PdbGen/LambdaCapturing.xml

@ -3,21 +3,20 @@ @@ -3,21 +3,20 @@
<files>
<file id="1" name="ICSharpCode\Decompiler\Tests\TestCases\PdbGen\LambdaCapturing.cs" language="C#" checksumAlgorithm="SHA256"><![CDATA[using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.PdbGen
namespace ICSharpCode.Decompiler.Tests.TestCases.PdbGen;
public class LambdaCapturing
{
public class LambdaCapturing
public static void Main(string[] args)
{
public static void Main(string[] args)
{
int num = 4;
int captured = Environment.TickCount + num;
Test((int a, int b) => a + b + captured);
}
int num = 4;
int captured = Environment.TickCount + num;
Test((int a, int b) => a + b + captured);
}
private static void Test(Func<int, int, int> p)
{
p(1, 2);
}
private static void Test(Func<int, int, int> p)
{
p(1, 2);
}
}
]]></file>

9
ICSharpCode.Decompiler.Tests/TestCases/Pretty/FileScopedNamespaces.cs

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty;
internal delegate void DelegateInFileScopedNamespace();
internal class FileScopedNamespaces
{
}

12
ICSharpCode.Decompiler.Tests/UglyTestRunner.cs

@ -74,7 +74,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -74,7 +74,7 @@ namespace ICSharpCode.Decompiler.Tests
[Test]
public void NoArrayInitializers([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings {
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings(CSharp.LanguageVersion.CSharp1) {
ArrayInitializers = false
});
}
@ -82,7 +82,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -82,7 +82,7 @@ namespace ICSharpCode.Decompiler.Tests
[Test]
public void NoDecimalConstants([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings {
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings(CSharp.LanguageVersion.CSharp1) {
DecimalConstants = false
});
}
@ -90,7 +90,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -90,7 +90,7 @@ namespace ICSharpCode.Decompiler.Tests
[Test]
public void NoExtensionMethods([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings {
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings(CSharp.LanguageVersion.CSharp9_0) {
ExtensionMethods = false
});
}
@ -98,7 +98,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -98,7 +98,7 @@ namespace ICSharpCode.Decompiler.Tests
[Test]
public void NoForEachStatement([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings {
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings(CSharp.LanguageVersion.CSharp1) {
ForEachStatement = false,
UseEnhancedUsing = false,
});
@ -113,7 +113,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -113,7 +113,7 @@ namespace ICSharpCode.Decompiler.Tests
[Test]
public void NoPropertiesAndEvents([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings {
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings(CSharp.LanguageVersion.CSharp1) {
AutomaticEvents = false,
AutomaticProperties = false,
});
@ -122,7 +122,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -122,7 +122,7 @@ namespace ICSharpCode.Decompiler.Tests
[Test]
public void AggressiveScalarReplacementOfAggregates([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings {
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings(CSharp.LanguageVersion.CSharp3) {
AggressiveScalarReplacementOfAggregates = true
});
}

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

@ -1524,15 +1524,26 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -1524,15 +1524,26 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
StartNode(namespaceDeclaration);
WriteKeyword(Roles.NamespaceKeyword);
namespaceDeclaration.NamespaceName.AcceptVisitor(this);
OpenBrace(policy.NamespaceBraceStyle);
if (namespaceDeclaration.IsFileScoped)
{
Semicolon();
NewLine();
}
else
{
OpenBrace(policy.NamespaceBraceStyle);
}
foreach (var member in namespaceDeclaration.Members)
{
member.AcceptVisitor(this);
MaybeNewLinesAfterUsings(member);
}
CloseBrace(policy.NamespaceBraceStyle);
OptionalSemicolon(namespaceDeclaration.LastChild);
NewLine();
if (!namespaceDeclaration.IsFileScoped)
{
CloseBrace(policy.NamespaceBraceStyle);
OptionalSemicolon(namespaceDeclaration.LastChild);
NewLine();
}
EndNode(namespaceDeclaration);
}

2
ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/NamespaceDeclaration.cs

@ -42,6 +42,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -42,6 +42,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
}
public bool IsFileScoped { get; set; }
public CSharpTokenNode NamespaceToken {
get { return GetChildByRole(Roles.NamespaceKeyword); }
}

25
ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs

@ -11,6 +11,31 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -11,6 +11,31 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
class NormalizeBlockStatements : DepthFirstAstVisitor, IAstTransform
{
TransformContext context;
bool hasNamespace;
NamespaceDeclaration singleNamespaceDeclaration;
public override void VisitSyntaxTree(SyntaxTree syntaxTree)
{
singleNamespaceDeclaration = null;
hasNamespace = false;
base.VisitSyntaxTree(syntaxTree);
if (context.Settings.FileScopedNamespaces && singleNamespaceDeclaration != null)
{
singleNamespaceDeclaration.IsFileScoped = true;
}
}
public override void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration)
{
singleNamespaceDeclaration = null;
if (!hasNamespace)
{
hasNamespace = true;
singleNamespaceDeclaration = namespaceDeclaration;
}
base.VisitNamespaceDeclaration(namespaceDeclaration);
}
public override void VisitIfElseStatement(IfElseStatement ifElseStatement)
{

24
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -142,10 +142,16 @@ namespace ICSharpCode.Decompiler @@ -142,10 +142,16 @@ namespace ICSharpCode.Decompiler
usePrimaryConstructorSyntax = false;
covariantReturns = false;
}
if (languageVersion < CSharp.LanguageVersion.CSharp10_0)
{
fileScopedNamespaces = false;
}
}
public CSharp.LanguageVersion GetMinimumRequiredVersion()
{
if (fileScopedNamespaces)
return CSharp.LanguageVersion.CSharp10_0;
if (nativeIntegers || initAccessors || functionPointers || forEachWithGetEnumeratorExtension
|| recordClasses || withExpressions || usePrimaryConstructorSyntax || covariantReturns)
return CSharp.LanguageVersion.CSharp9_0;
@ -323,6 +329,24 @@ namespace ICSharpCode.Decompiler @@ -323,6 +329,24 @@ namespace ICSharpCode.Decompiler
}
}
bool fileScopedNamespaces = true;
/// <summary>
/// Use C# 10 file-scoped namespaces.
/// </summary>
[Category("C# 10.0 / VS 2022")]
[Description("DecompilerSettings.FileScopedNamespaces")]
public bool FileScopedNamespaces {
get { return fileScopedNamespaces; }
set {
if (fileScopedNamespaces != value)
{
fileScopedNamespaces = value;
OnPropertyChanged();
}
}
}
bool anonymousMethods = true;
/// <summary>

3
ILSpy/Options/DecompilerSettingsPanel.xaml.cs

@ -26,6 +26,7 @@ using System.Windows.Data; @@ -26,6 +26,7 @@ using System.Windows.Data;
using System.Xml.Linq;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TreeNodes;
namespace ICSharpCode.ILSpy.Options
{
@ -157,7 +158,7 @@ namespace ICSharpCode.ILSpy.Options @@ -157,7 +158,7 @@ namespace ICSharpCode.ILSpy.Options
Settings = typeof(Decompiler.DecompilerSettings).GetProperties()
.Where(p => p.GetCustomAttribute<BrowsableAttribute>()?.Browsable != false)
.Select(p => new CSharpDecompilerSetting(p) { IsEnabled = (bool)p.GetValue(settings) })
.OrderBy(item => item.Category)
.OrderBy(item => item.Category, NaturalStringComparer.Instance)
.ThenBy(item => item.Description)
.ToArray();
}

9
ILSpy/Properties/Resources.Designer.cs generated

@ -954,6 +954,15 @@ namespace ICSharpCode.ILSpy.Properties { @@ -954,6 +954,15 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Use file-scoped namespace declarations.
/// </summary>
public static string DecompilerSettings_FileScopedNamespaces {
get {
return ResourceManager.GetString("DecompilerSettings.FileScopedNamespaces", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Transform to for, if possible.
/// </summary>

3
ILSpy/Properties/Resources.resx

@ -345,6 +345,9 @@ Are you sure you want to continue?</value> @@ -345,6 +345,9 @@ Are you sure you want to continue?</value>
<data name="DecompilerSettings.FSpecificOptions" xml:space="preserve">
<value>F#-specific options</value>
</data>
<data name="DecompilerSettings.FileScopedNamespaces" xml:space="preserve">
<value>Use file-scoped namespace declarations</value>
</data>
<data name="DecompilerSettings.ForStatement" xml:space="preserve">
<value>Transform to for, if possible</value>
</data>

Loading…
Cancel
Save