From f638e2a395a7af61cbf53473ed1f60c46ab326b5 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 27 Nov 2024 22:13:14 +0800 Subject: [PATCH 01/14] Update and remove duplicated entries from SolutionItems --- osu-framework.sln | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework.sln b/osu-framework.sln index 155884055c..0c502829ad 100644 --- a/osu-framework.sln +++ b/osu-framework.sln @@ -31,7 +31,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .globalconfig = .globalconfig Directory.Build.props = Directory.Build.props .config\dotnet-tools.json = .config\dotnet-tools.json - CodeAnalysis\osu-framework.ruleset = CodeAnalysis\osu-framework.ruleset + global.json = global.json osu-framework.sln.DotSettings = osu-framework.sln.DotSettings osu.Framework.Android.props = osu.Framework.Android.props osu.Framework.iOS.props = osu.Framework.iOS.props From a5cf8b698972e3230fc849b52b016798a6c786e6 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 27 Nov 2024 22:23:23 +0800 Subject: [PATCH 02/14] Port legacy ruleset into globalconfig --- .globalconfig | 9 ++++ CodeAnalysis/osu-framework.ruleset | 72 ------------------------------ Directory.Build.props | 3 -- osu-framework.sln | 1 - 4 files changed, 9 insertions(+), 76 deletions(-) delete mode 100644 CodeAnalysis/osu-framework.ruleset diff --git a/.globalconfig b/.globalconfig index 0a2727ecd4..512f6d45c9 100644 --- a/.globalconfig +++ b/.globalconfig @@ -46,6 +46,15 @@ dotnet_diagnostic.IDE0130.severity = warning # IDE1006: Naming style dotnet_diagnostic.IDE1006.severity = warning +# CA1309: Use ordinal string comparison +dotnet_diagnostic.CA1309.severity = warning + +# CA2007: Consider calling ConfigureAwait on the awaited task +dotnet_diagnostic.CA2007.severity = warning + +# CA2201: Do not raise reserved exception types +dotnet_diagnostic.CA2201.severity = warning + #Disable operator overloads requiring alternate named methods dotnet_diagnostic.CA2225.severity = none diff --git a/CodeAnalysis/osu-framework.ruleset b/CodeAnalysis/osu-framework.ruleset deleted file mode 100644 index 7b87ec248a..0000000000 --- a/CodeAnalysis/osu-framework.ruleset +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props index fb2120b7ad..c82d1e0254 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -27,9 +27,6 @@ - - $(MSBuildThisFileDirectory)CodeAnalysis\osu-framework.ruleset - ppy Pty Ltd ppy Pty Ltd diff --git a/osu-framework.sln b/osu-framework.sln index 0c502829ad..2ee06d1065 100644 --- a/osu-framework.sln +++ b/osu-framework.sln @@ -74,7 +74,6 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CodeAnalysis", "CodeAnalysis", "{50334A1F-990D-45FA-A1FE-C92F1994D97B}" ProjectSection(SolutionItems) = preProject CodeAnalysis\BannedSymbols.txt = CodeAnalysis\BannedSymbols.txt - CodeAnalysis\osu-framework.ruleset = CodeAnalysis\osu-framework.ruleset EndProjectSection EndProject Global From a69c2e066c13634c1f32544100c0df80abd29455 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 27 Nov 2024 22:40:35 +0800 Subject: [PATCH 03/14] Enable recommended documentation rules --- Directory.Build.props | 5 +++++ .../Audio/Mixing/Bass/BassAudioMixer.cs | 18 +++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index c82d1e0254..1ee11abeec 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -27,6 +27,11 @@ + + Default + Default + Recommended + ppy Pty Ltd ppy Pty Ltd diff --git a/osu.Framework/Audio/Mixing/Bass/BassAudioMixer.cs b/osu.Framework/Audio/Mixing/Bass/BassAudioMixer.cs index 07147af650..186c6cb692 100644 --- a/osu.Framework/Audio/Mixing/Bass/BassAudioMixer.cs +++ b/osu.Framework/Audio/Mixing/Bass/BassAudioMixer.cs @@ -186,7 +186,7 @@ public long ChannelGetPosition(IBassAudioChannel channel, PositionFlags mode = P /// How to set the position. /// /// If successful, then is returned, else is returned. - /// Use to get the error code. + /// Use to get the error code. /// public bool ChannelSetPosition(IBassAudioChannel channel, long position, PositionFlags mode = PositionFlags.Bytes) { @@ -222,11 +222,11 @@ public bool ChannelGetLevel(IBassAudioChannel channel, [In, Out] float[] levels, /// See: . /// The to retrieve the data of. /// float[] to write the data to. - /// Number of bytes wanted, and/or . - /// If an error occurs, -1 is returned, use to get the error code. + /// Number of bytes wanted, and/or . + /// If an error occurs, -1 is returned, use to get the error code. /// When requesting FFT data, the number of bytes read from the channel (to perform the FFT) is returned. - /// When requesting sample data, the number of bytes written to buffer will be returned (not necessarily the same as the number of bytes read when using the or DataFlags.Fixed flag). - /// When using the flag, the number of bytes in the channel's buffer is returned. + /// When requesting sample data, the number of bytes written to buffer will be returned (not necessarily the same as the number of bytes read when using the or DataFlags.Fixed flag). + /// When using the flag, the number of bytes in the channel's buffer is returned. /// public int ChannelGetData(IBassAudioChannel channel, float[] buffer, int length) => BassMix.ChannelGetData(channel.Handle, buffer, length); @@ -240,7 +240,7 @@ public int ChannelGetData(IBassAudioChannel channel, float[] buffer, int length) /// The sync parameters, depending on the sync type. /// The callback function which should be invoked with the sync. /// User instance data to pass to the callback function. - /// If successful, then the new synchroniser's handle is returned, else 0 is returned. Use to get the error code. + /// If successful, then the new synchroniser's handle is returned, else 0 is returned. Use to get the error code. public int ChannelSetSync(IBassAudioChannel channel, SyncFlags type, long parameter, SyncProcedure procedure, IntPtr user = default) => BassMix.ChannelSetSync(channel.Handle, type, parameter, procedure, user); @@ -248,8 +248,8 @@ public int ChannelSetSync(IBassAudioChannel channel, SyncFlags type, long parame /// Removes a synchroniser from a mixer source channel. /// /// The to remove the synchroniser for. - /// Handle of the synchroniser to remove (return value of a previous call). - /// If successful, is returned, else is returned. Use to get the error code. + /// Handle of the synchroniser to remove (return value of a previous call). + /// If successful, is returned, else is returned. Use to get the error code. public bool ChannelRemoveSync(IBassAudioChannel channel, int sync) => BassMix.ChannelRemoveSync(channel.Handle, sync); @@ -257,7 +257,7 @@ public bool ChannelRemoveSync(IBassAudioChannel channel, int sync) /// Frees a channel's resources. /// /// The to free. - /// If successful, is returned, else is returned. Use to get the error code. + /// If successful, is returned, else is returned. Use to get the error code. public bool StreamFree(IBassAudioChannel channel) { Remove(channel, false); From 1f604fdc347d946f7e3efe34298927cefcd80144 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 27 Nov 2024 22:55:29 +0800 Subject: [PATCH 04/14] Enable recommended globalization rules --- .globalconfig | 9 +++++++-- Directory.Build.props | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.globalconfig b/.globalconfig index 512f6d45c9..89555f9d4d 100644 --- a/.globalconfig +++ b/.globalconfig @@ -46,12 +46,17 @@ dotnet_diagnostic.IDE0130.severity = warning # IDE1006: Naming style dotnet_diagnostic.IDE1006.severity = warning -# CA1309: Use ordinal string comparison -dotnet_diagnostic.CA1309.severity = warning +# CA1305: Specify IFormatProvider +# Too many noisy warnings for parsing/formatting numbers +dotnet_diagnostic.CA1305.severity = none # CA2007: Consider calling ConfigureAwait on the awaited task dotnet_diagnostic.CA2007.severity = warning +# CA2101: Specify marshaling for P/Invoke string arguments +# Reports warning for all non-UTF16 usages on DllImport; consider migrating to LibraryImport +dotnet_diagnostic.CA2101.severity = none + # CA2201: Do not raise reserved exception types dotnet_diagnostic.CA2201.severity = warning diff --git a/Directory.Build.props b/Directory.Build.props index 1ee11abeec..fa33534d0e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -31,6 +31,7 @@ Default Default Recommended + Recommended ppy Pty Ltd From a91fef22e9898dc8a52e5195e6e95ee8d146f08a Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 27 Nov 2024 23:00:33 +0800 Subject: [PATCH 05/14] Enable recommended interoperability and maintainability rules --- Directory.Build.props | 2 ++ osu.Framework.Tests/IO/TestWebRequest.cs | 4 ++++ osu.Framework/Audio/Track/Waveform.cs | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index fa33534d0e..43c761059d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -32,6 +32,8 @@ Default Recommended Recommended + Recommended + Recommended ppy Pty Ltd diff --git a/osu.Framework.Tests/IO/TestWebRequest.cs b/osu.Framework.Tests/IO/TestWebRequest.cs index 5dc898de18..d2207bed05 100644 --- a/osu.Framework.Tests/IO/TestWebRequest.cs +++ b/osu.Framework.Tests/IO/TestWebRequest.cs @@ -891,7 +891,9 @@ private class HttpBinPostResponse [JsonProperty("json")] public TestObject Json { get; set; } +#pragma warning disable CA1507 // Happens to name the same because of casing preference [JsonProperty("form")] +#pragma warning restore CA1507 private Dictionary form { get; set; } } @@ -906,7 +908,9 @@ private class HttpBinPutResponse [JsonProperty("args")] private Dictionary arguments { get; set; } +#pragma warning disable CA1507 // Happens to name the same because of casing preference [JsonProperty("form")] +#pragma warning restore CA1507 private Dictionary form { get; set; } } diff --git a/osu.Framework/Audio/Track/Waveform.cs b/osu.Framework/Audio/Track/Waveform.cs index e38da672bc..f1f22d0f89 100644 --- a/osu.Framework/Audio/Track/Waveform.cs +++ b/osu.Framework/Audio/Track/Waveform.cs @@ -270,7 +270,7 @@ private float computeIntensity(ChannelInfo info, float[] bins, float startFreque /// An async task for the generation of the . public async Task GenerateResampledAsync(int pointCount, CancellationToken cancellationToken = default) { - if (pointCount < 0) throw new ArgumentOutOfRangeException(nameof(pointCount)); + ArgumentOutOfRangeException.ThrowIfNegative(pointCount); if (pointCount == 0) return new Waveform(null); From f24f39774fd9bb3ce42ba75672cd75c71587150f Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 27 Nov 2024 23:24:49 +0800 Subject: [PATCH 06/14] Enable basic performance rules --- .globalconfig | 24 +++++++++++++++++++ Directory.Build.props | 2 ++ osu.Framework.Tests/Audio/AudioThreadTest.cs | 3 ++- .../Containers/TestSceneContainerState.cs | 2 ++ .../UserInterface/TestSceneTabControl.cs | 2 ++ .../Graphics/Containers/FlowContainer.cs | 4 ++-- .../Graphics/Containers/SearchContainer.cs | 2 +- .../Input/Handlers/Midi/MidiHandler.cs | 4 ++-- 8 files changed, 37 insertions(+), 6 deletions(-) diff --git a/.globalconfig b/.globalconfig index 89555f9d4d..0a25db8388 100644 --- a/.globalconfig +++ b/.globalconfig @@ -50,6 +50,30 @@ dotnet_diagnostic.IDE1006.severity = warning # Too many noisy warnings for parsing/formatting numbers dotnet_diagnostic.CA1305.severity = none +# CA1806: Do not ignore method results +# Not sure why it's performance, but causing too much noise +dotnet_diagnostic.CA1806.severity = none + +# CA1822: Mark members as static +# Potential false positive around reflection/too much noise +dotnet_diagnostic.CA1822.severity = none + +# CA1826: Do not use Enumerable methods on indexable collections +# Noise for FirstOrDefault and LastOrDefault +dotnet_diagnostic.CA1826.severity = none + +# CA1859: Use concrete types when possible for improved performance +# Involves design considerations +dotnet_diagnostic.CA1859.severity = none + +# CA1861: Avoid constant arrays as arguments +# Outdated with collection expressions +dotnet_diagnostic.CA1861.severity = none + +# CA1868: Unnecessary call to 'Contains(item)' +# Causing noises only +dotnet_diagnostic.CA1868.severity = none + # CA2007: Consider calling ConfigureAwait on the awaited task dotnet_diagnostic.CA2007.severity = warning diff --git a/Directory.Build.props b/Directory.Build.props index 43c761059d..015096d9f8 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -34,6 +34,8 @@ Recommended Recommended Recommended + Default + Minimum ppy Pty Ltd diff --git a/osu.Framework.Tests/Audio/AudioThreadTest.cs b/osu.Framework.Tests/Audio/AudioThreadTest.cs index dd85628fd4..5919c34050 100644 --- a/osu.Framework.Tests/Audio/AudioThreadTest.cs +++ b/osu.Framework.Tests/Audio/AudioThreadTest.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using NUnit.Framework; using osu.Framework.Audio.Track; +using osu.Framework.Extensions; using osu.Framework.IO.Stores; using osu.Framework.Threading; @@ -87,7 +88,7 @@ void runScheduled() => thread.Scheduler.Add(() => runScheduled(); - Task.WaitAll(cts.Task); + cts.Task.WaitSafely(); } } } diff --git a/osu.Framework.Tests/Containers/TestSceneContainerState.cs b/osu.Framework.Tests/Containers/TestSceneContainerState.cs index 6d40f21b79..9107e0508e 100644 --- a/osu.Framework.Tests/Containers/TestSceneContainerState.cs +++ b/osu.Framework.Tests/Containers/TestSceneContainerState.cs @@ -16,6 +16,8 @@ using osu.Framework.Tests.Visual; using osuTK; +#pragma warning disable CA1826 // Performance for test is not important + namespace osu.Framework.Tests.Containers { [System.ComponentModel.Description("ensure valid container state in various scenarios")] diff --git a/osu.Framework.Tests/Visual/UserInterface/TestSceneTabControl.cs b/osu.Framework.Tests/Visual/UserInterface/TestSceneTabControl.cs index c83d51df50..f265370e34 100644 --- a/osu.Framework.Tests/Visual/UserInterface/TestSceneTabControl.cs +++ b/osu.Framework.Tests/Visual/UserInterface/TestSceneTabControl.cs @@ -17,6 +17,8 @@ using osu.Framework.Localisation; using osuTK; +#pragma warning disable CA1826 // Performance for test is not important + namespace osu.Framework.Tests.Visual.UserInterface { public partial class TestSceneTabControl : FrameworkTestScene diff --git a/osu.Framework/Graphics/Containers/FlowContainer.cs b/osu.Framework/Graphics/Containers/FlowContainer.cs index 4e78fe87e9..ab0d79287e 100644 --- a/osu.Framework/Graphics/Containers/FlowContainer.cs +++ b/osu.Framework/Graphics/Containers/FlowContainer.cs @@ -137,10 +137,10 @@ public void Insert(int position, T drawable) /// The position of the drawable in the layout. public float GetLayoutPosition(Drawable drawable) { - if (!layoutChildren.ContainsKey(drawable)) + if (!layoutChildren.TryGetValue(drawable, out float value)) throw new InvalidOperationException($"Cannot get layout position of drawable which is not contained within this {nameof(FlowContainer)}."); - return layoutChildren[drawable]; + return value; } protected override bool UpdateChildrenLife() diff --git a/osu.Framework/Graphics/Containers/SearchContainer.cs b/osu.Framework/Graphics/Containers/SearchContainer.cs index e9de9e7e4b..db5322d3b2 100644 --- a/osu.Framework/Graphics/Containers/SearchContainer.cs +++ b/osu.Framework/Graphics/Containers/SearchContainer.cs @@ -79,7 +79,7 @@ public string SearchTerm private LocalisationManager localisation { get; set; } private readonly Cached filterValid = new Cached(); - private readonly ICollection> canBeShownBindables = new List>(); + private readonly List> canBeShownBindables = new List>(); protected override void AddInternal(Drawable drawable) { diff --git a/osu.Framework/Input/Handlers/Midi/MidiHandler.cs b/osu.Framework/Input/Handlers/Midi/MidiHandler.cs index c414f98e06..b388abc18b 100644 --- a/osu.Framework/Input/Handlers/Midi/MidiHandler.cs +++ b/osu.Framework/Input/Handlers/Midi/MidiHandler.cs @@ -182,10 +182,10 @@ private void readEvent(byte[] data, string senderId, ref int i, out byte eventTy // need running status to be interpreted correctly if (statusType <= 0x7F) { - if (!runningStatus.ContainsKey(senderId)) + if (!runningStatus.TryGetValue(senderId, out byte value)) throw new InvalidDataException($"Received running status of sender {senderId}, but no event type was stored"); - eventType = runningStatus[senderId]; + eventType = value; key = statusType; velocity = data[i++]; return; From b531e25646ce4527f43f9ff04b0888ff1495501c Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 27 Nov 2024 23:25:07 +0800 Subject: [PATCH 07/14] Performance improvement for LINQ usages --- .../GeneratorTestHelper.cs | 2 +- osu.Framework/Graphics/Containers/SearchContainer.cs | 2 +- osu.Framework/Graphics/UserInterface/Dropdown.cs | 2 +- osu.Framework/Graphics/UserInterface/TabControl.cs | 9 +++++---- osu.Framework/Graphics/Visualisation/PropertyDisplay.cs | 2 +- osu.Framework/Input/Bindings/KeyBindingContainer.cs | 2 +- osu.Framework/Input/Bindings/KeyCombination.cs | 2 +- osu.Framework/Testing/TestBrowser.cs | 4 ++-- 8 files changed, 13 insertions(+), 12 deletions(-) diff --git a/osu.Framework.SourceGeneration.Tests/GeneratorTestHelper.cs b/osu.Framework.SourceGeneration.Tests/GeneratorTestHelper.cs index 36542417de..5dabc47f9b 100644 --- a/osu.Framework.SourceGeneration.Tests/GeneratorTestHelper.cs +++ b/osu.Framework.SourceGeneration.Tests/GeneratorTestHelper.cs @@ -23,7 +23,7 @@ public static void VerifyZeroDiagnostics(this GeneratorDriverRunResult runResult .Where(d => d.Severity == DiagnosticSeverity.Error) .ToArray(); - if (compilationDiagnostics.Any() || generatorDiagnostics.Any()) + if (compilationDiagnostics.Length > 0 || generatorDiagnostics.Length > 0) { var sb = new StringBuilder(); diff --git a/osu.Framework/Graphics/Containers/SearchContainer.cs b/osu.Framework/Graphics/Containers/SearchContainer.cs index db5322d3b2..b94bcf72fd 100644 --- a/osu.Framework/Graphics/Containers/SearchContainer.cs +++ b/osu.Framework/Graphics/Containers/SearchContainer.cs @@ -121,7 +121,7 @@ protected void Filter() private void performFilter() { string[] terms = (searchTerm ?? string.Empty).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - matchSubTree(this, terms, terms.Any(), allowNonContiguousMatching); + matchSubTree(this, terms, terms.Length > 0, allowNonContiguousMatching); } private bool matchSubTree(Drawable drawable, IReadOnlyList searchTerms, bool searchActive, bool nonContiguousMatching) diff --git a/osu.Framework/Graphics/UserInterface/Dropdown.cs b/osu.Framework/Graphics/UserInterface/Dropdown.cs index daa3af9d36..5a17242c4c 100644 --- a/osu.Framework/Graphics/UserInterface/Dropdown.cs +++ b/osu.Framework/Graphics/UserInterface/Dropdown.cs @@ -701,7 +701,7 @@ protected override bool OnKeyDown(KeyDownEvent e) { var visibleMenuItemsList = VisibleMenuItems.ToList(); - if (visibleMenuItemsList.Any()) + if (visibleMenuItemsList.Count > 0) { var currentPreselected = PreselectedItem; int targetPreselectionIndex = visibleMenuItemsList.IndexOf(currentPreselected); diff --git a/osu.Framework/Graphics/UserInterface/TabControl.cs b/osu.Framework/Graphics/UserInterface/TabControl.cs index e4cc0dad5c..97fd16b8e5 100644 --- a/osu.Framework/Graphics/UserInterface/TabControl.cs +++ b/osu.Framework/Graphics/UserInterface/TabControl.cs @@ -203,8 +203,8 @@ protected override void Update() protected override void LoadComplete() { // Default to first selection in list, if we can - if (firstSelection && SelectFirstTabByDefault && !Current.Disabled && Items.Any()) - Current.Value = Items.First(); + if (firstSelection && SelectFirstTabByDefault && !Current.Disabled && Items.Count > 0) + Current.Value = Items[0]; Current.BindValueChanged(v => { @@ -545,10 +545,11 @@ protected override IEnumerable ComputeLayoutPositions() private void updateChildIfNeeded(TabItem child, bool isVisible) { - if (!tabVisibility.ContainsKey(child) || tabVisibility[child] != isVisible) + if (!tabVisibility.TryGetValue(child, out bool value) || value != isVisible) { TabVisibilityChanged?.Invoke(child, isVisible); - tabVisibility[child] = isVisible; + value = isVisible; + tabVisibility[child] = value; if (isVisible) child.Show(); diff --git a/osu.Framework/Graphics/Visualisation/PropertyDisplay.cs b/osu.Framework/Graphics/Visualisation/PropertyDisplay.cs index 5088de48ae..02eb82e468 100644 --- a/osu.Framework/Graphics/Visualisation/PropertyDisplay.cs +++ b/osu.Framework/Graphics/Visualisation/PropertyDisplay.cs @@ -80,7 +80,7 @@ private void updateProperties(IDrawable source) foreach (var type in source.GetType().EnumerateBaseTypes()) { type.GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly) - .Where(m => m is FieldInfo || (m is PropertyInfo pi && pi.GetMethod != null && !pi.GetIndexParameters().Any())) + .Where(m => m is FieldInfo || (m is PropertyInfo pi && pi.GetMethod != null && pi.GetIndexParameters().Length == 0)) .ForEach(m => allMembers.Add(m)); } diff --git a/osu.Framework/Input/Bindings/KeyBindingContainer.cs b/osu.Framework/Input/Bindings/KeyBindingContainer.cs index 5f0149ded2..8ef1e9bb00 100644 --- a/osu.Framework/Input/Bindings/KeyBindingContainer.cs +++ b/osu.Framework/Input/Bindings/KeyBindingContainer.cs @@ -409,7 +409,7 @@ private List getInputQueue(IKeyBinding binding, bool rebuildIfEmpty = var currentQueue = keyBindingQueues[binding]; - if (rebuildIfEmpty && !currentQueue.Any()) + if (rebuildIfEmpty && currentQueue.Count == 0) currentQueue.AddRange(KeyBindingInputQueue); return currentQueue; diff --git a/osu.Framework/Input/Bindings/KeyCombination.cs b/osu.Framework/Input/Bindings/KeyCombination.cs index f2024456f9..4f30fc504d 100644 --- a/osu.Framework/Input/Bindings/KeyCombination.cs +++ b/osu.Framework/Input/Bindings/KeyCombination.cs @@ -32,7 +32,7 @@ namespace osu.Framework.Input.Bindings /// The keys. public KeyCombination(ICollection? keys) { - if (keys == null || !keys.Any()) + if (keys == null || keys.Count == 0) { Keys = none; return; diff --git a/osu.Framework/Testing/TestBrowser.cs b/osu.Framework/Testing/TestBrowser.cs index 8f5b708471..5f80650af4 100644 --- a/osu.Framework/Testing/TestBrowser.cs +++ b/osu.Framework/Testing/TestBrowser.cs @@ -403,7 +403,7 @@ private void finishLoad(TestScene newTest, Action onCompletion) var methods = newTest.GetType().GetMethods(); var soloTests = methods.Where(m => m.GetCustomAttribute(typeof(SoloAttribute), false) != null).ToArray(); - if (soloTests.Any()) + if (soloTests.Length > 0) methods = soloTests; foreach (var m in methods) @@ -519,7 +519,7 @@ void addSetUpSteps() { var setUpMethods = ReflectionUtils.GetMethodsWithAttribute(newTest.GetType(), typeof(SetUpAttribute), true); - if (setUpMethods.Any()) + if (setUpMethods.Length > 0) { CurrentTest.AddStep(new SingleStepButton { From 3887b9b6f58eacaec07fee16db0e3341f1f8ad69 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 27 Nov 2024 23:31:35 +0800 Subject: [PATCH 08/14] Enable recommended reliability rules --- Directory.Build.props | 1 + .../OpenGL/Buffers/GLShaderStorageBufferObject.cs | 8 ++++---- .../Platform/Windows/WindowsMouseHandler_SDL2.cs | 2 ++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 015096d9f8..02036877a7 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -36,6 +36,7 @@ Recommended Default Minimum + Recommended ppy Pty Ltd diff --git a/osu.Framework/Graphics/OpenGL/Buffers/GLShaderStorageBufferObject.cs b/osu.Framework/Graphics/OpenGL/Buffers/GLShaderStorageBufferObject.cs index ee4e3d88c9..4ab867e34f 100644 --- a/osu.Framework/Graphics/OpenGL/Buffers/GLShaderStorageBufferObject.cs +++ b/osu.Framework/Graphics/OpenGL/Buffers/GLShaderStorageBufferObject.cs @@ -18,7 +18,7 @@ internal class GLShaderStorageBufferObject : IShaderStorageBufferObject Date: Wed, 27 Nov 2024 23:47:03 +0800 Subject: [PATCH 09/14] Selectively opt-in some usage rules --- .globalconfig | 7 +++++-- Directory.Build.props | 2 ++ .../Graphics/Rendering/Deferred/DeferredVertexBatch.cs | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.globalconfig b/.globalconfig index 0a25db8388..734abab03f 100644 --- a/.globalconfig +++ b/.globalconfig @@ -84,8 +84,11 @@ dotnet_diagnostic.CA2101.severity = none # CA2201: Do not raise reserved exception types dotnet_diagnostic.CA2201.severity = warning -#Disable operator overloads requiring alternate named methods -dotnet_diagnostic.CA2225.severity = none +# CA2208: Instantiate argument exceptions correctly +dotnet_diagnostic.CA2208.severity = warning + +# CA2242: Test for NaN correctly +dotnet_diagnostic.CA2242.severity = warning # Banned APIs dotnet_diagnostic.RS0030.severity = error diff --git a/Directory.Build.props b/Directory.Build.props index 02036877a7..2d79ed8366 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -37,6 +37,8 @@ Default Minimum Recommended + Default + Default ppy Pty Ltd diff --git a/osu.Framework/Graphics/Rendering/Deferred/DeferredVertexBatch.cs b/osu.Framework/Graphics/Rendering/Deferred/DeferredVertexBatch.cs index d49940663c..e5f6fac8d7 100644 --- a/osu.Framework/Graphics/Rendering/Deferred/DeferredVertexBatch.cs +++ b/osu.Framework/Graphics/Rendering/Deferred/DeferredVertexBatch.cs @@ -54,7 +54,7 @@ public DeferredVertexBatch(DeferredRenderer renderer, PrimitiveTopology topology throw new NotImplementedException($"Topology '{topology}' is not yet implemented for this renderer."); default: - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(topology)); } } else From 2b68a6a7f37db933ddaaf4298f365251f528339a Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 27 Nov 2024 23:53:16 +0800 Subject: [PATCH 10/14] Remove banned ToUpper and ToLower covered by CA1304 --- CodeAnalysis/BannedSymbols.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CodeAnalysis/BannedSymbols.txt b/CodeAnalysis/BannedSymbols.txt index a0a86a2fe5..4f7ad3837c 100644 --- a/CodeAnalysis/BannedSymbols.txt +++ b/CodeAnalysis/BannedSymbols.txt @@ -8,8 +8,4 @@ M:System.Threading.Tasks.Task.Wait();Don't use Task.Wait. Use Task.WaitSafely() M:System.Guid.#ctor;Probably meaning to use Guid.NewGuid() instead. If actually wanting empty, use Guid.Empty. P:System.Threading.Tasks.Task`1.Result;Don't use Task.Result. Use Task.GetResultSafely() to ensure we avoid deadlocks. M:System.Threading.ManualResetEventSlim.Wait();Specify a timeout to avoid waiting forever. -M:System.Char.ToLower(System.Char);char.ToLower() changes behaviour depending on CultureInfo.CurrentCulture. Use char.ToLowerInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture. -M:System.Char.ToUpper(System.Char);char.ToUpper() changes behaviour depending on CultureInfo.CurrentCulture. Use char.ToUpperInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture. -M:System.String.ToLower();string.ToLower() changes behaviour depending on CultureInfo.CurrentCulture. Use string.ToLowerInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture or use LocalisableString. -M:System.String.ToUpper();string.ToUpper() changes behaviour depending on CultureInfo.CurrentCulture. Use string.ToUpperInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture or use LocalisableString. M:System.Reflection.Assembly.GetEntryAssembly();Use osu.Framework.RuntimeInfo.EntryAssembly instead From ab9552b4cd9a5c2bfc46efa11330d8a58ae9e929 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Thu, 28 Nov 2024 00:21:36 +0800 Subject: [PATCH 11/14] Revert change for rule non being enabled --- osu.Framework/Graphics/Containers/SearchContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Framework/Graphics/Containers/SearchContainer.cs b/osu.Framework/Graphics/Containers/SearchContainer.cs index b94bcf72fd..47edd3dd61 100644 --- a/osu.Framework/Graphics/Containers/SearchContainer.cs +++ b/osu.Framework/Graphics/Containers/SearchContainer.cs @@ -79,7 +79,7 @@ public string SearchTerm private LocalisationManager localisation { get; set; } private readonly Cached filterValid = new Cached(); - private readonly List> canBeShownBindables = new List>(); + private readonly ICollection> canBeShownBindables = new List>(); protected override void AddInternal(Drawable drawable) { From a80a4bea403ed39a95587f06a1bfa304a5f5272b Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Thu, 28 Nov 2024 12:20:48 +0800 Subject: [PATCH 12/14] Improve fixed code --- osu.Framework/Graphics/UserInterface/TabControl.cs | 5 ++--- osu.Framework/Input/Handlers/Midi/MidiHandler.cs | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Framework/Graphics/UserInterface/TabControl.cs b/osu.Framework/Graphics/UserInterface/TabControl.cs index 97fd16b8e5..1ed586dd38 100644 --- a/osu.Framework/Graphics/UserInterface/TabControl.cs +++ b/osu.Framework/Graphics/UserInterface/TabControl.cs @@ -545,11 +545,10 @@ protected override IEnumerable ComputeLayoutPositions() private void updateChildIfNeeded(TabItem child, bool isVisible) { - if (!tabVisibility.TryGetValue(child, out bool value) || value != isVisible) + if (!tabVisibility.TryGetValue(child, out bool currentVisibility) || currentVisibility != isVisible) { TabVisibilityChanged?.Invoke(child, isVisible); - value = isVisible; - tabVisibility[child] = value; + tabVisibility[child] = isVisible; if (isVisible) child.Show(); diff --git a/osu.Framework/Input/Handlers/Midi/MidiHandler.cs b/osu.Framework/Input/Handlers/Midi/MidiHandler.cs index b388abc18b..41f8cc8d4d 100644 --- a/osu.Framework/Input/Handlers/Midi/MidiHandler.cs +++ b/osu.Framework/Input/Handlers/Midi/MidiHandler.cs @@ -182,10 +182,9 @@ private void readEvent(byte[] data, string senderId, ref int i, out byte eventTy // need running status to be interpreted correctly if (statusType <= 0x7F) { - if (!runningStatus.TryGetValue(senderId, out byte value)) + if (!runningStatus.TryGetValue(senderId, out eventType)) throw new InvalidDataException($"Received running status of sender {senderId}, but no event type was stored"); - eventType = value; key = statusType; velocity = data[i++]; return; From 7a0d565ff24b45422f5f3173170f5f710c72804d Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Wed, 27 Nov 2024 23:06:41 -0500 Subject: [PATCH 13/14] Fix android workflow not installing .NET 8 version --- .github/workflows/ci.yml | 5 ++++- .github/workflows/deploy-pack.yml | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5905850e62..468117f5a2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -122,7 +122,10 @@ jobs: dotnet-version: "8.0.x" - name: Restore .NET workloads - run: dotnet workload install android + # since windows image 20241113.3.0, not specifying a version here + # installs the .NET 7 version of android workload for very unknown reasons. + # revisit once we upgrade to .NET 9, it's probably fixed there. + run: dotnet workload install android --version (dotnet --version) - name: Compile run: dotnet build -c Debug osu-framework.Android.slnf diff --git a/.github/workflows/deploy-pack.yml b/.github/workflows/deploy-pack.yml index 88bc62e7ac..d6055760d9 100644 --- a/.github/workflows/deploy-pack.yml +++ b/.github/workflows/deploy-pack.yml @@ -131,7 +131,10 @@ jobs: java-version: 11 - name: Restore .NET workloads - run: dotnet workload install android + # since windows image 20241113.3.0, not specifying a version here + # installs the .NET 7 version of android workload for very unknown reasons. + # revisit once we upgrade to .NET 9, it's probably fixed there. + run: dotnet workload install android --version (dotnet --version) - name: Pack (Android Framework) run: dotnet pack -c Release osu.Framework.Android /p:Version=${{ github.ref_name }} /p:GenerateDocumentationFile=true -o ${{steps.artifactsPath.outputs.nuget_artifacts}} From eafbed41be638828512c1822e0d45984222dfdb2 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 28 Nov 2024 18:06:05 +0900 Subject: [PATCH 14/14] Update SDL3 package --- .../osu.Framework.Android.csproj | 2 +- .../Platform/SDL3/SDL3GraphicsSurface.cs | 24 +++++++++---------- osu.Framework/osu.Framework.csproj | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/osu.Framework.Android/osu.Framework.Android.csproj b/osu.Framework.Android/osu.Framework.Android.csproj index 54e6baf66a..77975c826d 100644 --- a/osu.Framework.Android/osu.Framework.Android.csproj +++ b/osu.Framework.Android/osu.Framework.Android.csproj @@ -18,7 +18,7 @@ - + diff --git a/osu.Framework/Platform/SDL3/SDL3GraphicsSurface.cs b/osu.Framework/Platform/SDL3/SDL3GraphicsSurface.cs index 590c79b504..2091e70451 100644 --- a/osu.Framework/Platform/SDL3/SDL3GraphicsSurface.cs +++ b/osu.Framework/Platform/SDL3/SDL3GraphicsSurface.cs @@ -33,12 +33,12 @@ public SDL3GraphicsSurface(SDL3Window window, GraphicsSurfaceType surfaceType) switch (surfaceType) { case GraphicsSurfaceType.OpenGL: - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_RED_SIZE, 8); - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_GREEN_SIZE, 8); - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_BLUE_SIZE, 8); - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_ACCUM_ALPHA_SIZE, 0); - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_DEPTH_SIZE, 16); - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_STENCIL_SIZE, 8); + SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_ACCUM_ALPHA_SIZE, 0); + SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_DEPTH_SIZE, 16); + SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_STENCIL_SIZE, 8); break; case GraphicsSurfaceType.Vulkan: @@ -70,19 +70,19 @@ private void initialiseOpenGL() { if (RuntimeInfo.IsMobile) { - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, (int)SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_PROFILE_MASK, (int)SDL_GLProfile.SDL_GL_CONTEXT_PROFILE_ES); // Minimum OpenGL version for ES profile: - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, 0); + SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_MINOR_VERSION, 0); } else { - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, (int)SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_PROFILE_MASK, (int)SDL_GLProfile.SDL_GL_CONTEXT_PROFILE_CORE); // Minimum OpenGL version for core profile: - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_MINOR_VERSION, 2); } context = SDL_GL_CreateContext(window.SDLWindowHandle); diff --git a/osu.Framework/osu.Framework.csproj b/osu.Framework/osu.Framework.csproj index c4814789f0..32889ad1fc 100644 --- a/osu.Framework/osu.Framework.csproj +++ b/osu.Framework/osu.Framework.csproj @@ -38,7 +38,7 @@ - +