From 9596ad000eb3f6ffd054e556d71a0e34e571c177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Mon, 11 May 2026 21:50:54 -0400 Subject: [PATCH 1/2] Change CA2249 to warning --- .editorconfig | 1 - src/core/IronPython.Modules/IronPython.Modules.csproj | 1 + src/core/IronPython.Modules/_csv.cs | 2 +- src/core/IronPython.Modules/_ctypes/SimpleType.cs | 4 ++-- src/core/IronPython.Modules/_ctypes/_ctypes.cs | 2 +- src/core/IronPython.Modules/array.cs | 2 +- src/core/IronPython.Modules/grp.cs | 2 +- src/core/IronPython.Modules/nt.cs | 4 ++-- src/core/IronPython.Modules/winsound.cs | 2 +- src/core/IronPython/IronPython.csproj | 4 ++-- src/core/IronPython/Modules/Builtin.cs | 2 +- src/core/IronPython/Runtime/ClrModule.cs | 2 +- src/core/IronPython/Runtime/FormattingHelper.cs | 4 ++-- src/core/IronPython/Runtime/Importer.cs | 2 +- src/core/IronPython/Runtime/LiteralParser.cs | 2 +- src/core/IronPython/Runtime/MemoryView.cs | 2 +- src/core/IronPython/Runtime/NewStringFormatter.cs | 3 +-- src/core/IronPython/Runtime/Operations/FloatOps.cs | 2 +- src/core/IronPython/Runtime/Operations/PythonOps.cs | 4 ++-- src/core/IronPython/Runtime/Operations/StringOps.cs | 4 ++-- src/core/IronPython/Runtime/TypecodeOps.cs | 2 +- 21 files changed, 26 insertions(+), 27 deletions(-) diff --git a/.editorconfig b/.editorconfig index a9a2a81b8..2d2c3e500 100644 --- a/.editorconfig +++ b/.editorconfig @@ -97,7 +97,6 @@ dotnet_diagnostic.CA2208.severity = suggestion # CA2208: Instantiate argument e dotnet_diagnostic.CA2211.severity = none # CA2211: Non-constant fields should not be visible dotnet_diagnostic.CA2219.severity = suggestion # CA2219: Do not raise exceptions in finally clauses dotnet_diagnostic.CA2229.severity = suggestion # CA2229: Implement serialization constructors -dotnet_diagnostic.CA2249.severity = suggestion # CA2249: Consider using 'string.Contains' instead of 'string.IndexOf' dotnet_diagnostic.CA2263.severity = none # CA2263: Prefer generic overload when type is known dotnet_diagnostic.CA3075.severity = suggestion # CA3075: Insecure DTD processing in XML dotnet_diagnostic.CA5350.severity = suggestion # CA5350: Do Not Use Weak Cryptographic Algorithms diff --git a/src/core/IronPython.Modules/IronPython.Modules.csproj b/src/core/IronPython.Modules/IronPython.Modules.csproj index 7a314eec0..51f05e4d0 100644 --- a/src/core/IronPython.Modules/IronPython.Modules.csproj +++ b/src/core/IronPython.Modules/IronPython.Modules.csproj @@ -13,6 +13,7 @@ T:System.Diagnostics.CodeAnalysis.NotNullAttribute; T:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute; T:System.Runtime.Versioning.SupportedOSPlatformAttribute; + M:System.String.Contains(System.Char); M:System.String.EndsWith(System.Char); diff --git a/src/core/IronPython.Modules/_csv.cs b/src/core/IronPython.Modules/_csv.cs index f442329aa..278f65f01 100644 --- a/src/core/IronPython.Modules/_csv.cs +++ b/src/core/IronPython.Modules/_csv.cs @@ -1006,7 +1006,7 @@ private void JoinAppend(string field, bool quoted) { } foreach (char c in need_escape) { - if (field.IndexOf(c) >= 0) { + if (field.Contains(c)) { if (string.IsNullOrEmpty(_dialect.escapechar)) throw MakeError("need to escape, but no escapechar set"); field = field.Replace(c.ToString(), _dialect.escapechar + c); diff --git a/src/core/IronPython.Modules/_ctypes/SimpleType.cs b/src/core/IronPython.Modules/_ctypes/SimpleType.cs index 2bdcf6467..a786362da 100644 --- a/src/core/IronPython.Modules/_ctypes/SimpleType.cs +++ b/src/core/IronPython.Modules/_ctypes/SimpleType.cs @@ -44,7 +44,7 @@ public SimpleType(CodeContext/*!*/ context, string name, PythonTuple bases, Pyth if (!TryGetBoundCustomMember(context, "_type_", out object val) || (sVal = StringOps.AsString(val)) == null || sVal.Length != 1 || - allowedTypes.IndexOf(sVal[0]) == -1) { + !allowedTypes.Contains(sVal[0])) { throw PythonOps.AttributeError("AttributeError: class must define a '_type_' attribute which must be a single character string containing one of '{0}'.", allowedTypes); } @@ -86,7 +86,7 @@ public SimpleType(CodeContext/*!*/ context, string name, PythonTuple bases, Pyth throw new NotImplementedException("simple type " + sVal); } - if (!name.EndsWith("_be", StringComparison.Ordinal) && !name.EndsWith("_le", StringComparison.Ordinal) && swappedTypes.IndexOf(_charType) != -1) { + if (!name.EndsWith("_be", StringComparison.Ordinal) && !name.EndsWith("_le", StringComparison.Ordinal) && swappedTypes.Contains(_charType)) { CreateSwappedType(context, name, bases, dict); } _format = (BitConverter.IsLittleEndian ? '<' : '>') + _charType.ToString(); diff --git a/src/core/IronPython.Modules/_ctypes/_ctypes.cs b/src/core/IronPython.Modules/_ctypes/_ctypes.cs index 83ccefdb9..c841898b5 100644 --- a/src/core/IronPython.Modules/_ctypes/_ctypes.cs +++ b/src/core/IronPython.Modules/_ctypes/_ctypes.cs @@ -186,7 +186,7 @@ public static void FreeLibrary(IntPtr handle) { } private static object LoadDLL(string? library, int mode) { - if (library is not null && library.IndexOf((char)0) != -1) throw PythonOps.ValueError("embedded null byte"); + if (library is not null && library.Contains((char)0)) throw PythonOps.ValueError("embedded null byte"); IntPtr res = NativeFunctions.LoadDLL(library, mode); if (res == IntPtr.Zero) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { diff --git a/src/core/IronPython.Modules/array.cs b/src/core/IronPython.Modules/array.cs index 1102c5ed6..03a70cabc 100644 --- a/src/core/IronPython.Modules/array.cs +++ b/src/core/IronPython.Modules/array.cs @@ -865,7 +865,7 @@ bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer) { if (_typeCode == 'u') { char quote = '\''; string s = new string(((ArrayData)_data).Data, 0, _data.Count); - if (s.IndexOf('\'') != -1 && s.IndexOf('\"') == -1) { + if (s.Contains('\'') && !s.Contains('\"')) { quote = '\"'; } diff --git a/src/core/IronPython.Modules/grp.cs b/src/core/IronPython.Modules/grp.cs index dc0344358..e1ae57b47 100644 --- a/src/core/IronPython.Modules/grp.cs +++ b/src/core/IronPython.Modules/grp.cs @@ -112,7 +112,7 @@ public static struct_group getgrgid(CodeContext context, object gid) { } public static struct_group getgrnam(string name) { - if (name is not null && name.IndexOf((char)0) != -1) throw PythonOps.ValueError("embedded null byte"); + if (name is not null && name.Contains((char)0)) throw PythonOps.ValueError("embedded null byte"); var grp = _getgrnam(name); if (grp == IntPtr.Zero) { throw PythonOps.KeyError($"getgrnam()): name not found: {name}"); diff --git a/src/core/IronPython.Modules/nt.cs b/src/core/IronPython.Modules/nt.cs index 773c4634a..dbc80ff0d 100644 --- a/src/core/IronPython.Modules/nt.cs +++ b/src/core/IronPython.Modules/nt.cs @@ -1221,7 +1221,7 @@ private static string ArgumentsToString(CodeContext/*!*/ context, object? args, if (space) { sb.Append(' '); } - if (strarg.IndexOf(' ') != -1) { + if (strarg.Contains(' ')) { sb.Append('"'); // double quote any existing quotes sb.Append(strarg.Replace("\"", "\"\"")); @@ -2398,7 +2398,7 @@ private static void CheckOptionalArgsCount(int numRegParms, int numOptPosParms, } private static void VerifyPath(string path, string functionName, string argName) { - if (path.IndexOf((char)0) != -1) throw PythonOps.ValueError($"{functionName}: embedded null character in {argName}"); + if (path.Contains((char)0)) throw PythonOps.ValueError($"{functionName}: embedded null character in {argName}"); } [SupportedOSPlatform("windows")] diff --git a/src/core/IronPython.Modules/winsound.cs b/src/core/IronPython.Modules/winsound.cs index 3231430cb..c9bb99f54 100644 --- a/src/core/IronPython.Modules/winsound.cs +++ b/src/core/IronPython.Modules/winsound.cs @@ -90,7 +90,7 @@ public static void PlaySound(CodeContext/*!*/ context, string? sound, int flags) if (((flags & SND_ASYNC) == SND_ASYNC) && ((flags & SND_MEMORY) == SND_MEMORY)) throw PythonOps.RuntimeError("Cannot play asynchronously from memory"); if ((flags & SND_MEMORY) == SND_MEMORY) throw PythonOps.TypeError($"a bytes-like object is required, not '{PythonOps.GetPythonTypeName(sound)}'"); - if (sound.IndexOf((char)0) != -1) throw PythonOps.ValueError("embedded null character"); + if (sound.Contains((char)0)) throw PythonOps.ValueError("embedded null character"); if (!PlaySound(sound, IntPtr.Zero, flags)) { throw PythonOps.RuntimeError("Failed to play sound"); diff --git a/src/core/IronPython/IronPython.csproj b/src/core/IronPython/IronPython.csproj index f4c66fcf1..9e8115844 100644 --- a/src/core/IronPython/IronPython.csproj +++ b/src/core/IronPython/IronPython.csproj @@ -16,9 +16,9 @@ T:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute; T:System.Runtime.Versioning.SupportedOSPlatformAttribute; T:System.Runtime.Versioning.UnsupportedOSPlatformAttribute; - M:System.String.StartsWith(System.Char); - M:System.String.EndsWith(System.Char); M:System.String.Contains(System.Char); + M:System.String.EndsWith(System.Char); + M:System.String.StartsWith(System.Char); diff --git a/src/core/IronPython/Modules/Builtin.cs b/src/core/IronPython/Modules/Builtin.cs index b16807d79..92d81bbd9 100644 --- a/src/core/IronPython/Modules/Builtin.cs +++ b/src/core/IronPython/Modules/Builtin.cs @@ -206,7 +206,7 @@ public static object compile(CodeContext/*!*/ context, [NotNone] string source, string sfilename = PythonOps.FsPathDecoded(context, filename); var sourceCodeKind = ValidateCompileMode(mode); - if (source.IndexOf('\0') != -1) { + if (source.Contains('\0')) { throw PythonOps.TypeError("compile() expected string without null bytes"); } diff --git a/src/core/IronPython/Runtime/ClrModule.cs b/src/core/IronPython/Runtime/ClrModule.cs index e9382f31f..79d47a881 100644 --- a/src/core/IronPython/Runtime/ClrModule.cs +++ b/src/core/IronPython/Runtime/ClrModule.cs @@ -193,7 +193,7 @@ object. Namespaces or types in the assembly can be accessed directly from if (file.Length == 0) throw new ValueErrorException("assembly name must not be empty string"); ContractUtils.RequiresNotNull(context, nameof(context)); - if (file.IndexOf(Path.DirectorySeparatorChar) != -1) { + if (file.Contains(Path.DirectorySeparatorChar)) { throw new ValueErrorException("filenames must not contain full paths, first add the path to sys.path"); } diff --git a/src/core/IronPython/Runtime/FormattingHelper.cs b/src/core/IronPython/Runtime/FormattingHelper.cs index 21be97843..8edc383a7 100644 --- a/src/core/IronPython/Runtime/FormattingHelper.cs +++ b/src/core/IronPython/Runtime/FormattingHelper.cs @@ -107,9 +107,9 @@ public static NumberFormatInfo InvariantUnderscoreNumberInfo { // that isn't. After all, it would be pretty weird to produce: // 000,xxx,xxx,xxx. So, produce 0,000,xxx,xxx,xxx instead. // (Just like CPython) - if (separator.IndexOf(res[beginningOfMaximumWidth]) != -1) { + if (separator.Contains(res[beginningOfMaximumWidth])) { for (int i = beginningOfMaximumWidth - 1; i >= 0; i--) { - if (separator.IndexOf(res[i]) == -1) { + if (!separator.Contains(res[i])) { res.Remove(0, i); break; } diff --git a/src/core/IronPython/Runtime/Importer.cs b/src/core/IronPython/Runtime/Importer.cs index 824bdf692..973b97c20 100644 --- a/src/core/IronPython/Runtime/Importer.cs +++ b/src/core/IronPython/Runtime/Importer.cs @@ -148,7 +148,7 @@ private static object ImportModuleFrom(CodeContext/*!*/ context, object from, Ar public static object ImportModule(CodeContext/*!*/ context, object globals, string/*!*/ modName, bool bottom, int level) { if (level < 0) throw PythonOps.ValueError("level must be >= 0"); - if (modName.IndexOf(Path.DirectorySeparatorChar) != -1) { + if (modName.Contains(Path.DirectorySeparatorChar)) { throw PythonOps.ImportError("Import by filename is not supported."); } diff --git a/src/core/IronPython/Runtime/LiteralParser.cs b/src/core/IronPython/Runtime/LiteralParser.cs index 24ac94dfa..3548efdbf 100644 --- a/src/core/IronPython/Runtime/LiteralParser.cs +++ b/src/core/IronPython/Runtime/LiteralParser.cs @@ -933,7 +933,7 @@ public static Complex ParseComplex(string s) { text = text.Trim(); - if (string.IsNullOrEmpty(text) || text.IndexOf(' ') != -1) { + if (string.IsNullOrEmpty(text) || text.Contains(' ')) { throw ExnMalformed(); } diff --git a/src/core/IronPython/Runtime/MemoryView.cs b/src/core/IronPython/Runtime/MemoryView.cs index f10b3f2cb..bd6dc75a7 100644 --- a/src/core/IronPython/Runtime/MemoryView.cs +++ b/src/core/IronPython/Runtime/MemoryView.cs @@ -516,7 +516,7 @@ public MemoryView cast([NotNone] string format, [NotNone, AllowNull]object shape } private static bool IsSupportedTypecode(char code) { - return ValidCodes.IndexOf(code) >= 0; + return ValidCodes.Contains(code); } private static void UnpackBytes(char typecode, object o, Span dest) { diff --git a/src/core/IronPython/Runtime/NewStringFormatter.cs b/src/core/IronPython/Runtime/NewStringFormatter.cs index fa5278f25..6fdddb86f 100644 --- a/src/core/IronPython/Runtime/NewStringFormatter.cs +++ b/src/core/IronPython/Runtime/NewStringFormatter.cs @@ -356,8 +356,7 @@ private string ReplaceText(string format) { /// spec to compute those values. /// private string/*!*/ ReplaceComputedFormats(string/*!*/ formatSpec) { - int computeStart = formatSpec.IndexOf('{'); - if (computeStart != -1) { + if (formatSpec.Contains('{')) { _depth++; formatSpec = ReplaceText(formatSpec); _depth--; diff --git a/src/core/IronPython/Runtime/Operations/FloatOps.cs b/src/core/IronPython/Runtime/Operations/FloatOps.cs index 51cf840ea..a086f5e62 100644 --- a/src/core/IronPython/Runtime/Operations/FloatOps.cs +++ b/src/core/IronPython/Runtime/Operations/FloatOps.cs @@ -760,7 +760,7 @@ internal static string Repr(CodeContext/*!*/ context, double self, bool trailing // if it's not round trippable though use .NET's round-trip format res = self.ToString("R", CultureInfo.InvariantCulture); - if (trailingZeroAfterWholeFloat && res.IndexOf('.') == -1) { + if (trailingZeroAfterWholeFloat && !res.Contains('.')) { res += ".0"; } return res; diff --git a/src/core/IronPython/Runtime/Operations/PythonOps.cs b/src/core/IronPython/Runtime/Operations/PythonOps.cs index 42c7744fb..b3280ab6e 100644 --- a/src/core/IronPython/Runtime/Operations/PythonOps.cs +++ b/src/core/IronPython/Runtime/Operations/PythonOps.cs @@ -155,7 +155,7 @@ internal static void RegisterEncodingError(CodeContext/*!*/ context, string name } internal static PythonTuple LookupEncoding(CodeContext/*!*/ context, string encoding) { - if (encoding.IndexOf('\0') != -1) { + if (encoding.Contains('\0')) { throw PythonOps.TypeError("lookup string cannot contain null character"); } //compute encoding.ToLower().Replace(' ', '-') but ToLower only on ASCII letters @@ -2109,7 +2109,7 @@ public static object ImportTop(CodeContext/*!*/ context, string fullName, int le public static object ImportBottom(CodeContext/*!*/ context, string fullName, int level) { object module = Importer.ImportLightThrow(context, fullName, null, level); - if (!LightExceptions.IsLightException(module) && fullName.IndexOf('.') >= 0) { + if (!LightExceptions.IsLightException(module) && fullName.Contains('.')) { // Extract bottom from the imported module chain string[] parts = fullName.Split('.'); diff --git a/src/core/IronPython/Runtime/Operations/StringOps.cs b/src/core/IronPython/Runtime/Operations/StringOps.cs index f17a210d3..20b8b0f89 100644 --- a/src/core/IronPython/Runtime/Operations/StringOps.cs +++ b/src/core/IronPython/Runtime/Operations/StringOps.cs @@ -297,7 +297,7 @@ public static bool __contains__([NotNone] string s, string? item) { } public static bool __contains__([NotNone] string s, char item) { - return s.IndexOf(item) != -1; + return s.Contains(item); } public static string __format__(CodeContext/*!*/ context, [NotNone] string self, [NotNone] string formatSpec) { @@ -1520,7 +1520,7 @@ public static string __str__([NotNone] ExtensibleString self) { internal static string Quote(string s) { StringBuilder b = new StringBuilder(s.Length + 5); char quote = '\''; - if (s.IndexOf('\'') != -1 && s.IndexOf('\"') == -1) { + if (s.Contains('\'') && !s.Contains('\"')) { quote = '\"'; } b.Append(quote); diff --git a/src/core/IronPython/Runtime/TypecodeOps.cs b/src/core/IronPython/Runtime/TypecodeOps.cs index cfd9ed3cf..69be2235f 100644 --- a/src/core/IronPython/Runtime/TypecodeOps.cs +++ b/src/core/IronPython/Runtime/TypecodeOps.cs @@ -33,7 +33,7 @@ public static bool TryDecomposeTypecode(string format, out char byteorder, out c code = format[1]; } // TODO: add validation of combinations - return ValidByteorder.IndexOf(byteorder) >= 0 && ValidCodes.IndexOf(code) >= 0; + return ValidByteorder.Contains(byteorder) && ValidCodes.Contains(code); } public static void DecomposeTypecode(string format, out char byteorder, out char code) { From cb53a7cea9ceee86710b35bddcd5d4b1a5f110c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Tue, 12 May 2026 20:37:14 -0400 Subject: [PATCH 2/2] Bump Meziantou.Polyfill to 1.0.127 --- src/core/IronPython.Modules/IronPython.Modules.csproj | 2 +- src/core/IronPython/IronPython.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/IronPython.Modules/IronPython.Modules.csproj b/src/core/IronPython.Modules/IronPython.Modules.csproj index 51f05e4d0..86e575a48 100644 --- a/src/core/IronPython.Modules/IronPython.Modules.csproj +++ b/src/core/IronPython.Modules/IronPython.Modules.csproj @@ -44,7 +44,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/core/IronPython/IronPython.csproj b/src/core/IronPython/IronPython.csproj index 9e8115844..9360331b0 100644 --- a/src/core/IronPython/IronPython.csproj +++ b/src/core/IronPython/IronPython.csproj @@ -65,7 +65,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive