diff --git a/eng/CodeAnalysis.src.globalconfig b/eng/CodeAnalysis.src.globalconfig index 26c9d2e8cf7..d06bf337e23 100644 --- a/eng/CodeAnalysis.src.globalconfig +++ b/eng/CodeAnalysis.src.globalconfig @@ -489,6 +489,9 @@ dotnet_diagnostic.CA1868.severity = warning # CA1869: Cache and reuse 'JsonSerializerOptions' instances dotnet_diagnostic.CA1869.severity = warning +# CA1870: Use a cached 'SearchValues' instance +dotnet_diagnostic.CA1870.severity = warning + # CA2000: Dispose objects before losing scope dotnet_diagnostic.CA2000.severity = none diff --git a/eng/CodeAnalysis.test.globalconfig b/eng/CodeAnalysis.test.globalconfig index 0a50a68d677..76afd46a0b7 100644 --- a/eng/CodeAnalysis.test.globalconfig +++ b/eng/CodeAnalysis.test.globalconfig @@ -486,6 +486,9 @@ dotnet_diagnostic.CA1868.severity = none # CA1869: Cache and reuse 'JsonSerializerOptions' instances dotnet_diagnostic.CA1869.severity = none +# CA1870: Use a cached 'SearchValues' instance +dotnet_diagnostic.CA1870.severity = none + # CA2000: Dispose objects before losing scope dotnet_diagnostic.CA2000.severity = none diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/Helpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/Helpers.cs index 354d56e6a26..ced17c84f44 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/Helpers.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/Helpers.cs @@ -109,7 +109,12 @@ namespace System.Reflection.Runtime.General public static string EscapeTypeNameIdentifier(this string identifier) { // Some characters in a type name need to be escaped + + // We're avoiding calling into MemoryExtensions here as it has paths that lead to reflection, + // and that would lead to an infinite loop given that this is the implementation of reflection. +#pragma warning disable CA1870 // Use a cached 'SearchValues' instance if (identifier != null && identifier.IndexOfAny(s_charsToEscape) != -1) +#pragma warning restore CA1870 { StringBuilder sbEscapedName = new StringBuilder(identifier.Length); foreach (char c in identifier) diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/BaseConfigurationRecord.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/BaseConfigurationRecord.cs index c0495b2aee2..f72769050e5 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/BaseConfigurationRecord.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/BaseConfigurationRecord.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; @@ -18,6 +19,12 @@ namespace System.Configuration [DebuggerDisplay("ConfigPath = {ConfigPath}")] internal abstract class BaseConfigurationRecord : IInternalConfigRecord { +#if NET8_0_OR_GREATER + private static readonly SearchValues s_invalidSubPathChars = SearchValues.Create(InvalidSubPathCharactersString); +#else + private static ReadOnlySpan s_invalidSubPathChars => InvalidSubPathCharactersString.AsSpan(); +#endif + protected const string NewLine = "\r\n"; internal const string KeywordTrue = "true"; @@ -128,7 +135,6 @@ namespace System.Configuration // Comparer used in sorting IndirectInputs. private static readonly IComparer s_indirectInputsComparer = new IndirectLocationInputComparer(); - private static readonly char[] s_invalidSubPathCharactersArray = InvalidSubPathCharactersString.ToCharArray(); protected Hashtable _children; // configName -> record private object _configContext; // Context for config level @@ -3090,7 +3096,7 @@ namespace System.Configuration throw new ConfigurationErrorsException(SR.Config_location_path_invalid_last_character, errorInfo); // combination of URI reserved characters and OS invalid filename characters, minus / (allowed reserved character) - if (subPath.IndexOfAny(s_invalidSubPathCharactersArray) != -1) + if (subPath.AsSpan().IndexOfAny(s_invalidSubPathChars) >= 0) throw new ConfigurationErrorsException(SR.Config_location_path_invalid_character, errorInfo); return subPath; diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Helpers.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Helpers.cs index 147259b5cde..d172f20f1cb 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Helpers.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Helpers.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; @@ -11,6 +12,12 @@ namespace System.Reflection.TypeLoading { internal static class Helpers { +#if NET8_0_OR_GREATER + private static readonly SearchValues s_charsToEscape = SearchValues.Create("\\[]+*&,"); +#else + private static ReadOnlySpan s_charsToEscape => "\\[]+*&,".AsSpan(); +#endif + [return: NotNullIfNotNull(nameof(original))] public static T[]? CloneArray(this T[]? original) { @@ -96,7 +103,7 @@ namespace System.Reflection.TypeLoading public static string EscapeTypeNameIdentifier(this string identifier) { // Some characters in a type name need to be escaped - if (identifier.IndexOfAny(s_charsToEscape) != -1) + if (TypeNameContainsTypeParserMetacharacters(identifier)) { StringBuilder sbEscapedName = new StringBuilder(identifier.Length); foreach (char c in identifier) @@ -113,12 +120,16 @@ namespace System.Reflection.TypeLoading public static bool TypeNameContainsTypeParserMetacharacters(this string identifier) { - return identifier.IndexOfAny(s_charsToEscape) != -1; + return identifier.AsSpan().IndexOfAny(s_charsToEscape) >= 0; } public static bool NeedsEscapingInTypeName(this char c) { - return Array.IndexOf(s_charsToEscape, c) >= 0; +#if NET8_0_OR_GREATER + return s_charsToEscape.Contains(c); +#else + return s_charsToEscape.IndexOf(c) >= 0; +#endif } public static string UnescapeTypeNameIdentifier(this string identifier) @@ -145,8 +156,6 @@ namespace System.Reflection.TypeLoading return identifier; } - private static readonly char[] s_charsToEscape = new char[] { '\\', '[', ']', '+', '*', '&', ',' }; - /// /// For AssemblyReferences, convert "unspecified" components from the ECMA format (0xffff) to the in-memory System.Version format (0xffffffff). /// diff --git a/src/libraries/System.Speech/src/Recognition/SrgsGrammar/SrgsRule.cs b/src/libraries/System.Speech/src/Recognition/SrgsGrammar/SrgsRule.cs index 7709a8f0a4b..030fe78a478 100644 --- a/src/libraries/System.Speech/src/Recognition/SrgsGrammar/SrgsRule.cs +++ b/src/libraries/System.Speech/src/Recognition/SrgsGrammar/SrgsRule.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers; using System.Collections.ObjectModel; using System.Diagnostics; using System.Speech.Internal; @@ -14,6 +15,8 @@ namespace System.Speech.Recognition.SrgsGrammar [DebuggerTypeProxy(typeof(SrgsRuleDebugDisplay))] public class SrgsRule : IRule { + private static readonly SearchValues s_invalidChars = SearchValues.Create("?*+|()^$/;.=<>[]{}\\ \t\r\n"); + #region Constructors private SrgsRule() { @@ -383,7 +386,7 @@ namespace System.Speech.Recognition.SrgsGrammar XmlParser.ThrowSrgsException(SRID.ConstructorNotAllowed, _id); } - if (s != null && (s.IndexOfAny(s_invalidChars) >= 0 || s.Length == 0)) + if (s != null && (s.Length == 0 || s.AsSpan().ContainsAny(s_invalidChars))) { XmlParser.ThrowSrgsException(SRID.InvalidMethodName); } @@ -416,7 +419,6 @@ namespace System.Speech.Recognition.SrgsGrammar private string _onError; private string _onRecognition; - private static readonly char[] s_invalidChars = new char[] { '?', '*', '+', '|', '(', ')', '^', '$', '/', ';', '.', '=', '<', '>', '[', ']', '{', '}', '\\', ' ', '\t', '\r', '\n' }; #endregion