diff --git a/.globalconfig b/.globalconfig
index 0a2727ecd4..734abab03f 100644
--- a/.globalconfig
+++ b/.globalconfig
@@ -46,8 +46,49 @@ dotnet_diagnostic.IDE0130.severity = warning
# IDE1006: Naming style
dotnet_diagnostic.IDE1006.severity = warning
-#Disable operator overloads requiring alternate named methods
-dotnet_diagnostic.CA2225.severity = none
+# CA1305: Specify IFormatProvider
+# 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
+
+# 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
+
+# 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/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
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..2d79ed8366 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -28,7 +28,17 @@
- $(MSBuildThisFileDirectory)CodeAnalysis\osu-framework.ruleset
+ Default
+ Default
+ Recommended
+ Recommended
+ Recommended
+ Recommended
+ Default
+ Minimum
+ Recommended
+ Default
+ Default
ppy Pty Ltd
diff --git a/osu-framework.sln b/osu-framework.sln
index 155884055c..2ee06d1065 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
@@ -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
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.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/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.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/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);
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);
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..47edd3dd61 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/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 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..1ed586dd38 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,7 +545,7 @@ protected override IEnumerable ComputeLayoutPositions()
private void updateChildIfNeeded(TabItem child, bool isVisible)
{
- if (!tabVisibility.ContainsKey(child) || tabVisibility[child] != isVisible)
+ if (!tabVisibility.TryGetValue(child, out bool currentVisibility) || currentVisibility != isVisible)
{
TabVisibilityChanged?.Invoke(child, isVisible);
tabVisibility[child] = isVisible;
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/Input/Handlers/Midi/MidiHandler.cs b/osu.Framework/Input/Handlers/Midi/MidiHandler.cs
index c414f98e06..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.ContainsKey(senderId))
+ if (!runningStatus.TryGetValue(senderId, out eventType))
throw new InvalidDataException($"Received running status of sender {senderId}, but no event type was stored");
- eventType = runningStatus[senderId];
key = statusType;
velocity = data[i++];
return;
diff --git a/osu.Framework/Platform/Windows/WindowsMouseHandler_SDL2.cs b/osu.Framework/Platform/Windows/WindowsMouseHandler_SDL2.cs
index 2d941ec173..cf568ee19d 100644
--- a/osu.Framework/Platform/Windows/WindowsMouseHandler_SDL2.cs
+++ b/osu.Framework/Platform/Windows/WindowsMouseHandler_SDL2.cs
@@ -65,7 +65,9 @@ private unsafe IntPtr onWndProcSDL2(IntPtr userData, IntPtr hWnd, uint message,
int payloadSize = sizeof(RawInputData);
+#pragma warning disable CA2020 // Prevent behavioral change for IntPtr conversion
Native.Input.GetRawInputData((IntPtr)lParam, RawInputCommand.Input, out var data, ref payloadSize, sizeof(RawInputHeader));
+#pragma warning restore CA2020
if (data.Header.Type != RawInputType.Mouse)
return IntPtr.Zero;
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
{