From 1d7a93c2e0152a15c9fe9ec5cb0dad218f738d7b Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Wed, 4 Nov 2020 03:56:26 +0000 Subject: [PATCH] Enable CA1825: Avoid zero-length array allocations (#13961) --- .globalconfig | 2 +- docs/dev-process/coding-guidelines.md | 3 -- .../utility/ImplicitRemotingCommands.cs | 2 +- .../commands/utility/MatchString.cs | 8 +-- .../commands/utility/New-Object.cs | 2 +- .../engine/EventManager.cs | 2 +- .../engine/LanguagePrimitives.cs | 2 +- .../engine/Modules/ModuleCmdletBase.cs | 2 +- .../engine/ParameterBinderBase.cs | 2 +- .../engine/PseudoParameters.cs | 2 +- .../engine/hostifaces/LocalConnection.cs | 2 +- .../remoting/host/RemoteHostMethodInfo.cs | 54 +++++++++---------- .../engine/runtime/Operations/ArrayOps.cs | 5 +- .../engine/runtime/Operations/MiscOps.cs | 5 +- .../namespaces/RegistryProvider.cs | 4 +- .../utils/GraphicalHostReflectionWrapper.cs | 8 +-- test/xUnit/csharp/test_CommandLineParser.cs | 6 +-- 17 files changed, 57 insertions(+), 54 deletions(-) diff --git a/.globalconfig b/.globalconfig index 33d6fe31e7..01f154f0b2 100644 --- a/.globalconfig +++ b/.globalconfig @@ -286,7 +286,7 @@ dotnet_diagnostic.CA1823.severity = none dotnet_diagnostic.CA1824.severity = suggestion # CA1825: Avoid zero-length array allocations -dotnet_diagnostic.CA1825.severity = suggestion +dotnet_diagnostic.CA1825.severity = warning # CA1826: Do not use Enumerable methods on indexable collections dotnet_diagnostic.CA1826.severity = suggestion diff --git a/docs/dev-process/coding-guidelines.md b/docs/dev-process/coding-guidelines.md index 3472296251..dab26aa5bf 100644 --- a/docs/dev-process/coding-guidelines.md +++ b/docs/dev-process/coding-guidelines.md @@ -111,9 +111,6 @@ Some general guidelines: * Avoid using string interpolations and overloads with implicit parameters such as `Culture` and `StringComparison`. Instead, use overloads with more explicit parameters such as `String.Format(IFormatProvider, String, Object[])` and `Equals(String, String, StringComparison)`. -* Avoid creating empty arrays. - Instead, reuse the static ones via `Utils.EmptyArray`. - * Avoid unnecessary memory allocation in a loop. Move the memory allocation outside the loop if possible. diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImplicitRemotingCommands.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImplicitRemotingCommands.cs index e8c95c484c..120320ee71 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImplicitRemotingCommands.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImplicitRemotingCommands.cs @@ -480,7 +480,7 @@ namespace Microsoft.PowerShell.Commands } } - private ModuleSpecification[] _moduleSpecifications = new ModuleSpecification[0]; + private ModuleSpecification[] _moduleSpecifications = Array.Empty(); internal bool IsFullyQualifiedModuleSpecified = false; private bool _commandParameterSpecified; // initialized to default value in the constructor diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MatchString.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MatchString.cs index 667f55e50c..155a134ca7 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MatchString.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MatchString.cs @@ -366,7 +366,7 @@ namespace Microsoft.PowerShell.Commands /// /// Gets or sets a list of all Regex matches on the matching line. /// - public Match[] Matches { get; set; } = new Match[] { }; + public Match[] Matches { get; set; } = Array.Empty(); /// /// Create a deep copy of this MatchInfo instance. @@ -1897,8 +1897,8 @@ namespace Microsoft.PowerShell.Commands if (matchInfo.Context != null) { matchResult = matchInfo.Clone(); - matchResult.Context.DisplayPreContext = new string[] { }; - matchResult.Context.DisplayPostContext = new string[] { }; + matchResult.Context.DisplayPreContext = Array.Empty(); + matchResult.Context.DisplayPostContext = Array.Empty(); } else { @@ -1924,7 +1924,7 @@ namespace Microsoft.PowerShell.Commands // Matches should be an empty list, rather than null, // in the cases of notMatch and simpleMatch. - matchResult.Matches = matches ?? new Match[] { }; + matchResult.Matches = matches ?? Array.Empty(); return true; } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/New-Object.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/New-Object.cs index 39fd390b1c..a8b4b69aa1 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/New-Object.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/New-Object.cs @@ -207,7 +207,7 @@ namespace Microsoft.PowerShell.Commands ConstructorInfo ci = type.GetConstructor(Type.EmptyTypes); if (ci != null && ci.IsPublic) { - _newObject = CallConstructor(type, new ConstructorInfo[] { ci }, new object[] { }); + _newObject = CallConstructor(type, new ConstructorInfo[] { ci }, Array.Empty()); if (_newObject != null && Property != null) { // The method invocation is disabled for "Hashtable to Object conversion" (Win8:649519), but we need to keep it enabled for New-Object for compatibility to PSv2 diff --git a/src/System.Management.Automation/engine/EventManager.cs b/src/System.Management.Automation/engine/EventManager.cs index 012f5c0857..b44d99eb3e 100644 --- a/src/System.Management.Automation/engine/EventManager.cs +++ b/src/System.Management.Automation/engine/EventManager.cs @@ -599,7 +599,7 @@ namespace System.Management.Automation if (_engineEventSubscribers.TryGetValue(PSEngineEvent.OnIdle, out subscribers) && subscribers.Count > 0) { // We send out on-idle event and keep enabling the timer only if there still are subscribers to the on-idle event - GenerateEvent(PSEngineEvent.OnIdle, null, new object[] { }, null, false, false); + GenerateEvent(PSEngineEvent.OnIdle, null, Array.Empty(), null, false, false); EnableTimer(); } else diff --git a/src/System.Management.Automation/engine/LanguagePrimitives.cs b/src/System.Management.Automation/engine/LanguagePrimitives.cs index 5929a5637e..a7c1f8c2e1 100644 --- a/src/System.Management.Automation/engine/LanguagePrimitives.cs +++ b/src/System.Management.Automation/engine/LanguagePrimitives.cs @@ -384,7 +384,7 @@ namespace System.Management.Automation emitter.Emit(OpCodes.Ldarg_0); emitter.Emit(OpCodes.Castclass, _enumerableType); - MethodInfo methodInfo = _enumerableType.GetMethod("GetEnumerator", new Type[] { }); + MethodInfo methodInfo = _enumerableType.GetMethod("GetEnumerator", Array.Empty()); emitter.Emit(OpCodes.Callvirt, methodInfo); emitter.Emit(OpCodes.Ret); } diff --git a/src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs b/src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs index a70ac75dfc..b2967ded1a 100644 --- a/src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs +++ b/src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs @@ -6882,7 +6882,7 @@ namespace Microsoft.PowerShell.Commands iss.Bind(Context, updateOnly: true, module, options.NoClobber, options.Local, setLocation: false); // Scan all of the types in the assembly to register JobSourceAdapters. - IEnumerable allTypes = new Type[] { }; + IEnumerable allTypes = Array.Empty(); if (assembly != null) { allTypes = assembly.ExportedTypes; diff --git a/src/System.Management.Automation/engine/ParameterBinderBase.cs b/src/System.Management.Automation/engine/ParameterBinderBase.cs index ddf8072b94..b3b56ec614 100644 --- a/src/System.Management.Automation/engine/ParameterBinderBase.cs +++ b/src/System.Management.Automation/engine/ParameterBinderBase.cs @@ -1559,7 +1559,7 @@ namespace System.Management.Automation toType, 0, null, - new object[] { }, + Array.Empty(), System.Globalization.CultureInfo.InvariantCulture); if (collectionTypeInformation.ParameterCollectionType == ParameterCollectionType.IList) resultAsIList = (IList)resultCollection; diff --git a/src/System.Management.Automation/engine/PseudoParameters.cs b/src/System.Management.Automation/engine/PseudoParameters.cs index 78ef034cf5..97b1d60e8a 100644 --- a/src/System.Management.Automation/engine/PseudoParameters.cs +++ b/src/System.Management.Automation/engine/PseudoParameters.cs @@ -236,6 +236,6 @@ namespace System.Management.Automation /// public object Data { get; set; } - internal static readonly RuntimeDefinedParameter[] EmptyParameterArray = new RuntimeDefinedParameter[0]; + internal static readonly RuntimeDefinedParameter[] EmptyParameterArray = Array.Empty(); } } diff --git a/src/System.Management.Automation/engine/hostifaces/LocalConnection.cs b/src/System.Management.Automation/engine/hostifaces/LocalConnection.cs index 92740acf8d..61b6301574 100644 --- a/src/System.Management.Automation/engine/hostifaces/LocalConnection.cs +++ b/src/System.Management.Automation/engine/hostifaces/LocalConnection.cs @@ -851,7 +851,7 @@ namespace System.Management.Automation.Runspaces // Generate the shutdown event if (Events != null) - Events.GenerateEvent(PSEngineEvent.Exiting, null, new object[] { }, null, true, false); + Events.GenerateEvent(PSEngineEvent.Exiting, null, Array.Empty(), null, true, false); // Stop all running pipelines // Note:Do not perform the Cancel in lock. Reason is diff --git a/src/System.Management.Automation/engine/remoting/host/RemoteHostMethodInfo.cs b/src/System.Management.Automation/engine/remoting/host/RemoteHostMethodInfo.cs index 63185ebc13..e24f8961b9 100644 --- a/src/System.Management.Automation/engine/remoting/host/RemoteHostMethodInfo.cs +++ b/src/System.Management.Automation/engine/remoting/host/RemoteHostMethodInfo.cs @@ -144,35 +144,35 @@ namespace System.Management.Automation.Remoting typeof(PSHost), "get_Name", typeof(string), - new Type[] { }); + Array.Empty()); case RemoteHostMethodId.GetVersion: return new RemoteHostMethodInfo( typeof(PSHost), "get_Version", typeof(Version), - new Type[] { }); + Array.Empty()); case RemoteHostMethodId.GetInstanceId: return new RemoteHostMethodInfo( typeof(PSHost), "get_InstanceId", typeof(Guid), - new Type[] { }); + Array.Empty()); case RemoteHostMethodId.GetCurrentCulture: return new RemoteHostMethodInfo( typeof(PSHost), "get_CurrentCulture", typeof(System.Globalization.CultureInfo), - new Type[] { }); + Array.Empty()); case RemoteHostMethodId.GetCurrentUICulture: return new RemoteHostMethodInfo( typeof(PSHost), "get_CurrentUICulture", typeof(System.Globalization.CultureInfo), - new Type[] { }); + Array.Empty()); // Host methods. @@ -188,28 +188,28 @@ namespace System.Management.Automation.Remoting typeof(PSHost), "EnterNestedPrompt", typeof(void), - new Type[] { }); + Array.Empty()); case RemoteHostMethodId.ExitNestedPrompt: return new RemoteHostMethodInfo( typeof(PSHost), "ExitNestedPrompt", typeof(void), - new Type[] { }); + Array.Empty()); case RemoteHostMethodId.NotifyBeginApplication: return new RemoteHostMethodInfo( typeof(PSHost), "NotifyBeginApplication", typeof(void), - new Type[] { }); + Array.Empty()); case RemoteHostMethodId.NotifyEndApplication: return new RemoteHostMethodInfo( typeof(PSHost), "NotifyEndApplication", typeof(void), - new Type[] { }); + Array.Empty()); // Host UI methods. @@ -218,14 +218,14 @@ namespace System.Management.Automation.Remoting typeof(PSHostUserInterface), "ReadLine", typeof(string), - new Type[] { }); + Array.Empty()); case RemoteHostMethodId.ReadLineAsSecureString: return new RemoteHostMethodInfo( typeof(PSHostUserInterface), "ReadLineAsSecureString", typeof(System.Security.SecureString), - new Type[] { }); + Array.Empty()); case RemoteHostMethodId.Write1: return new RemoteHostMethodInfo( @@ -246,7 +246,7 @@ namespace System.Management.Automation.Remoting typeof(PSHostUserInterface), "WriteLine", typeof(void), - new Type[] { }); + Array.Empty()); case RemoteHostMethodId.WriteLine2: return new RemoteHostMethodInfo( @@ -339,7 +339,7 @@ namespace System.Management.Automation.Remoting typeof(PSHostRawUserInterface), "get_ForegroundColor", typeof(ConsoleColor), - new Type[] { }); + Array.Empty()); case RemoteHostMethodId.SetForegroundColor: return new RemoteHostMethodInfo( @@ -353,7 +353,7 @@ namespace System.Management.Automation.Remoting typeof(PSHostRawUserInterface), "get_BackgroundColor", typeof(ConsoleColor), - new Type[] { }); + Array.Empty()); case RemoteHostMethodId.SetBackgroundColor: return new RemoteHostMethodInfo( @@ -367,7 +367,7 @@ namespace System.Management.Automation.Remoting typeof(PSHostRawUserInterface), "get_CursorPosition", typeof(Coordinates), - new Type[] { }); + Array.Empty()); case RemoteHostMethodId.SetCursorPosition: return new RemoteHostMethodInfo( @@ -381,7 +381,7 @@ namespace System.Management.Automation.Remoting typeof(PSHostRawUserInterface), "get_WindowPosition", typeof(Coordinates), - new Type[] { }); + Array.Empty()); case RemoteHostMethodId.SetWindowPosition: return new RemoteHostMethodInfo( @@ -395,7 +395,7 @@ namespace System.Management.Automation.Remoting typeof(PSHostRawUserInterface), "get_CursorSize", typeof(int), - new Type[] { }); + Array.Empty()); case RemoteHostMethodId.SetCursorSize: return new RemoteHostMethodInfo( @@ -409,7 +409,7 @@ namespace System.Management.Automation.Remoting typeof(PSHostRawUserInterface), "get_BufferSize", typeof(Size), - new Type[] { }); + Array.Empty()); case RemoteHostMethodId.SetBufferSize: return new RemoteHostMethodInfo( @@ -423,7 +423,7 @@ namespace System.Management.Automation.Remoting typeof(PSHostRawUserInterface), "get_WindowSize", typeof(Size), - new Type[] { }); + Array.Empty()); case RemoteHostMethodId.SetWindowSize: return new RemoteHostMethodInfo( @@ -437,7 +437,7 @@ namespace System.Management.Automation.Remoting typeof(PSHostRawUserInterface), "get_WindowTitle", typeof(string), - new Type[] { }); + Array.Empty()); case RemoteHostMethodId.SetWindowTitle: return new RemoteHostMethodInfo( @@ -453,21 +453,21 @@ namespace System.Management.Automation.Remoting typeof(PSHostRawUserInterface), "get_MaxWindowSize", typeof(Size), - new Type[] { }); + Array.Empty()); case RemoteHostMethodId.GetMaxPhysicalWindowSize: return new RemoteHostMethodInfo( typeof(PSHostRawUserInterface), "get_MaxPhysicalWindowSize", typeof(Size), - new Type[] { }); + Array.Empty()); case RemoteHostMethodId.GetKeyAvailable: return new RemoteHostMethodInfo( typeof(PSHostRawUserInterface), "get_KeyAvailable", typeof(bool), - new Type[] { }); + Array.Empty()); // Host raw UI methods. @@ -483,7 +483,7 @@ namespace System.Management.Automation.Remoting typeof(PSHostRawUserInterface), "FlushInputBuffer", typeof(void), - new Type[] { }); + Array.Empty()); case RemoteHostMethodId.SetBufferContents1: return new RemoteHostMethodInfo( @@ -527,7 +527,7 @@ namespace System.Management.Automation.Remoting typeof(IHostSupportsInteractiveSession), "PopRunspace", typeof(void), - new Type[] { }); + Array.Empty()); // IHostSupportsInteractiveSession properties. @@ -536,14 +536,14 @@ namespace System.Management.Automation.Remoting typeof(IHostSupportsInteractiveSession), "get_IsRunspacePushed", typeof(bool), - new Type[] { }); + Array.Empty()); case RemoteHostMethodId.GetRunspace: return new RemoteHostMethodInfo( typeof(IHostSupportsInteractiveSession), "get_Runspace", typeof(System.Management.Automation.Runspaces.Runspace), - new Type[] { }); + Array.Empty()); default: Dbg.Assert(false, "All RemoteHostMethodId's should be handled. This code should not be reached."); diff --git a/src/System.Management.Automation/engine/runtime/Operations/ArrayOps.cs b/src/System.Management.Automation/engine/runtime/Operations/ArrayOps.cs index 5ce3b857c3..f6b71fad1e 100644 --- a/src/System.Management.Automation/engine/runtime/Operations/ArrayOps.cs +++ b/src/System.Management.Automation/engine/runtime/Operations/ArrayOps.cs @@ -52,7 +52,10 @@ namespace System.Management.Automation if (times == 0 || array.Length == 0) { - return new T[0]; // don't use Utils.EmptyArray, always return a new array +#pragma warning disable CA1825 // Avoid zero-length array allocations + // Don't use Array.Empty(); always return a new instance. + return new T[0]; +#pragma warning restore CA1825 // Avoid zero-length array allocations } var context = LocalPipeline.GetExecutionContextFromTLS(); diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs index dc4a8fbe20..ea80c64dbe 100644 --- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs +++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs @@ -3228,7 +3228,10 @@ namespace System.Management.Automation if (originalList.Count == 0) { - return new object[0]; // don't use Utils.EmptyArray, always return a new array +#pragma warning disable CA1825 // Avoid zero-length array allocations + // Don't use Array.Empty(); always return a new instance. + return new object[0]; +#pragma warning restore CA1825 // Avoid zero-length array allocations } return ArrayOps.Multiply(originalList.ToArray(), times); diff --git a/src/System.Management.Automation/namespaces/RegistryProvider.cs b/src/System.Management.Automation/namespaces/RegistryProvider.cs index bccbe80ab2..bac9796e0f 100644 --- a/src/System.Management.Automation/namespaces/RegistryProvider.cs +++ b/src/System.Management.Automation/namespaces/RegistryProvider.cs @@ -3814,7 +3814,7 @@ namespace Microsoft.PowerShell.Commands value, typeof(byte[]), CultureInfo.CurrentCulture) - : new byte[] { }; + : Array.Empty(); break; case RegistryValueKind.DWord: @@ -3851,7 +3851,7 @@ namespace Microsoft.PowerShell.Commands value, typeof(string[]), CultureInfo.CurrentCulture) - : new string[] { }; + : Array.Empty(); break; case RegistryValueKind.QWord: diff --git a/src/System.Management.Automation/utils/GraphicalHostReflectionWrapper.cs b/src/System.Management.Automation/utils/GraphicalHostReflectionWrapper.cs index 7579245980..f38bdb18f8 100644 --- a/src/System.Management.Automation/utils/GraphicalHostReflectionWrapper.cs +++ b/src/System.Management.Automation/utils/GraphicalHostReflectionWrapper.cs @@ -128,12 +128,12 @@ namespace System.Management.Automation.Internal ConstructorInfo constructor = returnValue._graphicalHostHelperType.GetConstructor( BindingFlags.NonPublic | BindingFlags.Instance, null, - new Type[] { }, + Array.Empty(), null); if (constructor != null) { - returnValue._graphicalHostHelperObject = constructor.Invoke(new object[] { }); + returnValue._graphicalHostHelperObject = constructor.Invoke(Array.Empty()); Diagnostics.Assert(returnValue._graphicalHostHelperObject != null, "the constructor does not throw anything"); } @@ -187,7 +187,7 @@ namespace System.Management.Automation.Internal Diagnostics.Assert(_graphicalHostHelperObject != null, "there should be a constructor in order to get an instance property value"); PropertyInfo property = _graphicalHostHelperType.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance); Diagnostics.Assert(property != null, "property " + propertyName + " exists in graphicalHostHelperType is verified by caller"); - return property.GetValue(_graphicalHostHelperObject, new object[] { }); + return property.GetValue(_graphicalHostHelperObject, Array.Empty()); } /// @@ -199,7 +199,7 @@ namespace System.Management.Automation.Internal { PropertyInfo property = _graphicalHostHelperType.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Static); Diagnostics.Assert(property != null, "property " + propertyName + " exists in graphicalHostHelperType is verified by caller"); - return property.GetValue(null, new object[] { }); + return property.GetValue(null, Array.Empty()); } /// diff --git a/test/xUnit/csharp/test_CommandLineParser.cs b/test/xUnit/csharp/test_CommandLineParser.cs index 2c44d9aa85..f52fcdf2fe 100644 --- a/test/xUnit/csharp/test_CommandLineParser.cs +++ b/test/xUnit/csharp/test_CommandLineParser.cs @@ -19,7 +19,7 @@ namespace PSTests.Parallel { var cpp = new CommandLineParameterParser(); - cpp.Parse(new string[0]); + cpp.Parse(System.Array.Empty()); Assert.False(cpp.AbortStartup); Assert.Empty(cpp.Args); @@ -60,9 +60,9 @@ namespace PSTests.Parallel { var cpp = new CommandLineParameterParser(); - cpp.Parse(new string[0]); + cpp.Parse(System.Array.Empty()); - Assert.Throws(() => cpp.Parse(new string[0])); + Assert.Throws(() => cpp.Parse(System.Array.Empty())); } [Theory]