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) { diff --git a/src/Fallout.Tooling/ToolOptions.Arguments.cs b/src/Fallout.Tooling/ToolOptions.Arguments.cs index 570f05e91..1342fdd03 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/src/Persistence/Fallout.Persistence.Solution/Serializer/ISolutionSerializer.cs b/src/Persistence/Fallout.Persistence.Solution/Serializer/ISolutionSerializer.cs index a47d54c5a..8cf92ab4b 100644 --- a/src/Persistence/Fallout.Persistence.Solution/Serializer/ISolutionSerializer.cs +++ b/src/Persistence/Fallout.Persistence.Solution/Serializer/ISolutionSerializer.cs @@ -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, 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]