From b11033809dc5e8465ce7e4120bcfa3a2b105201c Mon Sep 17 00:00:00 2001 From: Dennis Doomen Date: Sat, 20 Jun 2026 09:10:18 +0200 Subject: [PATCH 1/4] Remove unused method parameters from utilities and CLI Surfaced by re-enabling the editorconfig. Across the build utilities, CLI commands, and tool infrastructure, method signatures declared parameters that were never read by the implementation. Each call site passed values that went unused, and removing them makes the intent of each method clear. Areas touched: ControlFlow suppression helpers (includeStackTrace), SchemaUtility enumeration schema builder (SchemaContext), CLI navigation and setup/update commands (redundant path and project-name arguments), GitHubActionsAttribute step builder (GitHubActionsImage), CodeGenerator runtime information applicator (specificationFile, sourceFileProvider, namespaceProvider), ProcessTasks invocation logger (hasEnvironmentVariables). One unused `using System.IO` import and a stale comment in the splash screen are also cleaned up. --- src/Fallout.Build/ControlFlow.cs | 5 ++--- src/Fallout.Build/Utilities/SchemaUtility.cs | 4 ++-- src/Fallout.Cli/Program.Navigation.cs | 10 +++++----- src/Fallout.Cli/Program.Setup.cs | 8 ++------ src/Fallout.Cli/Program.Update.cs | 5 +---- .../CI/GitHubActions/GitHubActionsAttribute.cs | 6 +++--- src/Fallout.Common/Tools/CorFlags/CorFlagsSettings.cs | 3 +-- src/Fallout.Tooling.Generator/CodeGenerator.cs | 7 ++----- src/Fallout.Tooling/ProcessTasks.cs | 4 ++-- 9 files changed, 20 insertions(+), 32 deletions(-) diff --git a/src/Fallout.Build/ControlFlow.cs b/src/Fallout.Build/ControlFlow.cs index ca8f09cc0..858e3fe7a 100644 --- a/src/Fallout.Build/ControlFlow.cs +++ b/src/Fallout.Build/ControlFlow.cs @@ -17,12 +17,12 @@ public static class ControlFlow { public static void SuppressErrors(Action action, bool includeStackTrace = false, bool logWarning = true) { - SuppressErrorsIf(condition: true, action, includeStackTrace: includeStackTrace, logWarning: logWarning); + SuppressErrorsIf(condition: true, action, logWarning: logWarning); } public static T SuppressErrors(Func action, T defaultValue = default, bool includeStackTrace = false, bool logWarning = true) { - return (T)SuppressErrorsIf(condition: true, action, defaultValue, includeStackTrace, logWarning); + return (T)SuppressErrorsIf(condition: true, action, defaultValue, logWarning); } public static IEnumerable SuppressErrors(Func> action, bool includeStackTrace = false) @@ -34,7 +34,6 @@ private static object SuppressErrorsIf( bool condition, Delegate action, object defaultValue = null, - bool includeStackTrace = false, bool logWarning = true) { if (!condition) diff --git a/src/Fallout.Build/Utilities/SchemaUtility.cs b/src/Fallout.Build/Utilities/SchemaUtility.cs index 7c6e5c171..c1685fb19 100644 --- a/src/Fallout.Build/Utilities/SchemaUtility.cs +++ b/src/Fallout.Build/Utilities/SchemaUtility.cs @@ -244,7 +244,7 @@ private static JsonObject SchemaForType(Type type, SchemaContext ctx) return new JsonObject { ["type"] = "string" }; if (typeof(Enumeration).IsAssignableFrom(type)) - return BuildEnumerationSchema(type, ctx); + return BuildEnumerationSchema(type); if (type.IsEnum) return StringEnumSchema(Enum.GetNames(type)); @@ -266,7 +266,7 @@ private static JsonObject SchemaForType(Type type, SchemaContext ctx) return BuildComplexTypeReference(type, ctx); } - private static JsonObject BuildEnumerationSchema(Type enumerationType, SchemaContext ctx) + private static JsonObject BuildEnumerationSchema(Type enumerationType) { var values = enumerationType .GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy) diff --git a/src/Fallout.Cli/Program.Navigation.cs b/src/Fallout.Cli/Program.Navigation.cs index 71f8e53a5..bfd191ac0 100644 --- a/src/Fallout.Cli/Program.Navigation.cs +++ b/src/Fallout.Cli/Program.Navigation.cs @@ -25,7 +25,7 @@ private static string SessionId private static AbsolutePath SessionFile => GlobalTemporaryDirectory / $"nuke-{SessionId}.dat"; - private static int GetNextDirectory(string[] args, AbsolutePath rootDirectory, AbsolutePath buildScript) + private static int GetNextDirectory() { var content = SessionFile.Existing()?.ReadAllLines(); if (content == null || string.IsNullOrWhiteSpace(content[0])) @@ -41,7 +41,7 @@ private static int GetNextDirectory(string[] args, AbsolutePath rootDirectory, A return 0; } - private static int PopDirectory(string[] args, AbsolutePath rootDirectory, AbsolutePath buildScript) + private static int PopDirectory() { var content = SessionFile.Existing()?.ReadAllLines().ToList(); if (content == null || content.Count <= 1) @@ -56,18 +56,18 @@ private static int PopDirectory(string[] args, AbsolutePath rootDirectory, Absol return 0; } - private static int PushWithCurrentRootDirectory(string[] args, AbsolutePath rootDirectory, AbsolutePath buildScript) + private static int PushWithCurrentRootDirectory(AbsolutePath rootDirectory) { return PushAndSetNext(() => rootDirectory.NotNull("No root directory")); } - private static int PushWithParentRootDirectory(string[] args, AbsolutePath rootDirectory, AbsolutePath buildScript) + private static int PushWithParentRootDirectory(AbsolutePath rootDirectory) { return PushAndSetNext(() => TryGetRootDirectoryFrom(Path.GetDirectoryName(rootDirectory.NotNull("No root directory"))) .NotNull("No parent root directory")); } - private static int PushWithChosenRootDirectory(string[] args, AbsolutePath rootDirectory, AbsolutePath buildScript) + private static int PushWithChosenRootDirectory() { return PushAndSetNext(() => { diff --git a/src/Fallout.Cli/Program.Setup.cs b/src/Fallout.Cli/Program.Setup.cs index a094e2e2f..819afebce 100644 --- a/src/Fallout.Cli/Program.Setup.cs +++ b/src/Fallout.Cli/Program.Setup.cs @@ -96,9 +96,7 @@ public static int Setup(string[] args, AbsolutePath rootDirectory, AbsolutePath WriteBuildScripts( scriptDirectory: WorkingDirectory, - rootDirectory, - buildDirectory, - buildProjectName); + rootDirectory); WriteConfigurationFile(rootDirectory, solutionFile); @@ -186,9 +184,7 @@ private static string[] GetTemplate(string templateName) private static void WriteBuildScripts( AbsolutePath scriptDirectory, - AbsolutePath rootDirectory, - AbsolutePath buildDirectory, - string buildProjectName) + AbsolutePath rootDirectory) { (scriptDirectory / "build.sh").WriteAllLines( FillTemplate( diff --git a/src/Fallout.Cli/Program.Update.cs b/src/Fallout.Cli/Program.Update.cs index 145bf805c..24a1c6eb3 100644 --- a/src/Fallout.Cli/Program.Update.cs +++ b/src/Fallout.Cli/Program.Update.cs @@ -1,5 +1,4 @@ using System; -using System.IO; using System.Linq; using System.Text.Json.Nodes; using Fallout.Common; @@ -42,9 +41,7 @@ private static void UpdateBuildScripts(AbsolutePath rootDirectory, AbsolutePath WriteBuildScripts( scriptDirectory: buildScript.Parent, - rootDirectory, - buildDirectory: buildProjectFile.NotNull().Parent, - buildProjectName: Path.GetFileNameWithoutExtension(buildProjectFile)); + rootDirectory); } private static void UpdateBuildProject(AbsolutePath buildScript) diff --git a/src/Fallout.Common/CI/GitHubActions/GitHubActionsAttribute.cs b/src/Fallout.Common/CI/GitHubActions/GitHubActionsAttribute.cs index 98c870cd8..a2350477f 100644 --- a/src/Fallout.Common/CI/GitHubActions/GitHubActionsAttribute.cs +++ b/src/Fallout.Common/CI/GitHubActions/GitHubActionsAttribute.cs @@ -71,7 +71,7 @@ public GitHubActionsAttribute( public string PublishCondition { get; set; } public int TimeoutMinutes { get; set; } - + public string EnvironmentName { get; set; } public string EnvironmentUrl { get; set; } @@ -163,7 +163,7 @@ protected virtual GitHubActionsJob GetJobs(GitHubActionsImage image, IReadOnlyCo Name = image.GetValue().Replace(".", "_"), EnvironmentName = EnvironmentName, EnvironmentUrl = EnvironmentUrl, - Steps = GetSteps(image, relevantTargets).ToArray(), + Steps = GetSteps(relevantTargets).ToArray(), Image = image, TimeoutMinutes = TimeoutMinutes, ConcurrencyGroup = JobConcurrencyGroup, @@ -171,7 +171,7 @@ protected virtual GitHubActionsJob GetJobs(GitHubActionsImage image, IReadOnlyCo }; } - private IEnumerable GetSteps(GitHubActionsImage image, IReadOnlyCollection relevantTargets) + private IEnumerable GetSteps(IReadOnlyCollection relevantTargets) { yield return new GitHubActionsCheckoutStep { diff --git a/src/Fallout.Common/Tools/CorFlags/CorFlagsSettings.cs b/src/Fallout.Common/Tools/CorFlags/CorFlagsSettings.cs index cbf870749..f24733583 100644 --- a/src/Fallout.Common/Tools/CorFlags/CorFlagsSettings.cs +++ b/src/Fallout.Common/Tools/CorFlags/CorFlagsSettings.cs @@ -1,11 +1,10 @@ using System; -using System.Reflection; namespace Fallout.Common.Tools.CorFlags; partial class CorFlagsSettings { - string FormatBoolean(bool? value, PropertyInfo property) + private static string FormatBoolean(bool? value) => value switch { true => "+", diff --git a/src/Fallout.Tooling.Generator/CodeGenerator.cs b/src/Fallout.Tooling.Generator/CodeGenerator.cs index f37f81fd1..182c7ed87 100644 --- a/src/Fallout.Tooling.Generator/CodeGenerator.cs +++ b/src/Fallout.Tooling.Generator/CodeGenerator.cs @@ -38,7 +38,7 @@ public static void GenerateCode( tool.SpecificationFile = specificationFile; tool.SourceFile = sourceFileProvider?.Invoke(tool); tool.Namespace = namespaceProvider?.Invoke(tool); - ApplyRuntimeInformation(tool, specificationFile, sourceFileProvider, namespaceProvider); + ApplyRuntimeInformation(tool); GenerateCode(tool, outputFileProvider?.Invoke(tool) ?? tool.DefaultOutputFile); } @@ -58,10 +58,7 @@ public static void GenerateCode(Tool tool, string outputFile) // ReSharper disable once CognitiveComplexity private static void ApplyRuntimeInformation( - Tool tool, - string specificationFile, - Func sourceFileProvider, - Func namespaceProvider) + Tool tool) { foreach (var task in tool.Tasks) { diff --git a/src/Fallout.Tooling/ProcessTasks.cs b/src/Fallout.Tooling/ProcessTasks.cs index d95a4fbed..a8838bd93 100644 --- a/src/Fallout.Tooling/ProcessTasks.cs +++ b/src/Fallout.Tooling/ProcessTasks.cs @@ -156,7 +156,7 @@ private static IProcess StartProcessInternal( } if (logInvocation) - LogInvocation(startInfo, outputFilter, environmentVariables != null); + LogInvocation(startInfo, outputFilter); var process = Process.Start(startInfo); if (process == null) @@ -166,7 +166,7 @@ private static IProcess StartProcessInternal( return new Process2(process, outputFilter, timeout, output); } - private static void LogInvocation(ProcessStartInfo startInfo, Func outputFilter, bool hasEnvironmentVariables) + private static void LogInvocation(ProcessStartInfo startInfo, Func outputFilter) { lock (s_lock) { From 982710f0a82ef30be67aab9696713539479ef01f Mon Sep 17 00:00:00 2001 From: Dennis Doomen Date: Sat, 20 Jun 2026 09:10:35 +0200 Subject: [PATCH 2/4] Introduce SingleFileSerializerBase and clean up the persistence serializer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generic arity suffix on the base class name was noise — there is only one concrete base, so the backtick-1 form added no information. Renaming to SingleFileSerializerBase makes the type easier to reference and matches the naming of the interface it implements. The file-stream handling switches from `await using` to synchronous `using`. `FileStream` does not implement `IAsyncDisposable` on `netstandard2.0`, so the async disposal form failed to compile there (CS8417). Synchronous disposal of a local read/write stream is correct on all target frameworks. Also fixed: a namespace mismatch on ISerializerModelExtension, two doc-comment typos in ISolutionSerializer, and redundant lambda parameter names in XmlFolder. --- .../Model/ISerializerModelExtension.cs | 2 ++ .../Serializer/ISolutionSerializer.cs | 6 +++--- ...zerBase`1.cs => SingleFileSerializerBase.cs} | 17 +++++++++-------- .../Serializer/Xml/XmlDecorators/XmlFolder.cs | 4 ++-- 4 files changed, 16 insertions(+), 13 deletions(-) rename src/Persistence/Fallout.Persistence.Solution/Serializer/{SingleFileSerializerBase`1.cs => SingleFileSerializerBase.cs} (74%) diff --git a/src/Persistence/Fallout.Persistence.Solution/Model/ISerializerModelExtension.cs b/src/Persistence/Fallout.Persistence.Solution/Model/ISerializerModelExtension.cs index 0a64090f8..93640553b 100644 --- a/src/Persistence/Fallout.Persistence.Solution/Model/ISerializerModelExtension.cs +++ b/src/Persistence/Fallout.Persistence.Solution/Model/ISerializerModelExtension.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Fallout.Persistence.Solution.Serializer; + namespace Fallout.Persistence.Solution.Model; /// diff --git a/src/Persistence/Fallout.Persistence.Solution/Serializer/ISolutionSerializer.cs b/src/Persistence/Fallout.Persistence.Solution/Serializer/ISolutionSerializer.cs index a47d54c5a..d43b10f5a 100644 --- a/src/Persistence/Fallout.Persistence.Solution/Serializer/ISolutionSerializer.cs +++ b/src/Persistence/Fallout.Persistence.Solution/Serializer/ISolutionSerializer.cs @@ -3,7 +3,7 @@ using Fallout.Persistence.Solution.Model; -namespace Fallout.Persistence.Solution; +namespace Fallout.Persistence.Solution.Serializer; /// /// Represents a solution serializer. @@ -45,7 +45,7 @@ public interface ISolutionSerializer /// For single file serializers, this checks the file extension. /// /// The moniker that represents the solution location. - /// If this serilizer can open the solution. + /// If this serializer can open the solution. bool IsSupported(string moniker); } @@ -86,7 +86,7 @@ public interface ISolutionSingleFileSerializer : ISolutionSerializer< /// /// Saves a solution model to a stream. /// - /// The stream to save the file.. + /// The stream to save the file. /// The model to save. /// Cancellation token. /// Task to track the asynchronous call status. diff --git a/src/Persistence/Fallout.Persistence.Solution/Serializer/SingleFileSerializerBase`1.cs b/src/Persistence/Fallout.Persistence.Solution/Serializer/SingleFileSerializerBase.cs similarity index 74% rename from src/Persistence/Fallout.Persistence.Solution/Serializer/SingleFileSerializerBase`1.cs rename to src/Persistence/Fallout.Persistence.Solution/Serializer/SingleFileSerializerBase.cs index a03a259b3..0a818cf75 100644 --- a/src/Persistence/Fallout.Persistence.Solution/Serializer/SingleFileSerializerBase`1.cs +++ b/src/Persistence/Fallout.Persistence.Solution/Serializer/SingleFileSerializerBase.cs @@ -36,10 +36,11 @@ bool ISolutionSerializer.IsSupported(string fullPath) async Task ISolutionSerializer.OpenAsync(string moniker, CancellationToken cancellationToken) { - using (FileStream reader = File.OpenRead(moniker)) - { - return await this.ReadModelAsync(moniker, reader, cancellationToken); - } + // Plain `using` (not `await using`): FileStream does not implement IAsyncDisposable on + // netstandard2.0, so `await using` fails to compile there (CS8417). Synchronous disposal + // of a local read stream is fine across all target frameworks. + using FileStream reader = File.OpenRead(moniker); + return await this.ReadModelAsync(moniker, reader, cancellationToken); } async Task ISolutionSerializer.SaveAsync(string moniker, SolutionModel model, CancellationToken cancellationToken) @@ -50,10 +51,10 @@ async Task ISolutionSerializer.SaveAsync(string moniker, SolutionModel model, Ca _ = Directory.CreateDirectory(directory); } - using (FileStream writer = File.OpenWrite(moniker)) - { - await this.WriteModelAsync(moniker, model, writer, cancellationToken); - } + // Plain `using` (not `await using`): see OpenAsync — FileStream is not IAsyncDisposable + // on netstandard2.0 (CS8417). Synchronous disposal of a local write stream is fine here. + using FileStream writer = File.OpenWrite(moniker); + await this.WriteModelAsync(moniker, model, writer, cancellationToken); } private protected abstract Task ReadModelAsync(string? fullPath, Stream reader, CancellationToken cancellationToken); diff --git a/src/Persistence/Fallout.Persistence.Solution/Serializer/Xml/XmlDecorators/XmlFolder.cs b/src/Persistence/Fallout.Persistence.Solution/Serializer/Xml/XmlDecorators/XmlFolder.cs index f551d9adb..41436c84d 100644 --- a/src/Persistence/Fallout.Persistence.Solution/Serializer/Xml/XmlDecorators/XmlFolder.cs +++ b/src/Persistence/Fallout.Persistence.Solution/Serializer/Xml/XmlDecorators/XmlFolder.cs @@ -129,8 +129,8 @@ internal bool ApplyModelToXml(SolutionFolderModel modelFolder) // Projects List<(string ItemRef, SolutionProjectModel Item)> projectsInFolder = modelSolution.SolutionProjects.WhereToList( - (project, modelFolder) => ReferenceEquals(project.Parent, modelFolder), - (project, modelFolder) => (ItemRef: this.Root.ConvertToUserPath(project.ItemRef), Item: project), + (project, solutionFolderModel) => ReferenceEquals(project.Parent, solutionFolderModel), + (project, _) => (ItemRef: this.Root.ConvertToUserPath(project.ItemRef), Item: project), modelFolder); modified |= this.ApplyModelItemsToXml( modelItems: projectsInFolder, From a3996fdc9f1b3df7f5f8f69cf1cb138f1fa5c615 Mon Sep 17 00:00:00 2001 From: Dennis Doomen Date: Sat, 20 Jun 2026 09:10:42 +0200 Subject: [PATCH 3/4] Make PropertyInfo argument optional for tool-option formatters Formatter methods were always invoked with two arguments (value, PropertyInfo), requiring every formatter to declare a PropertyInfo parameter even when it didn't use it. Removing that unused parameter then caused a parameter-count mismatch at runtime, surfacing as test failures once the CorFlagsSettings formatter was cleaned up. The invoker now matches arguments to the formatter's declared arity: single-parameter formatters receive [value] only; two-parameter formatters still receive [value, PropertyInfo]. Existing formatters are unaffected; formatters that don't need reflection context can now omit the PropertyInfo parameter. --- src/Fallout.Tooling/ToolOptions.Arguments.cs | 8 +++++++- .../Fallout.Tooling.Tests/ToolOptionsArgumentsTest.cs | 10 +++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Fallout.Tooling/ToolOptions.Arguments.cs b/src/Fallout.Tooling/ToolOptions.Arguments.cs index 570f05e91..df59eac9d 100644 --- a/src/Fallout.Tooling/ToolOptions.Arguments.cs +++ b/src/Fallout.Tooling/ToolOptions.Arguments.cs @@ -112,7 +112,13 @@ string Parse(JsonNode token, Type type) var formatterType = attribute.FormatterType ?? GetType(); var formatterMethod = formatterType.GetMethod(attribute.FormatterMethod, ReflectionUtility.All); var objValue = type != typeof(object) ? DeserializeWithCoercion(token, type) : NodeToString(token); - value = formatterMethod.GetValue(obj: this, args: [objValue, property]); + // The PropertyInfo is an optional second argument: formatters that don't need it + // can declare just the value parameter. Pass args matching the method's arity so + // both `Format(value)` and `Format(value, PropertyInfo)` shapes are supported. + object[] formatterArgs = formatterMethod.GetParameters().Length <= 1 + ? [objValue] + : [objValue, property]; + value = formatterMethod.GetValue(obj: this, args: formatterArgs); } else { diff --git a/tests/Fallout.Tooling.Tests/ToolOptionsArgumentsTest.cs b/tests/Fallout.Tooling.Tests/ToolOptionsArgumentsTest.cs index cfa788dfb..db70ebf52 100644 --- a/tests/Fallout.Tooling.Tests/ToolOptionsArgumentsTest.cs +++ b/tests/Fallout.Tooling.Tests/ToolOptionsArgumentsTest.cs @@ -99,8 +99,8 @@ private class FormatToolOptions : ToolOptions [Argument(Format = "{value}", FormatterType = typeof(Formatter), FormatterMethod = nameof(Formatter.FormatMinutes))] public TimeSpan Minutes => Get(() => Minutes); - private string FormatTime(DateTime datetime, PropertyInfo property) => datetime.ToString("t", CultureInfo.InvariantCulture); - private string FormatDate(DateTime datetime, PropertyInfo property) => datetime.ToString("d", CultureInfo.InvariantCulture); + private string FormatTime(DateTime datetime) => datetime.ToString("t", CultureInfo.InvariantCulture); + private string FormatDate(DateTime datetime) => datetime.ToString("d", CultureInfo.InvariantCulture); } private static class Formatter @@ -124,7 +124,7 @@ private class ListToolOptions : ToolOptions [Argument(Format = "--param:{value}", Separator = " ", QuoteMultiple = true)] public IReadOnlyList QuotedList => Get>(() => QuotedList); [Argument(Format = "--param={value}", FormatterMethod = nameof(Format))] public IReadOnlyList FormattedList => Get>(() => FormattedList); - private string Format(bool value, PropertyInfo property) => value.ToString().ToUpperInvariant(); + private string Format(bool value) => value.ToString().ToUpperInvariant(); } private readonly Dictionary _simpleDictionary = new() { ["key1"] = 1, ["key2"] = "foobar" }; @@ -143,7 +143,7 @@ private class DictionaryToolOptions : ToolOptions [Argument(Format = "-- {key}={value}", Separator = " ")] public IReadOnlyDictionary WhitespaceDictionary => Get>(() => WhitespaceDictionary); [Argument(Format = "/p:{key}={value}", FormatterMethod = nameof(Format))] public IReadOnlyDictionary FormattedDictionary => Get>(() => FormattedDictionary); - private string Format(object value, PropertyInfo property) => value?.ToString()?.ToUpperInvariant(); + private string Format(object value) => value?.ToString()?.ToUpperInvariant(); } private readonly LookupTable _simpleLookupTable = new() { ["key1"] = [1, 2], ["key2"] = [true, false] }; @@ -160,7 +160,7 @@ private class LookupToolOptions : ToolOptions [Argument(Format = "--param:{key}={value}", Separator = ";", InnerSeparator = ",")] public ILookup SeparatorLookup => Get>(() => SeparatorLookup); [Argument(Format = "--param {key} {value}", InnerSeparator = "+", FormatterMethod = nameof(Format))] public ILookup FormattedLookup => Get>(() => FormattedLookup); - private string Format(object value, PropertyInfo property) => value?.ToString()?.ToUpperInvariant(); + private string Format(object value) => value?.ToString()?.ToUpperInvariant(); } [Fact] From 76c552ab958d17a9a2bb1d2cded31e5535ef66cf Mon Sep 17 00:00:00 2001 From: Chrison Simtian Date: Mon, 22 Jun 2026 19:31:13 +1200 Subject: [PATCH 4/4] Keep ISolutionSerializer in its original namespace; address formatter-arity nitpick Reverts the inadvertent namespace move of the public ISolutionSerializer interfaces (ISolutionSerializer, ISolutionSerializer, ISolutionSingleFileSerializer) from Fallout.Persistence.Solution to .Serializer. The move was an IDE-applied "namespace matches folder" tidy that slipped into this otherwise non-breaking style-cleanup PR. It is a breaking change to public API (per @dennisdoomen's review) and also diverges from the upstream vs-solutionpersistence layout, which deliberately keeps the primary interface in the root namespace while concrete serializers live under .Serializer. Restoring the original namespace also drops the now-unneeded using in ISerializerModelExtension.cs (.Model is a child namespace and sees the parent's types without an explicit using). Also tighten the formatter-arity check to `== 1` (a formatter always takes at least the value argument, so a length of 0 is not a valid shape). Co-Authored-By: Claude Opus 4.8 (1M context) --- src/Fallout.Tooling/ToolOptions.Arguments.cs | 2 +- .../Model/ISerializerModelExtension.cs | 2 -- .../Serializer/ISolutionSerializer.cs | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Fallout.Tooling/ToolOptions.Arguments.cs b/src/Fallout.Tooling/ToolOptions.Arguments.cs index df59eac9d..1342fdd03 100644 --- a/src/Fallout.Tooling/ToolOptions.Arguments.cs +++ b/src/Fallout.Tooling/ToolOptions.Arguments.cs @@ -115,7 +115,7 @@ string Parse(JsonNode token, Type type) // The PropertyInfo is an optional second argument: formatters that don't need it // can declare just the value parameter. Pass args matching the method's arity so // both `Format(value)` and `Format(value, PropertyInfo)` shapes are supported. - object[] formatterArgs = formatterMethod.GetParameters().Length <= 1 + object[] formatterArgs = formatterMethod.GetParameters().Length == 1 ? [objValue] : [objValue, property]; value = formatterMethod.GetValue(obj: this, args: formatterArgs); diff --git a/src/Persistence/Fallout.Persistence.Solution/Model/ISerializerModelExtension.cs b/src/Persistence/Fallout.Persistence.Solution/Model/ISerializerModelExtension.cs index 93640553b..0a64090f8 100644 --- a/src/Persistence/Fallout.Persistence.Solution/Model/ISerializerModelExtension.cs +++ b/src/Persistence/Fallout.Persistence.Solution/Model/ISerializerModelExtension.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Fallout.Persistence.Solution.Serializer; - namespace Fallout.Persistence.Solution.Model; /// diff --git a/src/Persistence/Fallout.Persistence.Solution/Serializer/ISolutionSerializer.cs b/src/Persistence/Fallout.Persistence.Solution/Serializer/ISolutionSerializer.cs index d43b10f5a..8cf92ab4b 100644 --- a/src/Persistence/Fallout.Persistence.Solution/Serializer/ISolutionSerializer.cs +++ b/src/Persistence/Fallout.Persistence.Solution/Serializer/ISolutionSerializer.cs @@ -3,7 +3,7 @@ using Fallout.Persistence.Solution.Model; -namespace Fallout.Persistence.Solution.Serializer; +namespace Fallout.Persistence.Solution; /// /// Represents a solution serializer.