diff --git a/ICSharpCode.Decompiler.Generators/AnalyzerReleases.Unshipped.md b/ICSharpCode.Decompiler.Generators/AnalyzerReleases.Unshipped.md index a0353a503..326331a43 100644 --- a/ICSharpCode.Decompiler.Generators/AnalyzerReleases.Unshipped.md +++ b/ICSharpCode.Decompiler.Generators/AnalyzerReleases.Unshipped.md @@ -6,3 +6,7 @@ Rule ID | Category | Severity | Notes --------|----------|----------|------- DSTG001 | DecompilerSyntaxTreeGenerator | Error | Slot kind must map to a single child type +DSTG002 | DecompilerSettingsGenerator | Error | [DecompilerSetting] target must be a partial instance bool property +DSTG003 | DecompilerSettingsGenerator | Error | Version-gated setting must not declare [Category] +DSTG004 | DecompilerSettingsGenerator | Error | Language version has no display category +DSTG005 | DecompilerSettingsGenerator | Error | Setting must be declared in a non-nested partial class diff --git a/ICSharpCode.Decompiler.Generators/DecompilerSettingsGenerator.cs b/ICSharpCode.Decompiler.Generators/DecompilerSettingsGenerator.cs new file mode 100644 index 000000000..96f6a7258 --- /dev/null +++ b/ICSharpCode.Decompiler.Generators/DecompilerSettingsGenerator.cs @@ -0,0 +1,369 @@ +// Copyright (c) 2026 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System.Collections.Immutable; +using System.Text; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; + +namespace ICSharpCode.Decompiler.Generators; + +/// +/// Generates the boilerplate behind [DecompilerSetting] partial properties: the backing field, +/// the accessors with change notification, and - from the per-setting language version - the +/// [Category] attribute plus the SetLanguageVersion and GetMinimumRequiredVersion methods, +/// so that a setting's version is declared in exactly one place. +/// +[Generator] +internal class DecompilerSettingsGenerator : IIncrementalGenerator +{ + static readonly DiagnosticDescriptor InvalidSettingProperty = new( + id: "DSTG002", + title: "[DecompilerSetting] target must be a partial instance bool property", + messageFormat: "Setting property '{0}' must be a partial instance bool property with get and set accessors and an uppercase-start name (the generated backing field uses the camelCase form)", + category: "DecompilerSettingsGenerator", + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true); + + // The generated [Category] would join a handwritten one on the merged partial property, and + // GetCustomAttribute() (used by the settings UI) throws on duplicates. + static readonly DiagnosticDescriptor CategoryOnVersionedSetting = new( + id: "DSTG003", + title: "Version-gated setting must not declare [Category]", + messageFormat: "Setting '{0}' derives its [Category] from the language version; remove the handwritten [Category] attribute", + category: "DecompilerSettingsGenerator", + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true); + + static readonly DiagnosticDescriptor UnsupportedLanguageVersion = new( + id: "DSTG004", + title: "Language version has no display category", + messageFormat: "Language version '{0}' has no display category; gate settings on a released C# version, or add the new version to DecompilerSettingsGenerator.CategoryByVersion", + category: "DecompilerSettingsGenerator", + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true); + + // The generated implementation is emitted as a top-level partial class; a nested or + // non-partial containing type would make it merge nowhere (or into a stray new type). + static readonly DiagnosticDescriptor InvalidContainingType = new( + id: "DSTG005", + title: "Setting must be declared in a non-nested partial class", + messageFormat: "Setting '{0}' must be declared in a partial, non-nested class so the generated implementation merges into it", + category: "DecompilerSettingsGenerator", + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true); + + static readonly Dictionary DescriptorsById = + new DiagnosticDescriptor[] { InvalidSettingProperty, CategoryOnVersionedSetting, UnsupportedLanguageVersion, InvalidContainingType } + .ToDictionary(d => d.Id); + + // Display category per released C# version; the settings UI groups options by these strings. + static readonly Dictionary CategoryByVersion = new() { + ["CSharp1"] = "C# 1.0 / VS .NET", + ["CSharp2"] = "C# 2.0 / VS 2005", + ["CSharp3"] = "C# 3.0 / VS 2008", + ["CSharp4"] = "C# 4.0 / VS 2010", + ["CSharp5"] = "C# 5.0 / VS 2012", + ["CSharp6"] = "C# 6.0 / VS 2015", + ["CSharp7"] = "C# 7.0 / VS 2017", + ["CSharp7_1"] = "C# 7.1 / VS 2017.3", + ["CSharp7_2"] = "C# 7.2 / VS 2017.4", + ["CSharp7_3"] = "C# 7.3 / VS 2017.7", + ["CSharp8_0"] = "C# 8.0 / VS 2019", + ["CSharp9_0"] = "C# 9.0 / VS 2019.8", + ["CSharp10_0"] = "C# 10.0 / VS 2022", + ["CSharp11_0"] = "C# 11.0 / VS 2022.4", + ["CSharp12_0"] = "C# 12.0 / VS 2022.8", + ["CSharp13_0"] = "C# 13.0 / VS 2022.12", + ["CSharp14_0"] = "C# 14.0 / VS 2026", + }; + + readonly record struct SettingInfo( + string Namespace, string ClassName, string Accessibility, string PropertyName, string FieldName, + bool DefaultValue, int VersionValue, string? VersionName, string? Category, + string FilePath, int SpanStart); + + // A diagnostic captured during the transform; kept as plain values so the pipeline stays cacheable. + readonly record struct DiagInfo(string Id, string MessageArg, string FilePath, int SpanStart, int SpanLength, + int StartLine, int StartChar, int EndLine, int EndChar); + + readonly record struct SettingResult(SettingInfo? Setting, EquatableArray? Diagnostics); + + public void Initialize(IncrementalGeneratorInitializationContext context) + { + context.RegisterPostInitializationOutput(i => i.AddSource("DecompilerSettingsGeneratorAttributes.g.cs", RoslynHelpers.EmbeddedAttributeSource + @" +namespace ICSharpCode.Decompiler +{ + [global::Microsoft.CodeAnalysis.EmbeddedAttribute] + [global::System.AttributeUsage(global::System.AttributeTargets.Property)] + sealed class DecompilerSettingAttribute : global::System.Attribute + { + public DecompilerSettingAttribute() { } + + public DecompilerSettingAttribute(global::ICSharpCode.Decompiler.CSharp.LanguageVersion introducedIn) { } + + /// Initial value of the setting. Defaults to true. + public bool DefaultValue { get; set; } = true; + } +} + +")); + + var settings = context.SyntaxProvider.ForAttributeWithMetadataName( + "ICSharpCode.Decompiler.DecompilerSettingAttribute", + (n, ct) => n is PropertyDeclarationSyntax, + GetSetting); + + context.RegisterSourceOutput(settings.Collect(), WriteSettingsClasses); + } + + static SettingResult GetSetting(GeneratorAttributeSyntaxContext context, CancellationToken cancellationToken) + { + var property = (IPropertySymbol)context.TargetSymbol; + var node = (PropertyDeclarationSyntax)context.TargetNode; + var diagnostics = new List(); + + if (property.Type.SpecialType != SpecialType.System_Boolean || property.IsStatic + || property.GetMethod == null || property.SetMethod == null || property.SetMethod.IsInitOnly + || !char.IsUpper(property.Name[0]) + || !node.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword))) + { + diagnostics.Add(MakeDiagInfo(InvalidSettingProperty.Id, property.Name, node)); + return new SettingResult(null, diagnostics.ToEquatableArray()); + } + + if (property.ContainingType.ContainingType != null + || node.Parent is not ClassDeclarationSyntax containingClass + || !containingClass.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword))) + { + diagnostics.Add(MakeDiagInfo(InvalidContainingType.Id, property.Name, node)); + return new SettingResult(null, diagnostics.ToEquatableArray()); + } + + var attribute = context.Attributes[0]; + int versionValue = 0; + string? versionName = null; + string? category = null; + if (attribute.ConstructorArguments.Length == 1) + { + var versionArgument = attribute.ConstructorArguments[0]; + if (versionArgument.Kind == TypedConstantKind.Error || versionArgument.Value is not int boundVersion || versionArgument.Type is null) + { + // The argument did not bind (e.g. a typo'd enum member); the compiler already + // reports that error at the argument, so just skip the setting instead of + // crashing the whole generator. + return new SettingResult(null, null); + } + versionValue = boundVersion; + versionName = VersionNameFromSyntax(attribute) + ?? versionArgument.Type.GetMembers() + .OfType() + .FirstOrDefault(f => f.HasConstantValue && Equals(f.ConstantValue, versionValue))?.Name + ?? versionValue.ToString(); + if (!CategoryByVersion.TryGetValue(versionName, out category)) + { + diagnostics.Add(MakeDiagInfo(UnsupportedLanguageVersion.Id, versionName, node)); + return new SettingResult(null, diagnostics.ToEquatableArray()); + } + if (property.GetAttributes().Any(a => a.AttributeClass?.ToDisplayString() == "System.ComponentModel.CategoryAttribute")) + { + diagnostics.Add(MakeDiagInfo(CategoryOnVersionedSetting.Id, property.Name, node)); + // The compiler would otherwise also flag the generated [Category] as a duplicate; + // suppress it so the mistake surfaces as the single DSTG003. + category = null; + } + } + + bool defaultValue = true; + foreach (var named in attribute.NamedArguments) + { + // A named argument that failed to bind is already a compiler error; ignore it here. + if (named.Value.Value is not bool namedValue) + continue; + if (named.Key == "DefaultValue") + defaultValue = namedValue; + } + + string fieldName = char.ToLowerInvariant(property.Name[0]) + property.Name.Substring(1); + if (SyntaxFacts.GetKeywordKind(fieldName) != SyntaxKind.None) + fieldName = "@" + fieldName; + + var setting = new SettingInfo( + property.ContainingNamespace.IsGlobalNamespace ? "" : property.ContainingNamespace.ToDisplayString(), + property.ContainingType.Name, + SyntaxFacts.GetText(property.DeclaredAccessibility), + property.Name, + fieldName, + defaultValue, + versionValue, + versionName, + category, + node.SyntaxTree.FilePath, + node.SpanStart); + return new SettingResult(setting, diagnostics.Count == 0 ? null : diagnostics.ToEquatableArray()); + } + + // Prefer the enum member name as spelled at the use site: constant values are not unique in + // LanguageVersion (CSharp15_0 and Preview share a value), so a value-based reverse lookup can + // name an alias the user never wrote. + static string? VersionNameFromSyntax(AttributeData attribute) + { + if (attribute.ApplicationSyntaxReference?.GetSyntax() is not AttributeSyntax { ArgumentList.Arguments: { Count: >= 1 } arguments }) + return null; + if (arguments[0].NameEquals != null) + return null; + return arguments[0].Expression switch { + MemberAccessExpressionSyntax memberAccess => memberAccess.Name.Identifier.Text, + IdentifierNameSyntax identifier => identifier.Identifier.Text, + _ => null, + }; + } + + static DiagInfo MakeDiagInfo(string id, string messageArg, SyntaxNode node) + { + var lineSpan = node.GetLocation().GetLineSpan(); + return new DiagInfo(id, messageArg, node.SyntaxTree.FilePath, node.Span.Start, node.Span.Length, + lineSpan.StartLinePosition.Line, lineSpan.StartLinePosition.Character, + lineSpan.EndLinePosition.Line, lineSpan.EndLinePosition.Character); + } + + static void WriteSettingsClasses(SourceProductionContext context, ImmutableArray results) + { + foreach (var result in results) + { + if (result.Diagnostics is not { } resultDiagnostics) + continue; + foreach (var diag in resultDiagnostics) + { + // Indexer lookup so a diagnostic id missing from the map fails loudly instead of + // being reported under an unrelated descriptor. + var descriptor = DescriptorsById[diag.Id]; + var location = Location.Create(diag.FilePath, new TextSpan(diag.SpanStart, diag.SpanLength), + new LinePositionSpan(new LinePosition(diag.StartLine, diag.StartChar), new LinePosition(diag.EndLine, diag.EndChar))); + context.ReportDiagnostic(Diagnostic.Create(descriptor, location, diag.MessageArg)); + } + } + + var settings = results + .Where(r => r.Setting != null) + .Select(r => r.Setting!.Value) + .OrderBy(s => s.FilePath, StringComparer.Ordinal) + .ThenBy(s => s.SpanStart); + + foreach (var settingsClass in settings.GroupBy(s => (s.Namespace, s.ClassName))) + { + WriteSettingsClass(context, settingsClass.Key.Namespace, settingsClass.Key.ClassName, settingsClass.ToArray()); + } + } + + static void WriteSettingsClass(SourceProductionContext context, string ns, string className, SettingInfo[] settings) + { + var builder = new StringBuilder(); + builder.AppendLine("// "); + builder.AppendLine("#nullable enable"); + builder.AppendLine(); + if (ns.Length > 0) + { + builder.AppendLine($"namespace {ns}"); + builder.AppendLine("{"); + } + builder.AppendLine($"\tpartial class {className}"); + builder.AppendLine("\t{"); + + foreach (var setting in settings) + { + builder.AppendLine($"\t\tbool {setting.FieldName} = {(setting.DefaultValue ? "true" : "false")};"); + builder.AppendLine(); + if (setting.Category != null) + { + builder.AppendLine($"\t\t[global::System.ComponentModel.Category(\"{setting.Category}\")]"); + } + builder.AppendLine($"\t\t{setting.Accessibility} partial bool {setting.PropertyName} {{"); + builder.AppendLine($"\t\t\tget {{ return {setting.FieldName}; }}"); + builder.AppendLine("\t\t\tset {"); + builder.AppendLine($"\t\t\t\tif ({setting.FieldName} != value)"); + builder.AppendLine("\t\t\t\t{"); + builder.AppendLine($"\t\t\t\t\t{setting.FieldName} = value;"); + builder.AppendLine("\t\t\t\t\tOnPropertyChanged();"); + builder.AppendLine("\t\t\t\t}"); + builder.AppendLine("\t\t\t}"); + builder.AppendLine("\t\t}"); + builder.AppendLine(); + } + + var versionBuckets = settings + .Where(s => s.VersionName != null) + .GroupBy(s => s.VersionValue) + .OrderBy(g => g.Key) + .ToArray(); + if (versionBuckets.Length > 0) + { + WriteSetLanguageVersion(builder, versionBuckets); + builder.AppendLine(); + WriteGetMinimumRequiredVersion(builder, versionBuckets); + } + + builder.AppendLine("\t}"); + if (ns.Length > 0) + { + builder.AppendLine("}"); + } + // The hint name must carry the full grouping key: two same-named settings classes in + // different namespaces would otherwise collide in AddSource and kill the generator. + string hintName = ns.Length == 0 ? $"{className}.Settings.g.cs" : $"{ns}.{className}.Settings.g.cs"; + context.AddSource(hintName, SourceText.From(builder.ToString().Replace("\r\n", "\n"), Encoding.UTF8)); + } + + // Emitted as partial implementing declarations: the containing class supplies the defining + // stubs, which is where the XML documentation lives (the docs on a partial method's defining + // declaration apply as long as the implementation carries none). + static void WriteSetLanguageVersion(StringBuilder builder, IGrouping[] versionBuckets) + { + builder.AppendLine("\t\tpublic partial void SetLanguageVersion(global::ICSharpCode.Decompiler.CSharp.LanguageVersion languageVersion)"); + builder.AppendLine("\t\t{"); + builder.AppendLine("\t\t\t// By default, all decompiler features are enabled."); + builder.AppendLine("\t\t\t// Disable some of them based on language version:"); + foreach (var bucket in versionBuckets) + { + builder.AppendLine($"\t\t\tif (languageVersion < global::ICSharpCode.Decompiler.CSharp.LanguageVersion.{bucket.First().VersionName})"); + builder.AppendLine("\t\t\t{"); + foreach (var setting in bucket) + { + builder.AppendLine($"\t\t\t\t{setting.FieldName} = false;"); + } + builder.AppendLine("\t\t\t}"); + } + builder.AppendLine("\t\t}"); + } + + static void WriteGetMinimumRequiredVersion(StringBuilder builder, IGrouping[] versionBuckets) + { + builder.AppendLine("\t\tpublic partial global::ICSharpCode.Decompiler.CSharp.LanguageVersion GetMinimumRequiredVersion()"); + builder.AppendLine("\t\t{"); + foreach (var bucket in versionBuckets.Reverse()) + { + builder.AppendLine($"\t\t\tif ({string.Join(" || ", bucket.Select(s => s.FieldName))})"); + builder.AppendLine($"\t\t\t\treturn global::ICSharpCode.Decompiler.CSharp.LanguageVersion.{bucket.First().VersionName};"); + } + builder.AppendLine("\t\t\treturn global::ICSharpCode.Decompiler.CSharp.LanguageVersion.CSharp1;"); + builder.AppendLine("\t\t}"); + } +} diff --git a/ICSharpCode.Decompiler.Generators/DecompilerSyntaxTreeGenerator.cs b/ICSharpCode.Decompiler.Generators/DecompilerSyntaxTreeGenerator.cs index c5ea313e1..44044d019 100644 --- a/ICSharpCode.Decompiler.Generators/DecompilerSyntaxTreeGenerator.cs +++ b/ICSharpCode.Decompiler.Generators/DecompilerSyntaxTreeGenerator.cs @@ -843,17 +843,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator var visitorMembers = astNodeAdditions.Collect(); context - .RegisterPostInitializationOutput(i => i.AddSource("DecompilerSyntaxTreeGeneratorAttributes.g.cs", @" - -using System; - -namespace Microsoft.CodeAnalysis -{ - internal sealed partial class EmbeddedAttribute : global::System.Attribute - { - } -} - + .RegisterPostInitializationOutput(i => i.AddSource("DecompilerSyntaxTreeGeneratorAttributes.g.cs", RoslynHelpers.EmbeddedAttributeSource + @" namespace ICSharpCode.Decompiler.CSharp.Syntax { [global::Microsoft.CodeAnalysis.EmbeddedAttribute] diff --git a/ICSharpCode.Decompiler.Generators/RoslynHelpers.cs b/ICSharpCode.Decompiler.Generators/RoslynHelpers.cs index c0350704e..60e2ce2ef 100644 --- a/ICSharpCode.Decompiler.Generators/RoslynHelpers.cs +++ b/ICSharpCode.Decompiler.Generators/RoslynHelpers.cs @@ -22,6 +22,21 @@ namespace ICSharpCode.Decompiler.Generators; public static class RoslynHelpers { + /// + /// Post-init source declaring Microsoft.CodeAnalysis.EmbeddedAttribute. Every generator that + /// emits embedded attribute types has to ship its own copy in its own post-init output; the + /// declarations merge only while they stay partial and textually compatible, so all generators + /// must build their copy from this single constant. + /// + public const string EmbeddedAttributeSource = @" +namespace Microsoft.CodeAnalysis +{ + internal sealed partial class EmbeddedAttribute : global::System.Attribute + { + } +} +"; + public static bool IsDerivedFrom(this INamedTypeSymbol type, INamedTypeSymbol baseType) { INamedTypeSymbol? t = type; diff --git a/ICSharpCode.Decompiler.Tests/DecompilerSettingsTests.cs b/ICSharpCode.Decompiler.Tests/DecompilerSettingsTests.cs new file mode 100644 index 000000000..2645e6f93 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/DecompilerSettingsTests.cs @@ -0,0 +1,53 @@ +// Copyright (c) 2026 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using ICSharpCode.Decompiler.CSharp; + +using NUnit.Framework; + +namespace ICSharpCode.Decompiler.Tests +{ + [TestFixture] + class DecompilerSettingsTests + { + [Test] + public void GetMinimumRequiredVersionReturnsTheHighestEnabledFeatureVersion() + { + var settings = new DecompilerSettings(LanguageVersion.CSharp1); + Assert.That(settings.GetMinimumRequiredVersion(), Is.EqualTo(LanguageVersion.CSharp1)); + + settings.AnonymousMethods = true; + Assert.That(settings.GetMinimumRequiredVersion(), Is.EqualTo(LanguageVersion.CSharp2)); + + // Syntax-preference settings participate too: enabled, they emit syntax of their version. + settings.UseEnhancedUsing = true; + Assert.That(settings.GetMinimumRequiredVersion(), Is.EqualTo(LanguageVersion.CSharp8_0)); + + settings.SwitchOnReadOnlySpanChar = true; + Assert.That(settings.GetMinimumRequiredVersion(), Is.EqualTo(LanguageVersion.CSharp11_0)); + + // The scan must pick the highest enabled feature, not the first match bottom-up. + settings.ParamsCollections = true; + Assert.That(settings.GetMinimumRequiredVersion(), Is.EqualTo(LanguageVersion.CSharp13_0)); + + settings.AnonymousMethods = false; + settings.ParamsCollections = false; + Assert.That(settings.GetMinimumRequiredVersion(), Is.EqualTo(LanguageVersion.CSharp11_0)); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs b/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs index c6ce3eada..84f85968e 100644 --- a/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs @@ -60,17 +60,31 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler LanguageVersion? languageVersion; + /// + /// The C# language version written into the exported project file as LangVersion. + /// This is an export parameter, not decompiler state: when not set explicitly, it defaults + /// to of the current settings, + /// and an explicit value below that minimum is rejected (here and again when the export + /// starts) because the emitted code could not compile under it. + /// public LanguageVersion LanguageVersion { get { return languageVersion ?? Settings.GetMinimumRequiredVersion(); } set { - var minVersion = Settings.GetMinimumRequiredVersion(); - if (value < minVersion) - throw new InvalidOperationException($"The chosen settings require at least {minVersion}." + - $" Please change the DecompilerSettings accordingly."); + ValidateLanguageVersion(value); languageVersion = value; } } + void ValidateLanguageVersion(LanguageVersion version) + { + var minVersion = Settings.GetMinimumRequiredVersion(); + if (version < minVersion) + { + throw new InvalidOperationException($"The chosen settings require at least {minVersion}." + + " Please change the DecompilerSettings accordingly."); + } + } + bool IProjectInfoProvider.CheckForOverflowUnderflow => Settings.CheckForOverflowUnderflow; public IAssemblyResolver AssemblyResolver { get; } @@ -154,6 +168,14 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler { throw new InvalidOperationException("Must set TargetDirectory"); } + // The LanguageVersion setter already rejects a version below what the settings require, + // but Settings is mutable and shared, so re-validate against the settings actually in + // effect now - otherwise the exported project would carry a LangVersion under which the + // emitted code cannot compile. + if (languageVersion is { } explicitVersion) + { + ValidateLanguageVersion(explicitVersion); + } DecompilerEventSource.Log.ProjectDecompilationStart(file.Name); int codeFileCount = 0, resourceFileCount = 0; try diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index 955003893..31082c624 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -27,7 +27,7 @@ namespace ICSharpCode.Decompiler /// /// Settings for the decompiler. /// - public class DecompilerSettings : INotifyPropertyChanged + public partial class DecompilerSettings : INotifyPropertyChanged { /// /// Equivalent to new DecompilerSettings(LanguageVersion.Latest) @@ -44,6 +44,8 @@ namespace ICSharpCode.Decompiler /// This does not imply that the resulting code strictly uses only language features from /// that version. Language constructs like generics or ref locals cannot be removed from /// the compiled code. + /// The language version is a construction shortcut, not state: it initializes the feature + /// flags once (see ) and is not stored afterwards. /// public DecompilerSettings(CSharp.LanguageVersion languageVersion) { @@ -51,377 +53,99 @@ namespace ICSharpCode.Decompiler } /// - /// Deactivates all language features from versions newer than . + /// One-shot profile initializer: deactivates all language features from versions newer than + /// . The version itself is not stored - the feature flags + /// are the only state - so the call is not reversible and a later call with a higher version + /// does not re-enable features. Use to derive a + /// version back from the flags. /// - public void SetLanguageVersion(CSharp.LanguageVersion languageVersion) - { - // By default, all decompiler features are enabled. - // Disable some of them based on language version: - if (languageVersion < CSharp.LanguageVersion.CSharp2) - { - anonymousMethods = false; - liftNullables = false; - yieldReturn = false; - useImplicitMethodGroupConversion = false; - useObjectCreationOfGenericTypeParameter = false; - } - if (languageVersion < CSharp.LanguageVersion.CSharp3) - { - anonymousTypes = false; - useLambdaSyntax = false; - objectCollectionInitializers = false; - automaticProperties = false; - extensionMethods = false; - queryExpressions = false; - expressionTrees = false; - } - if (languageVersion < CSharp.LanguageVersion.CSharp4) - { - dynamic = false; - namedArguments = false; - optionalArguments = false; - } - if (languageVersion < CSharp.LanguageVersion.CSharp5) - { - asyncAwait = false; - } - if (languageVersion < CSharp.LanguageVersion.CSharp6) - { - awaitInCatchFinally = false; - useExpressionBodyForCalculatedGetterOnlyProperties = false; - nullPropagation = false; - stringInterpolation = false; - dictionaryInitializers = false; - extensionMethodsInCollectionInitializers = false; - getterOnlyAutomaticProperties = false; - } - if (languageVersion < CSharp.LanguageVersion.CSharp7) - { - outVariables = false; - throwExpressions = false; - tupleTypes = false; - tupleConversions = false; - discards = false; - localFunctions = false; - deconstruction = false; - patternMatching = false; - useRefLocalsForAccurateOrderOfEvaluation = false; - } - if (languageVersion < CSharp.LanguageVersion.CSharp7_2) - { - introduceReadonlyAndInModifiers = false; - introduceRefModifiersOnStructs = false; - nonTrailingNamedArguments = false; - refExtensionMethods = false; - introducePrivateProtectedAccessibilty = false; - } - if (languageVersion < CSharp.LanguageVersion.CSharp7_3) - { - introduceUnmanagedConstraint = false; - stackAllocInitializers = false; - tupleComparisons = false; - patternBasedFixedStatement = false; - } - if (languageVersion < CSharp.LanguageVersion.CSharp8_0) - { - nullableReferenceTypes = false; - readOnlyMethods = false; - asyncUsingAndForEachStatement = false; - asyncEnumerator = false; - useEnhancedUsing = false; - staticLocalFunctions = false; - ranges = false; - switchExpressions = false; - recursivePatternMatching = false; - } - if (languageVersion < CSharp.LanguageVersion.CSharp9_0) - { - nativeIntegers = false; - initAccessors = false; - functionPointers = false; - forEachWithGetEnumeratorExtension = false; - recordClasses = false; - withExpressions = false; - usePrimaryConstructorSyntax = false; - covariantReturns = false; - relationalPatterns = false; - patternCombinators = false; - } - if (languageVersion < CSharp.LanguageVersion.CSharp10_0) - { - fileScopedNamespaces = false; - recordStructs = false; - structDefaultConstructorsAndFieldInitializers = false; - } - if (languageVersion < CSharp.LanguageVersion.CSharp11_0) - { - scopedRef = false; - requiredMembers = false; - numericIntPtr = false; - utf8StringLiterals = false; - unsignedRightShift = false; - checkedOperators = false; - } - if (languageVersion < CSharp.LanguageVersion.CSharp12_0) - { - refReadOnlyParameters = false; - usePrimaryConstructorSyntaxForNonRecordTypes = false; - inlineArrays = false; - } - if (languageVersion < CSharp.LanguageVersion.CSharp13_0) - { - paramsCollections = false; - } - if (languageVersion < CSharp.LanguageVersion.CSharp14_0) - { - extensionMembers = false; - firstClassSpanTypes = false; - } - } - - public CSharp.LanguageVersion GetMinimumRequiredVersion() - { - if (extensionMembers || firstClassSpanTypes) - return CSharp.LanguageVersion.CSharp14_0; - if (paramsCollections) - return CSharp.LanguageVersion.CSharp13_0; - if (refReadOnlyParameters || usePrimaryConstructorSyntaxForNonRecordTypes || inlineArrays) - return CSharp.LanguageVersion.CSharp12_0; - if (scopedRef || requiredMembers || numericIntPtr || utf8StringLiterals || unsignedRightShift || checkedOperators) - return CSharp.LanguageVersion.CSharp11_0; - if (fileScopedNamespaces || recordStructs || structDefaultConstructorsAndFieldInitializers) - return CSharp.LanguageVersion.CSharp10_0; - if (nativeIntegers || initAccessors || functionPointers || forEachWithGetEnumeratorExtension - || recordClasses || withExpressions || usePrimaryConstructorSyntax || covariantReturns - || relationalPatterns || patternCombinators) - return CSharp.LanguageVersion.CSharp9_0; - if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement - || staticLocalFunctions || ranges || switchExpressions || recursivePatternMatching) - return CSharp.LanguageVersion.CSharp8_0; - if (introduceUnmanagedConstraint || tupleComparisons || stackAllocInitializers - || patternBasedFixedStatement) - return CSharp.LanguageVersion.CSharp7_3; - if (introduceRefModifiersOnStructs || introduceReadonlyAndInModifiers - || nonTrailingNamedArguments || refExtensionMethods || introducePrivateProtectedAccessibilty) - return CSharp.LanguageVersion.CSharp7_2; - // C# 7.1 missing - if (outVariables || throwExpressions || tupleTypes || tupleConversions - || discards || localFunctions || deconstruction || patternMatching || useRefLocalsForAccurateOrderOfEvaluation) - return CSharp.LanguageVersion.CSharp7; - if (awaitInCatchFinally || useExpressionBodyForCalculatedGetterOnlyProperties || nullPropagation - || stringInterpolation || dictionaryInitializers || extensionMethodsInCollectionInitializers - || getterOnlyAutomaticProperties) - return CSharp.LanguageVersion.CSharp6; - if (asyncAwait) - return CSharp.LanguageVersion.CSharp5; - if (dynamic || namedArguments || optionalArguments) - return CSharp.LanguageVersion.CSharp4; - if (anonymousTypes || objectCollectionInitializers || automaticProperties - || queryExpressions || expressionTrees) - return CSharp.LanguageVersion.CSharp3; - if (anonymousMethods || liftNullables || yieldReturn || useImplicitMethodGroupConversion || useObjectCreationOfGenericTypeParameter) - return CSharp.LanguageVersion.CSharp2; - return CSharp.LanguageVersion.CSharp1; - } + public partial void SetLanguageVersion(CSharp.LanguageVersion languageVersion); - bool nativeIntegers = true; + /// + /// Derives the lowest language version that includes all currently enabled language + /// features. The settings do not store a language version, so this derivation is how a + /// version is recovered from the flags; project export uses it as the default (and lower + /// bound) for the LangVersion written into the project file. + /// + public partial CSharp.LanguageVersion GetMinimumRequiredVersion(); /// /// Use C# 9 nint/nuint types. /// - [Category("C# 9.0 / VS 2019.8")] [Description("DecompilerSettings.NativeIntegers")] - public bool NativeIntegers { - get { return nativeIntegers; } - set { - if (nativeIntegers != value) - { - nativeIntegers = value; - OnPropertyChanged(); - } - } - } - - bool numericIntPtr = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp9_0)] + public partial bool NativeIntegers { get; set; } /// /// Treat IntPtr/UIntPtr as nint/nuint. /// - [Category("C# 11.0 / VS 2022.4")] [Description("DecompilerSettings.NumericIntPtr")] - public bool NumericIntPtr { - get { return numericIntPtr; } - set { - if (numericIntPtr != value) - { - numericIntPtr = value; - OnPropertyChanged(); - } - } - } - - bool covariantReturns = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp11_0)] + public partial bool NumericIntPtr { get; set; } /// /// Decompile C# 9 covariant return types. /// - [Category("C# 9.0 / VS 2019.8")] [Description("DecompilerSettings.CovariantReturns")] - public bool CovariantReturns { - get { return covariantReturns; } - set { - if (covariantReturns != value) - { - covariantReturns = value; - OnPropertyChanged(); - } - } - } - - bool initAccessors = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp9_0)] + public partial bool CovariantReturns { get; set; } /// /// Use C# 9 init; property accessors. /// - [Category("C# 9.0 / VS 2019.8")] [Description("DecompilerSettings.InitAccessors")] - public bool InitAccessors { - get { return initAccessors; } - set { - if (initAccessors != value) - { - initAccessors = value; - OnPropertyChanged(); - } - } - } - - bool recordClasses = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp9_0)] + public partial bool InitAccessors { get; set; } /// /// Use C# 9 record classes. /// - [Category("C# 9.0 / VS 2019.8")] [Description("DecompilerSettings.RecordClasses")] - public bool RecordClasses { - get { return recordClasses; } - set { - if (recordClasses != value) - { - recordClasses = value; - OnPropertyChanged(); - } - } - } - - bool recordStructs = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp9_0)] + public partial bool RecordClasses { get; set; } /// /// Use C# 10 record structs. /// - [Category("C# 10.0 / VS 2022")] [Description("DecompilerSettings.RecordStructs")] - public bool RecordStructs { - get { return recordStructs; } - set { - if (recordStructs != value) - { - recordStructs = value; - OnPropertyChanged(); - } - } - } - - bool structDefaultConstructorsAndFieldInitializers = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp10_0)] + public partial bool RecordStructs { get; set; } /// /// Use field initializers in structs. /// - [Category("C# 10.0 / VS 2022")] [Description("DecompilerSettings.StructDefaultConstructorsAndFieldInitializers")] - public bool StructDefaultConstructorsAndFieldInitializers { - get { return structDefaultConstructorsAndFieldInitializers; } - set { - if (structDefaultConstructorsAndFieldInitializers != value) - { - structDefaultConstructorsAndFieldInitializers = value; - OnPropertyChanged(); - } - } - } - - bool withExpressions = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp10_0)] + public partial bool StructDefaultConstructorsAndFieldInitializers { get; set; } /// /// Use C# 9 with initializer expressions. /// - [Category("C# 9.0 / VS 2019.8")] [Description("DecompilerSettings.WithExpressions")] - public bool WithExpressions { - get { return withExpressions; } - set { - if (withExpressions != value) - { - withExpressions = value; - OnPropertyChanged(); - } - } - } - - bool usePrimaryConstructorSyntax = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp9_0)] + public partial bool WithExpressions { get; set; } /// /// Use primary constructor syntax with records. /// - [Category("C# 9.0 / VS 2019.8")] [Description("DecompilerSettings.UsePrimaryConstructorSyntax")] - public bool UsePrimaryConstructorSyntax { - get { return usePrimaryConstructorSyntax; } - set { - if (usePrimaryConstructorSyntax != value) - { - usePrimaryConstructorSyntax = value; - OnPropertyChanged(); - } - } - } - - bool functionPointers = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp9_0)] + public partial bool UsePrimaryConstructorSyntax { get; set; } /// /// Use C# 9 delegate* unmanaged types. /// If this option is disabled, function pointers will instead be decompiled with type `IntPtr`. /// - [Category("C# 9.0 / VS 2019.8")] [Description("DecompilerSettings.FunctionPointers")] - public bool FunctionPointers { - get { return functionPointers; } - set { - if (functionPointers != value) - { - functionPointers = value; - OnPropertyChanged(); - } - } - } - - bool scopedRef = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp9_0)] + public partial bool FunctionPointers { get; set; } /// /// Use C# 11 scoped modifier. /// - [Category("C# 11.0 / VS 2022.4")] [Description("DecompilerSettings.ScopedRef")] - public bool ScopedRef { - get { return scopedRef; } - set { - if (scopedRef != value) - { - scopedRef = value; - OnPropertyChanged(); - } - } - } + [DecompilerSetting(CSharp.LanguageVersion.CSharp11_0)] + public partial bool ScopedRef { get; set; } [Obsolete("Renamed to ScopedRef. This property will be removed in a future version of the decompiler.")] [Browsable(false)] @@ -430,610 +154,235 @@ namespace ICSharpCode.Decompiler set { ScopedRef = value; } } - bool requiredMembers = true; - /// /// Use C# 11 required modifier. /// - [Category("C# 11.0 / VS 2022.4")] [Description("DecompilerSettings.RequiredMembers")] - public bool RequiredMembers { - get { return requiredMembers; } - set { - if (requiredMembers != value) - { - requiredMembers = value; - OnPropertyChanged(); - } - } - } - - bool switchExpressions = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp11_0)] + public partial bool RequiredMembers { get; set; } /// /// Use C# 8 switch expressions. /// - [Category("C# 8.0 / VS 2019")] [Description("DecompilerSettings.SwitchExpressions")] - public bool SwitchExpressions { - get { return switchExpressions; } - set { - if (switchExpressions != value) - { - switchExpressions = value; - OnPropertyChanged(); - } - } - } - - bool fileScopedNamespaces = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp8_0)] + public partial bool SwitchExpressions { get; set; } /// /// 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; + [DecompilerSetting(CSharp.LanguageVersion.CSharp10_0)] + public partial bool FileScopedNamespaces { get; set; } /// /// Decompile anonymous methods/lambdas. /// - [Category("C# 2.0 / VS 2005")] [Description("DecompilerSettings.DecompileAnonymousMethodsLambdas")] - public bool AnonymousMethods { - get { return anonymousMethods; } - set { - if (anonymousMethods != value) - { - anonymousMethods = value; - OnPropertyChanged(); - } - } - } - - bool anonymousTypes = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp2)] + public partial bool AnonymousMethods { get; set; } /// /// Decompile anonymous types. /// - [Category("C# 3.0 / VS 2008")] [Description("DecompilerSettings.DecompileAnonymousTypes")] - public bool AnonymousTypes { - get { return anonymousTypes; } - set { - if (anonymousTypes != value) - { - anonymousTypes = value; - OnPropertyChanged(); - } - } - } - - bool useLambdaSyntax = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp3)] + public partial bool AnonymousTypes { get; set; } /// /// Use C# 3 lambda syntax if possible. /// - [Category("C# 3.0 / VS 2008")] [Description("DecompilerSettings.UseLambdaSyntaxIfPossible")] - public bool UseLambdaSyntax { - get { return useLambdaSyntax; } - set { - if (useLambdaSyntax != value) - { - useLambdaSyntax = value; - OnPropertyChanged(); - } - } - } - - bool expressionTrees = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp3)] + public partial bool UseLambdaSyntax { get; set; } /// /// Decompile expression trees. /// - [Category("C# 3.0 / VS 2008")] [Description("DecompilerSettings.DecompileExpressionTrees")] - public bool ExpressionTrees { - get { return expressionTrees; } - set { - if (expressionTrees != value) - { - expressionTrees = value; - OnPropertyChanged(); - } - } - } - - bool yieldReturn = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp3)] + public partial bool ExpressionTrees { get; set; } /// /// Decompile enumerators. /// - [Category("C# 2.0 / VS 2005")] [Description("DecompilerSettings.DecompileEnumeratorsYieldReturn")] - public bool YieldReturn { - get { return yieldReturn; } - set { - if (yieldReturn != value) - { - yieldReturn = value; - OnPropertyChanged(); - } - } - } - - bool dynamic = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp2)] + public partial bool YieldReturn { get; set; } /// /// Decompile use of the 'dynamic' type. /// - [Category("C# 4.0 / VS 2010")] [Description("DecompilerSettings.DecompileUseOfTheDynamicType")] - public bool Dynamic { - get { return dynamic; } - set { - if (dynamic != value) - { - dynamic = value; - OnPropertyChanged(); - } - } - } - - bool asyncAwait = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp4)] + public partial bool Dynamic { get; set; } /// /// Decompile async methods. /// - [Category("C# 5.0 / VS 2012")] [Description("DecompilerSettings.DecompileAsyncMethods")] - public bool AsyncAwait { - get { return asyncAwait; } - set { - if (asyncAwait != value) - { - asyncAwait = value; - OnPropertyChanged(); - } - } - } - - bool awaitInCatchFinally = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp5)] + public partial bool AsyncAwait { get; set; } /// /// Decompile await in catch/finally blocks. /// Only has an effect if is enabled. /// - [Category("C# 6.0 / VS 2015")] [Description("DecompilerSettings.DecompileAwaitInCatchFinallyBlocks")] - public bool AwaitInCatchFinally { - get { return awaitInCatchFinally; } - set { - if (awaitInCatchFinally != value) - { - awaitInCatchFinally = value; - OnPropertyChanged(); - } - } - } - - bool asyncEnumerator = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp6)] + public partial bool AwaitInCatchFinally { get; set; } /// /// Decompile IAsyncEnumerator/IAsyncEnumerable. /// Only has an effect if is enabled. /// - [Category("C# 8.0 / VS 2019")] [Description("DecompilerSettings.AsyncEnumerator")] - public bool AsyncEnumerator { - get { return asyncEnumerator; } - set { - if (asyncEnumerator != value) - { - asyncEnumerator = value; - OnPropertyChanged(); - } - } - } - - bool decimalConstants = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp8_0)] + public partial bool AsyncEnumerator { get; set; } /// /// Decompile [DecimalConstant(...)] as simple literal values. /// - [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.DecompileDecimalConstantAsSimpleLiteralValues")] - public bool DecimalConstants { - get { return decimalConstants; } - set { - if (decimalConstants != value) - { - decimalConstants = value; - OnPropertyChanged(); - } - } - } - - bool fixedBuffers = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp1)] + public partial bool DecimalConstants { get; set; } /// /// Decompile C# 1.0 'public unsafe fixed int arr[10];' members. /// - [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.DecompileC10PublicUnsafeFixedIntArr10Members")] - public bool FixedBuffers { - get { return fixedBuffers; } - set { - if (fixedBuffers != value) - { - fixedBuffers = value; - OnPropertyChanged(); - } - } - } - - bool stringConcat = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp1)] + public partial bool FixedBuffers { get; set; } /// /// Decompile 'string.Concat(a, b)' calls into 'a + b'. /// - [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.StringConcat")] - public bool StringConcat { - get { return stringConcat; } - set { - if (stringConcat != value) - { - stringConcat = value; - OnPropertyChanged(); - } - } - } - - bool liftNullables = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp1)] + public partial bool StringConcat { get; set; } /// /// Use lifted operators for nullables. /// - [Category("C# 2.0 / VS 2005")] [Description("DecompilerSettings.UseLiftedOperatorsForNullables")] - public bool LiftNullables { - get { return liftNullables; } - set { - if (liftNullables != value) - { - liftNullables = value; - OnPropertyChanged(); - } - } - } - - bool nullPropagation = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp2)] + public partial bool LiftNullables { get; set; } /// /// Decompile C# 6 ?. and ?[] operators. /// - [Category("C# 6.0 / VS 2015")] [Description("DecompilerSettings.NullPropagation")] - public bool NullPropagation { - get { return nullPropagation; } - set { - if (nullPropagation != value) - { - nullPropagation = value; - OnPropertyChanged(); - } - } - } - - bool automaticProperties = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp6)] + public partial bool NullPropagation { get; set; } /// /// Decompile automatic properties /// - [Category("C# 3.0 / VS 2008")] [Description("DecompilerSettings.DecompileAutomaticProperties")] - public bool AutomaticProperties { - get { return automaticProperties; } - set { - if (automaticProperties != value) - { - automaticProperties = value; - OnPropertyChanged(); - } - } - } - - bool getterOnlyAutomaticProperties = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp3)] + public partial bool AutomaticProperties { get; set; } /// /// Decompile getter-only automatic properties /// - [Category("C# 6.0 / VS 2015")] [Description("DecompilerSettings.GetterOnlyAutomaticProperties")] - public bool GetterOnlyAutomaticProperties { - get { return getterOnlyAutomaticProperties; } - set { - if (getterOnlyAutomaticProperties != value) - { - getterOnlyAutomaticProperties = value; - OnPropertyChanged(); - } - } - } - - bool automaticEvents = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp6)] + public partial bool GetterOnlyAutomaticProperties { get; set; } /// /// Decompile automatic events /// - [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.DecompileAutomaticEvents")] - public bool AutomaticEvents { - get { return automaticEvents; } - set { - if (automaticEvents != value) - { - automaticEvents = value; - OnPropertyChanged(); - } - } - } - - bool usingStatement = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp1)] + public partial bool AutomaticEvents { get; set; } /// /// Decompile using statements. /// - [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.DetectUsingStatements")] - public bool UsingStatement { - get { return usingStatement; } - set { - if (usingStatement != value) - { - usingStatement = value; - OnPropertyChanged(); - } - } - } - - bool useEnhancedUsing = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp1)] + public partial bool UsingStatement { get; set; } /// /// Use enhanced using statements. /// - [Category("C# 8.0 / VS 2019")] [Description("DecompilerSettings.UseEnhancedUsing")] - public bool UseEnhancedUsing { - get { return useEnhancedUsing; } - set { - if (useEnhancedUsing != value) - { - useEnhancedUsing = value; - OnPropertyChanged(); - } - } - } - - bool alwaysUseBraces = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp8_0)] + public partial bool UseEnhancedUsing { get; set; } /// /// Gets/Sets whether to use braces for single-statement-blocks. /// [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.AlwaysUseBraces")] - public bool AlwaysUseBraces { - get { return alwaysUseBraces; } - set { - if (alwaysUseBraces != value) - { - alwaysUseBraces = value; - OnPropertyChanged(); - } - } - } - - bool forEachStatement = true; + [DecompilerSetting] + public partial bool AlwaysUseBraces { get; set; } /// /// Decompile foreach statements. /// - [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.DetectForeachStatements")] - public bool ForEachStatement { - get { return forEachStatement; } - set { - if (forEachStatement != value) - { - forEachStatement = value; - OnPropertyChanged(); - } - } - } - - bool forEachWithGetEnumeratorExtension = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp1)] + public partial bool ForEachStatement { get; set; } /// /// Support GetEnumerator extension methods in foreach. /// - [Category("C# 9.0 / VS 2019.8")] [Description("DecompilerSettings.DecompileForEachWithGetEnumeratorExtension")] - public bool ForEachWithGetEnumeratorExtension { - get { return forEachWithGetEnumeratorExtension; } - set { - if (forEachWithGetEnumeratorExtension != value) - { - forEachWithGetEnumeratorExtension = value; - OnPropertyChanged(); - } - } - } - - bool paramsCollections = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp9_0)] + public partial bool ForEachWithGetEnumeratorExtension { get; set; } /// /// Support params collections. /// - [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; + [DecompilerSetting(CSharp.LanguageVersion.CSharp13_0)] + public partial bool ParamsCollections { get; set; } /// /// Decompile lock statements. /// - [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.DetectLockStatements")] - public bool LockStatement { - get { return lockStatement; } - set { - if (lockStatement != value) - { - lockStatement = value; - OnPropertyChanged(); - } - } - } - - bool switchStatementOnString = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp1)] + public partial bool LockStatement { get; set; } - [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.DetectSwitchOnString")] - public bool SwitchStatementOnString { - get { return switchStatementOnString; } - set { - if (switchStatementOnString != value) - { - switchStatementOnString = value; - OnPropertyChanged(); - } - } - } + [DecompilerSetting(CSharp.LanguageVersion.CSharp1)] + public partial bool SwitchStatementOnString { get; set; } - bool sparseIntegerSwitch = true; - - [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.SparseIntegerSwitch")] - public bool SparseIntegerSwitch { - get { return sparseIntegerSwitch; } - set { - if (sparseIntegerSwitch != value) - { - sparseIntegerSwitch = value; - OnPropertyChanged(); - } - } - } - - bool usingDeclarations = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp1)] + public partial bool SparseIntegerSwitch { get; set; } - [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.InsertUsingDeclarations")] - public bool UsingDeclarations { - get { return usingDeclarations; } - set { - if (usingDeclarations != value) - { - usingDeclarations = value; - OnPropertyChanged(); - } - } - } - - bool extensionMethods = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp1)] + public partial bool UsingDeclarations { get; set; } - [Category("C# 3.0 / VS 2008")] [Description("DecompilerSettings.UseExtensionMethodSyntax")] - public bool ExtensionMethods { - get { return extensionMethods; } - set { - if (extensionMethods != value) - { - extensionMethods = value; - OnPropertyChanged(); - } - } - } + [DecompilerSetting(CSharp.LanguageVersion.CSharp3)] + public partial bool ExtensionMethods { get; set; } - bool queryExpressions = true; - - [Category("C# 3.0 / VS 2008")] [Description("DecompilerSettings.UseLINQExpressionSyntax")] - public bool QueryExpressions { - get { return queryExpressions; } - set { - if (queryExpressions != value) - { - queryExpressions = value; - OnPropertyChanged(); - } - } - } - - bool useImplicitMethodGroupConversion = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp3)] + public partial bool QueryExpressions { get; set; } /// /// Gets/Sets whether to use C# 2.0 method group conversions. /// true: EventHandler h = this.OnClick; /// false: EventHandler h = new EventHandler(this.OnClick); /// - [Category("C# 2.0 / VS 2005")] [Description("DecompilerSettings.UseImplicitMethodGroupConversions")] - public bool UseImplicitMethodGroupConversion { - get { return useImplicitMethodGroupConversion; } - set { - if (useImplicitMethodGroupConversion != value) - { - useImplicitMethodGroupConversion = value; - OnPropertyChanged(); - } - } - } - - bool useObjectCreationOfGenericTypeParameter = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp2)] + public partial bool UseImplicitMethodGroupConversion { get; set; } /// /// Gets/Sets whether to use object creation expressions for generic types with new() constraint. /// true: T t = new T(); /// false: T t = Activator.CreateInstance<T>() /// - [Category("C# 2.0 / VS 2005")] [Description("DecompilerSettings.UseObjectCreationOfGenericTypeParameter")] - public bool UseObjectCreationOfGenericTypeParameter { - get { return useObjectCreationOfGenericTypeParameter; } - set { - if (useObjectCreationOfGenericTypeParameter != value) - { - useObjectCreationOfGenericTypeParameter = value; - OnPropertyChanged(); - } - } - } - - bool alwaysCastTargetsOfExplicitInterfaceImplementationCalls = false; + [DecompilerSetting(CSharp.LanguageVersion.CSharp2)] + public partial bool UseObjectCreationOfGenericTypeParameter { get; set; } /// /// Gets/Sets whether to always cast targets to explicitly implemented methods. @@ -1041,20 +390,10 @@ namespace ICSharpCode.Decompiler /// false: pictureBox1.BeginInit(); /// default: false /// - [Category("Other")] + [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.AlwaysCastTargetsOfExplicitInterfaceImplementationCalls")] - public bool AlwaysCastTargetsOfExplicitInterfaceImplementationCalls { - get { return alwaysCastTargetsOfExplicitInterfaceImplementationCalls; } - set { - if (alwaysCastTargetsOfExplicitInterfaceImplementationCalls != value) - { - alwaysCastTargetsOfExplicitInterfaceImplementationCalls = value; - OnPropertyChanged(); - } - } - } - - bool alwaysQualifyMemberReferences = false; + [DecompilerSetting(DefaultValue = false)] + public partial bool AlwaysCastTargetsOfExplicitInterfaceImplementationCalls { get; set; } /// /// Gets/Sets whether to always qualify member references. @@ -1062,20 +401,10 @@ namespace ICSharpCode.Decompiler /// false: DoSomething(); /// default: false /// - [Category("Other")] + [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.AlwaysQualifyMemberReferences")] - public bool AlwaysQualifyMemberReferences { - get { return alwaysQualifyMemberReferences; } - set { - if (alwaysQualifyMemberReferences != value) - { - alwaysQualifyMemberReferences = value; - OnPropertyChanged(); - } - } - } - - bool alwaysShowEnumMemberValues = false; + [DecompilerSetting(DefaultValue = false)] + public partial bool AlwaysQualifyMemberReferences { get; set; } /// /// Gets/Sets whether to always show enum member values. @@ -1083,113 +412,49 @@ namespace ICSharpCode.Decompiler /// false: enum Kind { A, B, C = 5 } /// default: false /// - [Category("Other")] + [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.AlwaysShowEnumMemberValues")] - public bool AlwaysShowEnumMemberValues { - get { return alwaysShowEnumMemberValues; } - set { - if (alwaysShowEnumMemberValues != value) - { - alwaysShowEnumMemberValues = value; - OnPropertyChanged(); - } - } - } - - bool useDebugSymbols = true; + [DecompilerSetting(DefaultValue = false)] + public partial bool AlwaysShowEnumMemberValues { get; set; } /// /// Gets/Sets whether to use variable names from debug symbols, if available. /// - [Category("Other")] - [Description("DecompilerSettings.UseVariableNamesFromDebugSymbolsIfAvailable")] - public bool UseDebugSymbols { - get { return useDebugSymbols; } - set { - if (useDebugSymbols != value) - { - useDebugSymbols = value; - OnPropertyChanged(); - } - } - } - - bool arrayInitializers = true; + [Category("DecompilerSettings.Other")] + [Description("DecompilerSettings.UseVariableNamesFromDebugSymbolsIfAvailable")] + [DecompilerSetting] + public partial bool UseDebugSymbols { get; set; } /// /// Gets/Sets whether to use array initializers. /// If set to false, might produce non-compilable code. /// - [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.ArrayInitializerExpressions")] - public bool ArrayInitializers { - get { return arrayInitializers; } - set { - if (arrayInitializers != value) - { - arrayInitializers = value; - OnPropertyChanged(); - } - } - } - - bool objectCollectionInitializers = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp1)] + public partial bool ArrayInitializers { get; set; } /// /// Gets/Sets whether to use C# 3.0 object/collection initializers. /// - [Category("C# 3.0 / VS 2008")] [Description("DecompilerSettings.ObjectCollectionInitializerExpressions")] - public bool ObjectOrCollectionInitializers { - get { return objectCollectionInitializers; } - set { - if (objectCollectionInitializers != value) - { - objectCollectionInitializers = value; - OnPropertyChanged(); - } - } - } - - bool dictionaryInitializers = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp3)] + public partial bool ObjectOrCollectionInitializers { get; set; } /// /// Gets/Sets whether to use C# 6.0 dictionary initializers. /// Only has an effect if ObjectOrCollectionInitializers is enabled. /// - [Category("C# 6.0 / VS 2015")] [Description("DecompilerSettings.DictionaryInitializerExpressions")] - public bool DictionaryInitializers { - get { return dictionaryInitializers; } - set { - if (dictionaryInitializers != value) - { - dictionaryInitializers = value; - OnPropertyChanged(); - } - } - } - - bool extensionMethodsInCollectionInitializers = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp6)] + public partial bool DictionaryInitializers { get; set; } /// /// Gets/Sets whether to use C# 6.0 Extension Add methods in collection initializers. /// Only has an effect if ObjectOrCollectionInitializers is enabled. /// - [Category("C# 6.0 / VS 2015")] [Description("DecompilerSettings.AllowExtensionAddMethodsInCollectionInitializerExpressions")] - public bool ExtensionMethodsInCollectionInitializers { - get { return extensionMethodsInCollectionInitializers; } - set { - if (extensionMethodsInCollectionInitializers != value) - { - extensionMethodsInCollectionInitializers = value; - OnPropertyChanged(); - } - } - } - - bool useRefLocalsForAccurateOrderOfEvaluation = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp6)] + public partial bool ExtensionMethodsInCollectionInitializers { get; set; } /// /// Gets/Sets whether to use local ref variables in cases where this is necessary @@ -1198,744 +463,289 @@ namespace ICSharpCode.Decompiler /// order of evaluation. /// See https://github.com/icsharpcode/ILSpy/issues/2050 /// - [Category("C# 7.0 / VS 2017")] [Description("DecompilerSettings.UseRefLocalsForAccurateOrderOfEvaluation")] - public bool UseRefLocalsForAccurateOrderOfEvaluation { - get { return useRefLocalsForAccurateOrderOfEvaluation; } - set { - if (useRefLocalsForAccurateOrderOfEvaluation != value) - { - useRefLocalsForAccurateOrderOfEvaluation = value; - OnPropertyChanged(); - } - } - } - - bool refExtensionMethods = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp7)] + public partial bool UseRefLocalsForAccurateOrderOfEvaluation { get; set; } /// /// Gets/Sets whether to use C# 7.2 'ref' extension methods. /// - [Category("C# 7.2 / VS 2017.4")] [Description("DecompilerSettings.AllowExtensionMethodSyntaxOnRef")] - public bool RefExtensionMethods { - get { return refExtensionMethods; } - set { - if (refExtensionMethods != value) - { - refExtensionMethods = value; - OnPropertyChanged(); - } - } - } - - bool stringInterpolation = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp7_2)] + public partial bool RefExtensionMethods { get; set; } /// /// Gets/Sets whether to use C# 6.0 string interpolation /// - [Category("C# 6.0 / VS 2015")] [Description("DecompilerSettings.UseStringInterpolation")] - public bool StringInterpolation { - get { return stringInterpolation; } - set { - if (stringInterpolation != value) - { - stringInterpolation = value; - OnPropertyChanged(); - } - } - } - - bool utf8StringLiterals = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp6)] + public partial bool StringInterpolation { get; set; } /// /// Gets/Sets whether to use C# 11.0 UTF-8 string literals /// - [Category("C# 11.0 / VS 2022.4")] [Description("DecompilerSettings.Utf8StringLiterals")] - public bool Utf8StringLiterals { - get { return utf8StringLiterals; } - set { - if (utf8StringLiterals != value) - { - utf8StringLiterals = value; - OnPropertyChanged(); - } - } - } - - bool switchOnReadOnlySpanChar = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp11_0)] + public partial bool Utf8StringLiterals { get; set; } /// /// Gets/Sets whether to use C# 11.0 switch on (ReadOnly)Span<char> /// - [Category("C# 11.0 / VS 2022.4")] [Description("DecompilerSettings.SwitchOnReadOnlySpanChar")] - public bool SwitchOnReadOnlySpanChar { - get { return switchOnReadOnlySpanChar; } - set { - if (switchOnReadOnlySpanChar != value) - { - switchOnReadOnlySpanChar = value; - OnPropertyChanged(); - } - } - } - - bool unsignedRightShift = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp11_0)] + public partial bool SwitchOnReadOnlySpanChar { get; set; } /// /// Gets/Sets whether to use C# 11.0 unsigned right shift operator. /// - [Category("C# 11.0 / VS 2022.4")] [Description("DecompilerSettings.UnsignedRightShift")] - public bool UnsignedRightShift { - get { return unsignedRightShift; } - set { - if (unsignedRightShift != value) - { - unsignedRightShift = value; - OnPropertyChanged(); - } - } - } - - bool checkedOperators = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp11_0)] + public partial bool UnsignedRightShift { get; set; } /// /// Gets/Sets whether to use C# 11.0 user-defined checked operators. /// - [Category("C# 11.0 / VS 2022.4")] [Description("DecompilerSettings.CheckedOperators")] - public bool CheckedOperators { - get { return checkedOperators; } - set { - if (checkedOperators != value) - { - checkedOperators = value; - OnPropertyChanged(); - } - } - } - - bool showXmlDocumentation = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp11_0)] + public partial bool CheckedOperators { get; set; } /// /// Gets/Sets whether to include XML documentation comments in the decompiled code. /// [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.IncludeXMLDocumentationCommentsInTheDecompiledCode")] - public bool ShowXmlDocumentation { - get { return showXmlDocumentation; } - set { - if (showXmlDocumentation != value) - { - showXmlDocumentation = value; - OnPropertyChanged(); - } - } - } - - bool foldBraces = false; + [DecompilerSetting] + public partial bool ShowXmlDocumentation { get; set; } [Browsable(false)] - public bool FoldBraces { - get { return foldBraces; } - set { - if (foldBraces != value) - { - foldBraces = value; - OnPropertyChanged(); - } - } - } - - bool expandXmlDocumentationComments = false; + [DecompilerSetting(DefaultValue = false)] + public partial bool FoldBraces { get; set; } [Browsable(false)] - public bool ExpandXmlDocumentationComments { - get { return expandXmlDocumentationComments; } - set { - if (expandXmlDocumentationComments != value) - { - expandXmlDocumentationComments = value; - OnPropertyChanged(); - } - } - } - - bool expandMemberDefinitions = false; + [DecompilerSetting(DefaultValue = false)] + public partial bool ExpandXmlDocumentationComments { get; set; } [Browsable(false)] - public bool ExpandMemberDefinitions { - get { return expandMemberDefinitions; } - set { - if (expandMemberDefinitions != value) - { - expandMemberDefinitions = value; - OnPropertyChanged(); - } - } - } - - bool expandUsingDeclarations = false; + [DecompilerSetting(DefaultValue = false)] + public partial bool ExpandMemberDefinitions { get; set; } [Browsable(false)] - public bool ExpandUsingDeclarations { - get { return expandUsingDeclarations; } - set { - if (expandUsingDeclarations != value) - { - expandUsingDeclarations = value; - OnPropertyChanged(); - } - } - } - - bool decompileMemberBodies = true; + [DecompilerSetting(DefaultValue = false)] + public partial bool ExpandUsingDeclarations { get; set; } /// /// Gets/Sets whether member bodies should be decompiled. /// [Category("DecompilerSettings.Other")] [Browsable(false)] - public bool DecompileMemberBodies { - get { return decompileMemberBodies; } - set { - if (decompileMemberBodies != value) - { - decompileMemberBodies = value; - OnPropertyChanged(); - } - } - } - - bool useExpressionBodyForCalculatedGetterOnlyProperties = true; + [DecompilerSetting] + public partial bool DecompileMemberBodies { get; set; } /// /// Gets/Sets whether simple calculated getter-only property declarations /// should use expression body syntax. /// - [Category("C# 6.0 / VS 2015")] [Description("DecompilerSettings.UseExpressionBodiedMemberSyntaxForGetOnlyProperties")] - public bool UseExpressionBodyForCalculatedGetterOnlyProperties { - get { return useExpressionBodyForCalculatedGetterOnlyProperties; } - set { - if (useExpressionBodyForCalculatedGetterOnlyProperties != value) - { - useExpressionBodyForCalculatedGetterOnlyProperties = value; - OnPropertyChanged(); - } - } - } - - bool outVariables = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp6)] + public partial bool UseExpressionBodyForCalculatedGetterOnlyProperties { get; set; } /// /// Gets/Sets whether out variable declarations should be used when possible. /// - [Category("C# 7.0 / VS 2017")] [Description("DecompilerSettings.UseOutVariableDeclarations")] - public bool OutVariables { - get { return outVariables; } - set { - if (outVariables != value) - { - outVariables = value; - OnPropertyChanged(); - } - } - } - - bool discards = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp7)] + public partial bool OutVariables { get; set; } /// /// Gets/Sets whether discards should be used when possible. /// Only has an effect if is enabled. /// - [Category("C# 7.0 / VS 2017")] [Description("DecompilerSettings.UseDiscards")] - public bool Discards { - get { return discards; } - set { - if (discards != value) - { - discards = value; - OnPropertyChanged(); - } - } - } - - bool introduceRefModifiersOnStructs = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp7)] + public partial bool Discards { get; set; } /// /// Gets/Sets whether IsByRefLikeAttribute should be replaced with 'ref' modifiers on structs. /// - [Category("C# 7.2 / VS 2017.4")] [Description("DecompilerSettings.IsByRefLikeAttributeShouldBeReplacedWithRefModifiersOnStructs")] - public bool IntroduceRefModifiersOnStructs { - get { return introduceRefModifiersOnStructs; } - set { - if (introduceRefModifiersOnStructs != value) - { - introduceRefModifiersOnStructs = value; - OnPropertyChanged(); - } - } - } - - bool introduceReadonlyAndInModifiers = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp7_2)] + public partial bool IntroduceRefModifiersOnStructs { get; set; } /// /// Gets/Sets whether IsReadOnlyAttribute should be replaced with 'readonly' modifiers on structs /// and with the 'in' modifier on parameters. /// - [Category("C# 7.2 / VS 2017.4")] [Description("DecompilerSettings." + "IsReadOnlyAttributeShouldBeReplacedWithReadonlyInModifiersOnStructsParameters")] - public bool IntroduceReadonlyAndInModifiers { - get { return introduceReadonlyAndInModifiers; } - set { - if (introduceReadonlyAndInModifiers != value) - { - introduceReadonlyAndInModifiers = value; - OnPropertyChanged(); - } - } - } - - bool introducePrivateProtectedAccessibilty = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp7_2)] + public partial bool IntroduceReadonlyAndInModifiers { get; set; } /// /// Gets/Sets whether "private protected" should be used. /// - [Category("C# 7.2 / VS 2017.4")] [Description("DecompilerSettings.IntroducePrivateProtectedAccessibility")] - public bool IntroducePrivateProtectedAccessibility { - get { return introducePrivateProtectedAccessibilty; } - set { - if (introducePrivateProtectedAccessibilty != value) - { - introducePrivateProtectedAccessibilty = value; - OnPropertyChanged(); - } - } - } + [DecompilerSetting(CSharp.LanguageVersion.CSharp7_2)] + public partial bool IntroducePrivateProtectedAccessibility { get; set; } - bool readOnlyMethods = true; - - [Category("C# 8.0 / VS 2019")] [Description("DecompilerSettings.ReadOnlyMethods")] - public bool ReadOnlyMethods { - get { return readOnlyMethods; } - set { - if (readOnlyMethods != value) - { - readOnlyMethods = value; - OnPropertyChanged(); - } - } - } - - bool asyncUsingAndForEachStatement = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp8_0)] + public partial bool ReadOnlyMethods { get; set; } - [Category("C# 8.0 / VS 2019")] [Description("DecompilerSettings.DetectAsyncUsingAndForeachStatements")] - public bool AsyncUsingAndForEachStatement { - get { return asyncUsingAndForEachStatement; } - set { - if (asyncUsingAndForEachStatement != value) - { - asyncUsingAndForEachStatement = value; - OnPropertyChanged(); - } - } - } - - bool introduceUnmanagedConstraint = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp8_0)] + public partial bool AsyncUsingAndForEachStatement { get; set; } /// /// If this option is active, [IsUnmanagedAttribute] on type parameters /// is replaced with "T : unmanaged" constraints. /// - [Category("C# 7.3 / VS 2017.7")] [Description("DecompilerSettings." + "IsUnmanagedAttributeOnTypeParametersShouldBeReplacedWithUnmanagedConstraints")] - public bool IntroduceUnmanagedConstraint { - get { return introduceUnmanagedConstraint; } - set { - if (introduceUnmanagedConstraint != value) - { - introduceUnmanagedConstraint = value; - OnPropertyChanged(); - } - } - } - - bool stackAllocInitializers = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp7_3)] + public partial bool IntroduceUnmanagedConstraint { get; set; } /// /// Gets/Sets whether C# 7.3 stackalloc initializers should be used. /// - [Category("C# 7.3 / VS 2017.7")] [Description("DecompilerSettings.UseStackallocInitializerSyntax")] - public bool StackAllocInitializers { - get { return stackAllocInitializers; } - set { - if (stackAllocInitializers != value) - { - stackAllocInitializers = value; - OnPropertyChanged(); - } - } - } - - bool patternBasedFixedStatement = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp7_3)] + public partial bool StackAllocInitializers { get; set; } /// /// Gets/Sets whether C# 7.3 pattern based fixed statement should be used. /// - [Category("C# 7.3 / VS 2017.7")] [Description("DecompilerSettings.UsePatternBasedFixedStatement")] - public bool PatternBasedFixedStatement { - get { return patternBasedFixedStatement; } - set { - if (patternBasedFixedStatement != value) - { - patternBasedFixedStatement = value; - OnPropertyChanged(); - } - } - } - - bool tupleTypes = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp7_3)] + public partial bool PatternBasedFixedStatement { get; set; } /// /// Gets/Sets whether tuple type syntax (int, string) /// should be used for System.ValueTuple. /// - [Category("C# 7.0 / VS 2017")] [Description("DecompilerSettings.UseTupleTypeSyntax")] - public bool TupleTypes { - get { return tupleTypes; } - set { - if (tupleTypes != value) - { - tupleTypes = value; - OnPropertyChanged(); - } - } - } - - bool throwExpressions = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp7)] + public partial bool TupleTypes { get; set; } /// /// Gets/Sets whether throw expressions should be used. /// - [Category("C# 7.0 / VS 2017")] [Description("DecompilerSettings.UseThrowExpressions")] - public bool ThrowExpressions { - get { return throwExpressions; } - set { - if (throwExpressions != value) - { - throwExpressions = value; - OnPropertyChanged(); - } - } - } - - bool tupleConversions = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp7)] + public partial bool ThrowExpressions { get; set; } /// /// Gets/Sets whether implicit conversions between tuples /// should be used in the decompiled output. /// - [Category("C# 7.0 / VS 2017")] [Description("DecompilerSettings.UseImplicitConversionsBetweenTupleTypes")] - public bool TupleConversions { - get { return tupleConversions; } - set { - if (tupleConversions != value) - { - tupleConversions = value; - OnPropertyChanged(); - } - } - } - - bool tupleComparisons = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp7)] + public partial bool TupleConversions { get; set; } /// /// Gets/Sets whether tuple comparisons should be detected. /// - [Category("C# 7.3 / VS 2017.7")] [Description("DecompilerSettings.DetectTupleComparisons")] - public bool TupleComparisons { - get { return tupleComparisons; } - set { - if (tupleComparisons != value) - { - tupleComparisons = value; - OnPropertyChanged(); - } - } - } - - bool namedArguments = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp7_3)] + public partial bool TupleComparisons { get; set; } /// /// Gets/Sets whether named arguments should be used. /// - [Category("C# 4.0 / VS 2010")] [Description("DecompilerSettings.UseNamedArguments")] - public bool NamedArguments { - get { return namedArguments; } - set { - if (namedArguments != value) - { - namedArguments = value; - OnPropertyChanged(); - } - } - } - - bool nonTrailingNamedArguments = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp4)] + public partial bool NamedArguments { get; set; } /// /// Gets/Sets whether C# 7.2 non-trailing named arguments should be used. /// - [Category("C# 7.2 / VS 2017.4")] [Description("DecompilerSettings.UseNonTrailingNamedArguments")] - public bool NonTrailingNamedArguments { - get { return nonTrailingNamedArguments; } - set { - if (nonTrailingNamedArguments != value) - { - nonTrailingNamedArguments = value; - OnPropertyChanged(); - } - } - } - - bool optionalArguments = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp7_2)] + public partial bool NonTrailingNamedArguments { get; set; } /// /// Gets/Sets whether optional arguments should be removed, if possible. /// - [Category("C# 4.0 / VS 2010")] [Description("DecompilerSettings.RemoveOptionalArgumentsIfPossible")] - public bool OptionalArguments { - get { return optionalArguments; } - set { - if (optionalArguments != value) - { - optionalArguments = value; - OnPropertyChanged(); - } - } - } - - bool expandParamsArguments = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp4)] + public partial bool OptionalArguments { get; set; } /// /// Gets/Sets whether to expand params arguments by replacing explicit array creation /// with individual values in method calls. - /// - [Category("C# 1.0 / VS .NET")] - [Description("DecompilerSettings.ExpandParamsArguments")] - public bool ExpandParamsArguments { - get { return expandParamsArguments; } - set { - if (expandParamsArguments != value) - { - expandParamsArguments = value; - OnPropertyChanged(); - } - } - } - - bool localFunctions = true; + /// + [Description("DecompilerSettings.ExpandParamsArguments")] + [DecompilerSetting(CSharp.LanguageVersion.CSharp1)] + public partial bool ExpandParamsArguments { get; set; } /// /// Gets/Sets whether C# 7.0 local functions should be transformed. /// - [Category("C# 7.0 / VS 2017")] [Description("DecompilerSettings.IntroduceLocalFunctions")] - public bool LocalFunctions { - get { return localFunctions; } - set { - if (localFunctions != value) - { - localFunctions = value; - OnPropertyChanged(); - } - } - } - - bool deconstruction = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp7)] + public partial bool LocalFunctions { get; set; } /// /// Gets/Sets whether C# 7.0 deconstruction should be detected. /// - [Category("C# 7.0 / VS 2017")] [Description("DecompilerSettings.Deconstruction")] - public bool Deconstruction { - get { return deconstruction; } - set { - if (deconstruction != value) - { - deconstruction = value; - OnPropertyChanged(); - } - } - } - - bool patternMatching = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp7)] + public partial bool Deconstruction { get; set; } /// /// Gets/Sets whether C# 7.0 pattern matching should be detected. /// - [Category("C# 7.0 / VS 2017")] [Description("DecompilerSettings.PatternMatching")] - public bool PatternMatching { - get { return patternMatching; } - set { - if (patternMatching != value) - { - patternMatching = value; - OnPropertyChanged(); - } - } - } - - bool recursivePatternMatching = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp7)] + public partial bool PatternMatching { get; set; } /// /// Gets/Sets whether C# 8.0 recursive patterns should be detected. /// - [Category("C# 8.0 / VS 2019")] [Description("DecompilerSettings.RecursivePatternMatching")] - public bool RecursivePatternMatching { - get { return recursivePatternMatching; } - set { - if (recursivePatternMatching != value) - { - recursivePatternMatching = value; - OnPropertyChanged(); - } - } - } - - bool patternCombinators = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp8_0)] + public partial bool RecursivePatternMatching { get; set; } /// /// Gets/Sets whether C# 9.0 and, or, not patterns should be detected. /// - [Category("C# 9.0 / VS 2019.8")] [Description("DecompilerSettings.PatternCombinators")] - public bool PatternCombinators { - get { return patternCombinators; } - set { - if (patternCombinators != value) - { - patternCombinators = value; - OnPropertyChanged(); - } - } - } - - bool relationalPatterns = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp9_0)] + public partial bool PatternCombinators { get; set; } /// /// Gets/Sets whether C# 9.0 relational patterns should be detected. /// - [Category("C# 9.0 / VS 2019.8")] [Description("DecompilerSettings.RelationalPatterns")] - public bool RelationalPatterns { - get { return relationalPatterns; } - set { - if (relationalPatterns != value) - { - relationalPatterns = value; - OnPropertyChanged(); - } - } - } - - bool staticLocalFunctions = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp9_0)] + public partial bool RelationalPatterns { get; set; } /// /// Gets/Sets whether C# 8.0 static local functions should be transformed. /// - [Category("C# 8.0 / VS 2019")] [Description("DecompilerSettings.IntroduceStaticLocalFunctions")] - public bool StaticLocalFunctions { - get { return staticLocalFunctions; } - set { - if (staticLocalFunctions != value) - { - staticLocalFunctions = value; - OnPropertyChanged(); - } - } - } - - bool ranges = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp8_0)] + public partial bool StaticLocalFunctions { get; set; } /// /// Gets/Sets whether C# 8.0 index and range syntax should be used. /// - [Category("C# 8.0 / VS 2019")] [Description("DecompilerSettings.Ranges")] - public bool Ranges { - get { return ranges; } - set { - if (ranges != value) - { - ranges = value; - OnPropertyChanged(); - } - } - } - - bool nullableReferenceTypes = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp8_0)] + public partial bool Ranges { get; set; } /// /// Gets/Sets whether C# 8.0 nullable reference types are enabled. /// - [Category("C# 8.0 / VS 2019")] [Description("DecompilerSettings.NullableReferenceTypes")] - public bool NullableReferenceTypes { - get { return nullableReferenceTypes; } - set { - if (nullableReferenceTypes != value) - { - nullableReferenceTypes = value; - OnPropertyChanged(); - } - } - } - - bool showDebugInfo; + [DecompilerSetting(CSharp.LanguageVersion.CSharp8_0)] + public partial bool NullableReferenceTypes { get; set; } [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.ShowInfoFromDebugSymbolsIfAvailable")] [Browsable(false)] - public bool ShowDebugInfo { - get { return showDebugInfo; } - set { - if (showDebugInfo != value) - { - showDebugInfo = value; - OnPropertyChanged(); - } - } - } - + [DecompilerSetting(DefaultValue = false)] + public partial bool ShowDebugInfo { get; set; } #region Options to aid VB decompilation - bool assumeArrayLengthFitsIntoInt32 = true; /// /// Gets/Sets whether the decompiler can assume that 'ldlen; conv.i4.ovf' @@ -1943,276 +753,106 @@ namespace ICSharpCode.Decompiler /// [Category("DecompilerSettings.VBSpecificOptions")] [Browsable(false)] - public bool AssumeArrayLengthFitsIntoInt32 { - get { return assumeArrayLengthFitsIntoInt32; } - set { - if (assumeArrayLengthFitsIntoInt32 != value) - { - assumeArrayLengthFitsIntoInt32 = value; - OnPropertyChanged(); - } - } - } - - bool introduceIncrementAndDecrement = true; + [DecompilerSetting] + public partial bool AssumeArrayLengthFitsIntoInt32 { get; set; } /// /// Gets/Sets whether to use increment and decrement operators /// [Category("DecompilerSettings.VBSpecificOptions")] [Browsable(false)] - public bool IntroduceIncrementAndDecrement { - get { return introduceIncrementAndDecrement; } - set { - if (introduceIncrementAndDecrement != value) - { - introduceIncrementAndDecrement = value; - OnPropertyChanged(); - } - } - } - - bool makeAssignmentExpressions = true; + [DecompilerSetting] + public partial bool IntroduceIncrementAndDecrement { get; set; } /// /// Gets/Sets whether to use assignment expressions such as in while ((count = Do()) != 0) ; /// [Category("DecompilerSettings.VBSpecificOptions")] [Browsable(false)] - public bool MakeAssignmentExpressions { - get { return makeAssignmentExpressions; } - set { - if (makeAssignmentExpressions != value) - { - makeAssignmentExpressions = value; - OnPropertyChanged(); - } - } - } - + [DecompilerSetting] + public partial bool MakeAssignmentExpressions { get; set; } #endregion - #region Options to aid F# decompilation - bool removeDeadCode = false; [Category("DecompilerSettings.FSpecificOptions")] [Description("DecompilerSettings.RemoveDeadAndSideEffectFreeCodeUseWithCaution")] - public bool RemoveDeadCode { - get { return removeDeadCode; } - set { - if (removeDeadCode != value) - { - removeDeadCode = value; - OnPropertyChanged(); - } - } - } - - bool removeDeadStores = false; + [DecompilerSetting(DefaultValue = false)] + public partial bool RemoveDeadCode { get; set; } [Category("DecompilerSettings.FSpecificOptions")] [Description("DecompilerSettings.RemoveDeadStores")] - public bool RemoveDeadStores { - get { return removeDeadStores; } - set { - if (removeDeadStores != value) - { - removeDeadStores = value; - OnPropertyChanged(); - } - } - } + [DecompilerSetting(DefaultValue = false)] + public partial bool RemoveDeadStores { get; set; } #endregion - #region Assembly Load and Resolve options - bool loadInMemory = false; - [Browsable(false)] - public bool LoadInMemory { - get { return loadInMemory; } - set { - if (loadInMemory != value) - { - loadInMemory = value; - OnPropertyChanged(); - } - } - } - - bool throwOnAssemblyResolveErrors = true; + [DecompilerSetting(DefaultValue = false)] + public partial bool LoadInMemory { get; set; } [Browsable(false)] - public bool ThrowOnAssemblyResolveErrors { - get { return throwOnAssemblyResolveErrors; } - set { - if (throwOnAssemblyResolveErrors != value) - { - throwOnAssemblyResolveErrors = value; - OnPropertyChanged(); - } - } - } - - bool applyWindowsRuntimeProjections = true; + [DecompilerSetting] + public partial bool ThrowOnAssemblyResolveErrors { get; set; } [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.ApplyWindowsRuntimeProjectionsOnLoadedAssemblies")] - public bool ApplyWindowsRuntimeProjections { - get { return applyWindowsRuntimeProjections; } - set { - if (applyWindowsRuntimeProjections != value) - { - applyWindowsRuntimeProjections = value; - OnPropertyChanged(); - } - } - } - - bool autoLoadAssemblyReferences = true; + [DecompilerSetting] + public partial bool ApplyWindowsRuntimeProjections { get; set; } [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.AutoLoadAssemblyReferences")] - public bool AutoLoadAssemblyReferences { - get { return autoLoadAssemblyReferences; } - set { - if (autoLoadAssemblyReferences != value) - { - autoLoadAssemblyReferences = value; - OnPropertyChanged(); - } - } - } - + [DecompilerSetting] + public partial bool AutoLoadAssemblyReferences { get; set; } #endregion - bool forStatement = true; - /// /// Gets/sets whether the decompiler should produce for loops. /// - [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.ForStatement")] - public bool ForStatement { - get { return forStatement; } - set { - if (forStatement != value) - { - forStatement = value; - OnPropertyChanged(); - } - } - } - - bool doWhileStatement = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp1)] + public partial bool ForStatement { get; set; } /// /// Gets/sets whether the decompiler should produce do-while loops. /// - [Category("C# 1.0 / VS .NET")] [Description("DecompilerSettings.DoWhileStatement")] - public bool DoWhileStatement { - get { return doWhileStatement; } - set { - if (doWhileStatement != value) - { - doWhileStatement = value; - OnPropertyChanged(); - } - } - } - - bool refReadOnlyParameters = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp1)] + public partial bool DoWhileStatement { get; set; } /// /// Gets/sets whether RequiresLocationAttribute on parameters should be replaced with 'ref readonly' modifiers. /// - [Category("C# 12.0 / VS 2022.8")] [Description("DecompilerSettings.RefReadOnlyParameters")] - public bool RefReadOnlyParameters { - get { return refReadOnlyParameters; } - set { - if (refReadOnlyParameters != value) - { - refReadOnlyParameters = value; - OnPropertyChanged(); - } - } - } - - bool usePrimaryConstructorSyntaxForNonRecordTypes = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp12_0)] + public partial bool RefReadOnlyParameters { get; set; } /// /// Use primary constructor syntax with classes and structs. /// - [Category("C# 12.0 / VS 2022.8")] [Description("DecompilerSettings.UsePrimaryConstructorSyntaxForNonRecordTypes")] - public bool UsePrimaryConstructorSyntaxForNonRecordTypes { - get { return usePrimaryConstructorSyntaxForNonRecordTypes; } - set { - if (usePrimaryConstructorSyntaxForNonRecordTypes != value) - { - usePrimaryConstructorSyntaxForNonRecordTypes = value; - OnPropertyChanged(); - } - } - } - - bool inlineArrays = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp12_0)] + public partial bool UsePrimaryConstructorSyntaxForNonRecordTypes { get; set; } /// /// Gets/Sets whether C# 12.0 inline array uses should be transformed. /// - [Category("C# 12.0 / VS 2022.8")] [Description("DecompilerSettings.InlineArrays")] - public bool InlineArrays { - get { return inlineArrays; } - set { - if (inlineArrays != value) - { - inlineArrays = value; - OnPropertyChanged(); - } - } - } - - bool extensionMembers = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp12_0)] + public partial bool InlineArrays { get; set; } /// /// Gets/Sets whether C# 14.0 extension members should be transformed. /// - [Category("C# 14.0 / VS 2026")] [Description("DecompilerSettings.ExtensionMembers")] - public bool ExtensionMembers { - get { return extensionMembers; } - set { - if (extensionMembers != value) - { - extensionMembers = value; - OnPropertyChanged(); - } - } - } - - bool firstClassSpanTypes = true; + [DecompilerSetting(CSharp.LanguageVersion.CSharp14_0)] + public partial bool ExtensionMembers { get; set; } /// /// Gets/Sets whether (ReadOnly)Span<T> should be treated like built-in types. /// - [Category("C# 14.0 / VS 2026")] [Description("DecompilerSettings.FirstClassSpanTypes")] - public bool FirstClassSpanTypes { - get { return firstClassSpanTypes; } - set { - if (firstClassSpanTypes != value) - { - firstClassSpanTypes = value; - OnPropertyChanged(); - } - } - } - - bool separateLocalVariableDeclarations = false; + [DecompilerSetting(CSharp.LanguageVersion.CSharp14_0)] + public partial bool FirstClassSpanTypes { get; set; } /// /// Gets/sets whether the decompiler should separate local variable declarations @@ -2220,18 +860,8 @@ namespace ICSharpCode.Decompiler /// [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.SeparateLocalVariableDeclarations")] - public bool SeparateLocalVariableDeclarations { - get { return separateLocalVariableDeclarations; } - set { - if (separateLocalVariableDeclarations != value) - { - separateLocalVariableDeclarations = value; - OnPropertyChanged(); - } - } - } - - bool useSdkStyleProjectFormat = true; + [DecompilerSetting(DefaultValue = false)] + public partial bool SeparateLocalVariableDeclarations { get; set; } /// /// Gets or sets a value indicating whether the new SDK style format @@ -2239,18 +869,8 @@ namespace ICSharpCode.Decompiler /// [Category("DecompilerSettings.ProjectExport")] [Description("DecompilerSettings.UseSdkStyleProjectFormat")] - public bool UseSdkStyleProjectFormat { - get { return useSdkStyleProjectFormat; } - set { - if (useSdkStyleProjectFormat != value) - { - useSdkStyleProjectFormat = value; - OnPropertyChanged(); - } - } - } - - bool useNestedDirectoriesForNamespaces; + [DecompilerSetting] + public partial bool UseSdkStyleProjectFormat { get; set; } /// /// Gets/sets whether namespaces and namespace-like identifiers should be split at '.' @@ -2258,18 +878,8 @@ namespace ICSharpCode.Decompiler /// [Category("DecompilerSettings.ProjectExport")] [Description("DecompilerSettings.UseNestedDirectoriesForNamespaces")] - public bool UseNestedDirectoriesForNamespaces { - get { return useNestedDirectoriesForNamespaces; } - set { - if (useNestedDirectoriesForNamespaces != value) - { - useNestedDirectoriesForNamespaces = value; - OnPropertyChanged(); - } - } - } - - bool aggressiveScalarReplacementOfAggregates = false; + [DecompilerSetting(DefaultValue = false)] + public partial bool UseNestedDirectoriesForNamespaces { get; set; } [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.AggressiveScalarReplacementOfAggregates")] @@ -2277,18 +887,8 @@ namespace ICSharpCode.Decompiler #if !DEBUG [Browsable(false)] #endif - public bool AggressiveScalarReplacementOfAggregates { - get { return aggressiveScalarReplacementOfAggregates; } - set { - if (aggressiveScalarReplacementOfAggregates != value) - { - aggressiveScalarReplacementOfAggregates = value; - OnPropertyChanged(); - } - } - } - - bool aggressiveInlining = false; + [DecompilerSetting(DefaultValue = false)] + public partial bool AggressiveScalarReplacementOfAggregates { get; set; } /// /// If set to false (the default), the decompiler will inline local variables only when they occur @@ -2297,36 +897,16 @@ namespace ICSharpCode.Decompiler /// [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.AggressiveInlining")] - public bool AggressiveInlining { - get { return aggressiveInlining; } - set { - if (aggressiveInlining != value) - { - aggressiveInlining = value; - OnPropertyChanged(); - } - } - } - - bool alwaysUseGlobal = false; + [DecompilerSetting(DefaultValue = false)] + public partial bool AggressiveInlining { get; set; } /// /// Always fully qualify namespaces using the "global::" prefix. /// [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.AlwaysUseGlobal")] - public bool AlwaysUseGlobal { - get { return alwaysUseGlobal; } - set { - if (alwaysUseGlobal != value) - { - alwaysUseGlobal = value; - OnPropertyChanged(); - } - } - } - - bool alwaysMoveInitializer = false; + [DecompilerSetting(DefaultValue = false)] + public partial bool AlwaysUseGlobal { get; set; } /// /// If set to false (the default), the decompiler will move field initializers at the start of constructors @@ -2336,36 +916,16 @@ namespace ICSharpCode.Decompiler /// [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.AlwaysMoveInitializer")] - public bool AlwaysMoveInitializer { - get { return alwaysMoveInitializer; } - set { - if (alwaysMoveInitializer != value) - { - alwaysMoveInitializer = value; - OnPropertyChanged(); - } - } - } - - bool sortCustomAttributes = false; + [DecompilerSetting(DefaultValue = false)] + public partial bool AlwaysMoveInitializer { get; set; } /// /// Sort custom attributes. /// [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.SortCustomAttributes")] - public bool SortCustomAttributes { - get { return sortCustomAttributes; } - set { - if (sortCustomAttributes != value) - { - sortCustomAttributes = value; - OnPropertyChanged(); - } - } - } - - bool sortSwitchSections = false; + [DecompilerSetting(DefaultValue = false)] + public partial bool SortCustomAttributes { get; set; } /// /// Sort switch sections by their label value instead of by IL offset. @@ -2374,34 +934,16 @@ namespace ICSharpCode.Decompiler /// [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.SortSwitchSections")] - public bool SortSwitchSections { - get { return sortSwitchSections; } - set { - if (sortSwitchSections != value) - { - sortSwitchSections = value; - OnPropertyChanged(); - } - } - } - - bool checkForOverflowUnderflow = false; + [DecompilerSetting(DefaultValue = false)] + public partial bool SortSwitchSections { get; set; } /// /// Check for overflow and underflow in operators. /// [Category("DecompilerSettings.Other")] [Description("DecompilerSettings.CheckForOverflowUnderflow")] - public bool CheckForOverflowUnderflow { - get { return checkForOverflowUnderflow; } - set { - if (checkForOverflowUnderflow != value) - { - checkForOverflowUnderflow = value; - OnPropertyChanged(); - } - } - } + [DecompilerSetting(DefaultValue = false)] + public partial bool CheckForOverflowUnderflow { get; set; } CSharpFormattingOptions csharpFormattingOptions;