diff --git a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
index ff86a5261..de8ab9482 100644
--- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
+++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
@@ -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
{
diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
index c8aa7e614..d175c6ce2 100644
--- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
+++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
@@ -108,6 +108,7 @@
+
diff --git a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
index aaf365db8..b6f3bb72f 100644
--- a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
+++ b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
@@ -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
[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
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
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");
diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
index e01d6dfdc..0254e2bab 100644
--- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
+++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
@@ -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
// 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
{
RunForLibrary(
cscOptions: cscOptions,
- decompilerSettings: new DecompilerSettings { UseEnhancedUsing = false }
+ decompilerSettings: new DecompilerSettings {
+ UseEnhancedUsing = false,
+ FileScopedNamespaces = false,
+ }
);
}
@@ -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
{
RunForLibrary(
cscOptions: cscOptions,
- decompilerSettings: new DecompilerSettings { UseEnhancedUsing = false }
+ decompilerSettings: new DecompilerSettings { UseEnhancedUsing = false, FileScopedNamespaces = false }
);
}
@@ -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)
{
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/ForLoopTests.xml b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/ForLoopTests.xml
index 1d41df003..ad3e3b093 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/ForLoopTests.xml
+++ b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/ForLoopTests.xml
@@ -3,26 +3,25 @@
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/LambdaCapturing.xml b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/LambdaCapturing.xml
index 62e069886..2f95d9ba2 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/LambdaCapturing.xml
+++ b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/LambdaCapturing.xml
@@ -3,21 +3,20 @@
a + b + captured);
- }
+ int num = 4;
+ int captured = Environment.TickCount + num;
+ Test((int a, int b) => a + b + captured);
+ }
- private static void Test(Func p)
- {
- p(1, 2);
- }
+ private static void Test(Func p)
+ {
+ p(1, 2);
}
}
]]>
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FileScopedNamespaces.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FileScopedNamespaces.cs
new file mode 100644
index 000000000..af5755e13
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FileScopedNamespaces.cs
@@ -0,0 +1,9 @@
+namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty;
+
+internal delegate void DelegateInFileScopedNamespace();
+
+internal class FileScopedNamespaces
+{
+
+}
+
diff --git a/ICSharpCode.Decompiler.Tests/UglyTestRunner.cs b/ICSharpCode.Decompiler.Tests/UglyTestRunner.cs
index d475afd6d..e3ec45e64 100644
--- a/ICSharpCode.Decompiler.Tests/UglyTestRunner.cs
+++ b/ICSharpCode.Decompiler.Tests/UglyTestRunner.cs
@@ -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
[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
[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
[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
[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
[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
});
}
diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
index c7766b1e7..1e0deaff1 100644
--- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
+++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
@@ -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);
}
diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/NamespaceDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/NamespaceDeclaration.cs
index 6da1f44ec..b41e9f882 100644
--- a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/NamespaceDeclaration.cs
+++ b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/NamespaceDeclaration.cs
@@ -42,6 +42,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
}
+ public bool IsFileScoped { get; set; }
+
public CSharpTokenNode NamespaceToken {
get { return GetChildByRole(Roles.NamespaceKeyword); }
}
diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs b/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs
index e4034be38..88c6ac2f2 100644
--- a/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs
+++ b/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs
@@ -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)
{
diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs
index d564bee88..d76921cac 100644
--- a/ICSharpCode.Decompiler/DecompilerSettings.cs
+++ b/ICSharpCode.Decompiler/DecompilerSettings.cs
@@ -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
}
}
+ bool fileScopedNamespaces = true;
+
+ ///
+ /// Use C# 10 file-scoped namespaces.
+ ///
+ [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;
///
diff --git a/ILSpy/Options/DecompilerSettingsPanel.xaml.cs b/ILSpy/Options/DecompilerSettingsPanel.xaml.cs
index 665b5e081..d2d9402dc 100644
--- a/ILSpy/Options/DecompilerSettingsPanel.xaml.cs
+++ b/ILSpy/Options/DecompilerSettingsPanel.xaml.cs
@@ -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
Settings = typeof(Decompiler.DecompilerSettings).GetProperties()
.Where(p => p.GetCustomAttribute()?.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();
}
diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs
index 6901b3577..1fdc2e2cf 100644
--- a/ILSpy/Properties/Resources.Designer.cs
+++ b/ILSpy/Properties/Resources.Designer.cs
@@ -954,6 +954,15 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to Use file-scoped namespace declarations.
+ ///
+ public static string DecompilerSettings_FileScopedNamespaces {
+ get {
+ return ResourceManager.GetString("DecompilerSettings.FileScopedNamespaces", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Transform to for, if possible.
///
diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx
index 98f62371d..b01f6a5ba 100644
--- a/ILSpy/Properties/Resources.resx
+++ b/ILSpy/Properties/Resources.resx
@@ -345,6 +345,9 @@ Are you sure you want to continue?
F#-specific options
+
+ Use file-scoped namespace declarations
+
Transform to for, if possible