Browse Source

Merge pull request #87133 from Repiteo/dotnet/enforce-globalization-rules

C#: Enforce globalization code quality rules
Rémi Verschelde 1 year ago
parent
commit
1aab6e96b9
25 changed files with 103 additions and 98 deletions
  1. 0 9
      modules/mono/.editorconfig
  2. 1 1
      modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
  3. 1 1
      modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
  4. 3 2
      modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/ForwarderMessageHandler.cs
  5. 7 5
      modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs
  6. 12 15
      modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
  7. 2 1
      modules/mono/editor/GodotTools/GodotTools/Build/BuildProblemsFilter.cs
  8. 16 15
      modules/mono/editor/GodotTools/GodotTools/Build/BuildProblemsView.cs
  9. 5 4
      modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
  10. 5 4
      modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
  11. 1 1
      modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs
  12. 2 2
      modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
  13. 8 5
      modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
  14. 4 3
      modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
  15. 3 2
      modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.exceptions.cs
  16. 2 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
  17. 2 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
  18. 2 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
  19. 14 27
      modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
  20. 2 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
  21. 2 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs
  22. 2 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
  23. 2 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs
  24. 2 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs
  25. 3 1
      modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs

+ 0 - 9
modules/mono/.editorconfig

@@ -30,14 +30,5 @@ dotnet_diagnostic.CA1720.severity = none
 # CA1805: Do not initialize unnecessarily
 # Don't tell me what to do.
 dotnet_diagnostic.CA1805.severity = none
-# CA1304: Specify CultureInfo
-# TODO: We should look into this.
-dotnet_diagnostic.CA1304.severity = warning
-# CA1305: Specify IFormatProvider
-# TODO: We should look into this. Disabled for now because it's annoying.
-dotnet_diagnostic.CA1305.severity = none
-# CA1310: Specify StringComparison for correctness
-# TODO: We should look into this. Disabled for now because it's annoying.
-dotnet_diagnostic.CA1310.severity = none
 # Diagnostics to prevent defensive copies of `in` struct parameters
 resharper_possibly_impure_method_call_on_readonly_variable_highlighting = error

+ 1 - 1
modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs

@@ -165,7 +165,7 @@ namespace GodotTools.BuildLogger
             bool hasSpecialChar = value.IndexOfAny(new[] { '\"', '\n', '\r', delimiter }) != -1;
 
             if (hasSpecialChar)
-                return "\"" + value.Replace("\"", "\"\"") + "\"";
+                return "\"" + value.Replace("\"", "\"\"", StringComparison.Ordinal) + "\"";
 
             return value;
         }

+ 1 - 1
modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs

@@ -15,7 +15,7 @@ namespace GodotTools.Core
             dir = Path.Combine(dir, " ").TrimEnd();
 
             if (Path.DirectorySeparatorChar == '\\')
-                dir = dir.Replace("/", "\\") + "\\";
+                dir = dir.Replace("/", "\\", StringComparison.Ordinal) + "\\";
 
             var fullPath = new Uri(Path.GetFullPath(path), UriKind.Absolute);
             var relRoot = new Uri(Path.GetFullPath(dir), UriKind.Absolute);

+ 3 - 2
modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/ForwarderMessageHandler.cs

@@ -1,3 +1,4 @@
+using System.Globalization;
 using System.IO;
 using System.Linq;
 using System.Threading;
@@ -28,7 +29,7 @@ namespace GodotTools.IdeMessaging.CLI
             {
                 await outputWriter.WriteLineAsync("======= Request =======");
                 await outputWriter.WriteLineAsync(id);
-                await outputWriter.WriteLineAsync(content.Body.Count(c => c == '\n').ToString());
+                await outputWriter.WriteLineAsync(content.Body.Count(c => c == '\n').ToString(CultureInfo.InvariantCulture));
                 await outputWriter.WriteLineAsync(content.Body);
                 await outputWriter.WriteLineAsync("=======================");
                 await outputWriter.FlushAsync();
@@ -41,7 +42,7 @@ namespace GodotTools.IdeMessaging.CLI
             {
                 await outputWriter.WriteLineAsync("======= Response =======");
                 await outputWriter.WriteLineAsync(id);
-                await outputWriter.WriteLineAsync(content.Body.Count(c => c == '\n').ToString());
+                await outputWriter.WriteLineAsync(content.Body.Count(c => c == '\n').ToString(CultureInfo.InvariantCulture));
                 await outputWriter.WriteLineAsync(content.Body);
                 await outputWriter.WriteLineAsync("========================");
                 await outputWriter.FlushAsync();

+ 7 - 5
modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs

@@ -1,5 +1,7 @@
 using GodotTools.Core;
+using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.Linq;
 using System.Text;
@@ -92,8 +94,8 @@ EndProject";
                 if (!isFirstProject)
                     projectsDecl += "\n";
 
-                projectsDecl += string.Format(_projectDeclaration,
-                    name, projectInfo.PathRelativeToSolution.Replace("/", "\\"), projectInfo.Guid);
+                projectsDecl += string.Format(CultureInfo.InvariantCulture, _projectDeclaration,
+                    name, projectInfo.PathRelativeToSolution.Replace("/", "\\", StringComparison.Ordinal), projectInfo.Guid);
 
                 for (int i = 0; i < projectInfo.Configs.Count; i++)
                 {
@@ -105,15 +107,15 @@ EndProject";
                         projPlatformsCfg += "\n";
                     }
 
-                    slnPlatformsCfg += string.Format(_solutionPlatformsConfig, config);
-                    projPlatformsCfg += string.Format(_projectPlatformsConfig, projectInfo.Guid, config);
+                    slnPlatformsCfg += string.Format(CultureInfo.InvariantCulture, _solutionPlatformsConfig, config);
+                    projPlatformsCfg += string.Format(CultureInfo.InvariantCulture, _projectPlatformsConfig, projectInfo.Guid, config);
                 }
 
                 isFirstProject = false;
             }
 
             string solutionPath = Path.Combine(DirectoryPath, Name + ".sln");
-            string content = string.Format(_solutionTemplate, projectsDecl, slnPlatformsCfg, projPlatformsCfg);
+            string content = string.Format(CultureInfo.InvariantCulture, _solutionTemplate, projectsDecl, slnPlatformsCfg, projPlatformsCfg);
 
             File.WriteAllText(solutionPath, content, Encoding.UTF8); // UTF-8 with BOM
         }

+ 12 - 15
modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs

@@ -38,21 +38,18 @@ namespace GodotTools.Build
 
         public override int GetHashCode()
         {
-            unchecked
-            {
-                int hash = 17;
-                hash = (hash * 29) + Solution.GetHashCode();
-                hash = (hash * 29) + Project.GetHashCode();
-                hash = (hash * 29) + Configuration.GetHashCode();
-                hash = (hash * 29) + (RuntimeIdentifier?.GetHashCode() ?? 0);
-                hash = (hash * 29) + (PublishOutputDir?.GetHashCode() ?? 0);
-                hash = (hash * 29) + Restore.GetHashCode();
-                hash = (hash * 29) + Rebuild.GetHashCode();
-                hash = (hash * 29) + OnlyClean.GetHashCode();
-                hash = (hash * 29) + CustomProperties.GetHashCode();
-                hash = (hash * 29) + LogsDirPath.GetHashCode();
-                return hash;
-            }
+            var hash = new HashCode();
+            hash.Add(Solution);
+            hash.Add(Project);
+            hash.Add(Configuration);
+            hash.Add(RuntimeIdentifier);
+            hash.Add(PublishOutputDir);
+            hash.Add(Restore);
+            hash.Add(Rebuild);
+            hash.Add(OnlyClean);
+            hash.Add(CustomProperties);
+            hash.Add(LogsDirPath);
+            return hash.ToHashCode();
         }
 
         // Needed for instantiation from Godot, after reloading assemblies

+ 2 - 1
modules/mono/editor/GodotTools/GodotTools/Build/BuildProblemsFilter.cs

@@ -1,4 +1,5 @@
 using Godot;
+using System.Globalization;
 
 namespace GodotTools.Build
 {
@@ -16,7 +17,7 @@ namespace GodotTools.Build
             set
             {
                 _problemsCount = value;
-                ToggleButton.Text = _problemsCount.ToString();
+                ToggleButton.Text = _problemsCount.ToString(CultureInfo.InvariantCulture);
             }
         }
 

+ 16 - 15
modules/mono/editor/GodotTools/GodotTools/Build/BuildProblemsView.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.Linq;
 using System.Text;
@@ -83,8 +84,8 @@ namespace GodotTools.Build
                         "error" or _ => BuildDiagnostic.DiagnosticType.Error,
                     },
                     File = csvColumns[1],
-                    Line = int.Parse(csvColumns[2]),
-                    Column = int.Parse(csvColumns[3]),
+                    Line = int.Parse(csvColumns[2], CultureInfo.InvariantCulture),
+                    Column = int.Parse(csvColumns[3], CultureInfo.InvariantCulture),
                     Code = csvColumns[4],
                     Message = csvColumns[5],
                     ProjectFile = csvColumns[6],
@@ -93,7 +94,7 @@ namespace GodotTools.Build
                 // If there's no ProjectFile but the File is a csproj, then use that.
                 if (string.IsNullOrEmpty(diagnostic.ProjectFile) &&
                     !string.IsNullOrEmpty(diagnostic.File) &&
-                    diagnostic.File.EndsWith(".csproj"))
+                    diagnostic.File.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase))
                 {
                     diagnostic.ProjectFile = diagnostic.File;
                 }
@@ -151,9 +152,9 @@ namespace GodotTools.Build
             foreach (var diagnostic in selectedDiagnostics)
             {
                 if (!string.IsNullOrEmpty(diagnostic.Code))
-                    sb.Append($"{diagnostic.Code}: ");
+                    sb.Append(CultureInfo.InvariantCulture, $"{diagnostic.Code}: ");
 
-                sb.AppendLine($"{diagnostic.Message} {diagnostic.File}({diagnostic.Line},{diagnostic.Column})");
+                sb.AppendLine(CultureInfo.InvariantCulture, $"{diagnostic.Message} {diagnostic.File}({diagnostic.Line},{diagnostic.Column})");
             }
 
             string text = sb.ToString();
@@ -251,7 +252,7 @@ namespace GodotTools.Build
 
             file = ProjectSettings.LocalizePath(file);
 
-            if (file.StartsWith("res://"))
+            if (file.StartsWith("res://", StringComparison.Ordinal))
             {
                 var script = (Script)ResourceLoader.Load(file, typeHint: Internal.CSharpLanguageType);
 
@@ -426,7 +427,7 @@ namespace GodotTools.Build
                         ? Path.GetRelativePath(projectDir, file)
                         : "Unknown file".TTR();
 
-                    string fileItemText = string.Format("{0} ({1} issues)".TTR(), relativeFilePath, fileDiagnostics.Length);
+                    string fileItemText = string.Format(CultureInfo.InvariantCulture, "{0} ({1} issues)".TTR(), relativeFilePath, fileDiagnostics.Length);
 
                     var fileItem = _problemsTree.CreateItem(projectItem);
                     fileItem.SetText(0, fileItemText);
@@ -468,10 +469,10 @@ namespace GodotTools.Build
                 shortMessage = shortMessage[..lineBreakIdx];
             text.Append(shortMessage);
 
-            tooltip.Append($"Message: {diagnostic.Message}");
+            tooltip.Append(CultureInfo.InvariantCulture, $"Message: {diagnostic.Message}");
 
             if (!string.IsNullOrEmpty(diagnostic.Code))
-                tooltip.Append($"\nCode: {diagnostic.Code}");
+                tooltip.Append(CultureInfo.InvariantCulture, $"\nCode: {diagnostic.Code}");
 
             string type = diagnostic.Type switch
             {
@@ -481,7 +482,7 @@ namespace GodotTools.Build
                 BuildDiagnostic.DiagnosticType.Error => "error",
                 _ => "unknown",
             };
-            tooltip.Append($"\nType: {type}");
+            tooltip.Append(CultureInfo.InvariantCulture, $"\nType: {type}");
 
             if (!string.IsNullOrEmpty(diagnostic.File))
             {
@@ -491,15 +492,15 @@ namespace GodotTools.Build
                     text.Append(diagnostic.File);
                 }
 
-                text.Append($"({diagnostic.Line},{diagnostic.Column})");
+                text.Append(CultureInfo.InvariantCulture, $"({diagnostic.Line},{diagnostic.Column})");
 
-                tooltip.Append($"\nFile: {diagnostic.File}");
-                tooltip.Append($"\nLine: {diagnostic.Line}");
-                tooltip.Append($"\nColumn: {diagnostic.Column}");
+                tooltip.Append(CultureInfo.InvariantCulture, $"\nFile: {diagnostic.File}");
+                tooltip.Append(CultureInfo.InvariantCulture, $"\nLine: {diagnostic.Line}");
+                tooltip.Append(CultureInfo.InvariantCulture, $"\nColumn: {diagnostic.Column}");
             }
 
             if (!string.IsNullOrEmpty(diagnostic.ProjectFile))
-                tooltip.Append($"\nProject: {diagnostic.ProjectFile}");
+                tooltip.Append(CultureInfo.InvariantCulture, $"\nProject: {diagnostic.ProjectFile}");
 
             return new ProblemItem()
             {

+ 5 - 4
modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs

@@ -12,6 +12,7 @@ using Directory = GodotTools.Utils.Directory;
 using File = GodotTools.Utils.File;
 using OS = GodotTools.Utils.OS;
 using Path = System.IO.Path;
+using System.Globalization;
 
 namespace GodotTools.Export
 {
@@ -277,7 +278,7 @@ namespace GodotTools.Export
                             if (platform == OS.Platforms.iOS)
                             {
                                 // Exclude dsym folders.
-                                return !dir.EndsWith(".dsym", StringComparison.InvariantCultureIgnoreCase);
+                                return !dir.EndsWith(".dsym", StringComparison.OrdinalIgnoreCase);
                             }
 
                             return true;
@@ -297,7 +298,7 @@ namespace GodotTools.Export
                             if (platform == OS.Platforms.iOS)
                             {
                                 // Don't recurse into dsym folders.
-                                return !dir.EndsWith(".dsym", StringComparison.InvariantCultureIgnoreCase);
+                                return !dir.EndsWith(".dsym", StringComparison.OrdinalIgnoreCase);
                             }
 
                             return true;
@@ -313,13 +314,13 @@ namespace GodotTools.Export
                                     byte[] fileData = File.ReadAllBytes(path);
                                     string hash = Convert.ToBase64String(SHA512.HashData(fileData));
 
-                                    manifest.Append($"{filePath}\t{hash}\n");
+                                    manifest.Append(CultureInfo.InvariantCulture, $"{filePath}\t{hash}\n");
 
                                     AddFile($"res://.godot/mono/publish/{arch}/{filePath}", fileData, false);
                                 }
                                 else
                                 {
-                                    if (platform == OS.Platforms.iOS && path.EndsWith(".dat"))
+                                    if (platform == OS.Platforms.iOS && path.EndsWith(".dat", StringComparison.OrdinalIgnoreCase))
                                     {
                                         AddIosBundleFile(path);
                                     }

+ 5 - 4
modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs

@@ -4,6 +4,7 @@ using GodotTools.Export;
 using GodotTools.Utils;
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.Linq;
 using GodotTools.Build;
@@ -202,10 +203,10 @@ namespace GodotTools
                     var insideQuotes = false;
                     var hasFileFlag = false;
 
-                    execArgs = execArgs.ReplaceN("{line}", line.ToString());
-                    execArgs = execArgs.ReplaceN("{col}", col.ToString());
+                    execArgs = execArgs.ReplaceN("{line}", line.ToString(CultureInfo.InvariantCulture));
+                    execArgs = execArgs.ReplaceN("{col}", col.ToString(CultureInfo.InvariantCulture));
                     execArgs = execArgs.StripEdges(true, true);
-                    execArgs = execArgs.Replace("\\\\", "\\");
+                    execArgs = execArgs.Replace("\\\\", "\\", StringComparison.Ordinal);
 
                     for (int i = 0; i < execArgs.Length; ++i)
                     {
@@ -225,7 +226,7 @@ namespace GodotTools
                             }
 
                             var arg = execArgs.Substr(from, numChars);
-                            if (arg.Contains("{file}"))
+                            if (arg.Contains("{file}", StringComparison.OrdinalIgnoreCase))
                             {
                                 hasFileFlag = true;
                             }

+ 1 - 1
modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs

@@ -73,7 +73,7 @@ namespace GodotTools.Internals
             string? slnParentDir = (string?)ProjectSettings.GetSetting("dotnet/project/solution_directory");
             if (string.IsNullOrEmpty(slnParentDir))
                 slnParentDir = "res://";
-            else if (!slnParentDir.StartsWith("res://"))
+            else if (!slnParentDir.StartsWith("res://", System.StringComparison.Ordinal))
                 slnParentDir = "res://" + slnParentDir;
 
             // The csproj should be in the same folder as project.godot.

+ 2 - 2
modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs

@@ -274,7 +274,7 @@ namespace GodotTools.Utils
             if (builder.Length > 0)
                 builder.Append(' ');
 
-            if (fileName.Contains(' '))
+            if (fileName.Contains(' ', StringComparison.Ordinal))
             {
                 builder.Append('"');
                 builder.Append(fileName);
@@ -297,7 +297,7 @@ namespace GodotTools.Utils
                 if (builder.Length > 0)
                     builder.Append(' ');
 
-                if (argument.Contains(' '))
+                if (argument.Contains(' ', StringComparison.Ordinal))
                 {
                     builder.Append('"');
                     builder.Append(argument);

+ 8 - 5
modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
 using System.Runtime.InteropServices;
 using Godot.NativeInterop;
 
@@ -776,11 +777,11 @@ namespace Godot
 
         private static bool FindNamedColor(string name, out Color color)
         {
-            name = name.Replace(" ", string.Empty);
-            name = name.Replace("-", string.Empty);
-            name = name.Replace("_", string.Empty);
-            name = name.Replace("'", string.Empty);
-            name = name.Replace(".", string.Empty);
+            name = name.Replace(" ", string.Empty, StringComparison.Ordinal);
+            name = name.Replace("-", string.Empty, StringComparison.Ordinal);
+            name = name.Replace("_", string.Empty, StringComparison.Ordinal);
+            name = name.Replace("'", string.Empty, StringComparison.Ordinal);
+            name = name.Replace(".", string.Empty, StringComparison.Ordinal);
             name = name.ToUpperInvariant();
 
             return Colors.namedColors.TryGetValue(name, out color);
@@ -1329,7 +1330,9 @@ namespace Godot
         /// <returns>A string representation of this color.</returns>
         public readonly string ToString(string? format)
         {
+#pragma warning disable CA1305 // Disable warning: "Specify IFormatProvider"
             return $"({R.ToString(format)}, {G.ToString(format)}, {B.ToString(format)}, {A.ToString(format)})";
+#pragma warning restore CA1305
         }
     }
 }

+ 4 - 3
modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs

@@ -3,6 +3,7 @@
 using System;
 using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
 using System.IO;
 using System.Reflection;
 using System.Runtime.CompilerServices;
@@ -698,7 +699,7 @@ namespace Godot
                     case GodotObject godotObject:
                         return VariantUtils.CreateFrom(godotObject);
                     case Enum @enum:
-                        return VariantUtils.CreateFrom(Convert.ToInt64(@enum));
+                        return VariantUtils.CreateFrom(Convert.ToInt64(@enum, CultureInfo.InvariantCulture));
                     case Collections.IGenericGodotDictionary godotDictionary:
                         return VariantUtils.CreateFrom(godotDictionary.UnderlyingDictionary);
                     case Collections.IGenericGodotArray godotArray:
@@ -777,7 +778,7 @@ namespace Godot
                     return func(variant);
 
                 if (typeof(GodotObject).IsAssignableFrom(type))
-                    return Convert.ChangeType(VariantUtils.ConvertTo<GodotObject>(variant), type);
+                    return Convert.ChangeType(VariantUtils.ConvertTo<GodotObject>(variant), type, CultureInfo.InvariantCulture);
 
                 if (typeof(GodotObject[]).IsAssignableFrom(type))
                 {
@@ -796,7 +797,7 @@ namespace Godot
                     }
 
                     using var godotArray = NativeFuncs.godotsharp_variant_as_array(variant);
-                    return Convert.ChangeType(ConvertToSystemArrayOfGodotObject(godotArray, type), type);
+                    return Convert.ChangeType(ConvertToSystemArrayOfGodotObject(godotArray, type), type, CultureInfo.InvariantCulture);
                 }
 
                 if (type.IsEnum)

+ 3 - 2
modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.exceptions.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Globalization;
 using System.Text;
 
 #nullable enable
@@ -73,7 +74,7 @@ namespace Godot
 
                     if (!string.IsNullOrEmpty(_nativeClassName))
                     {
-                        sb.Append($" (Method '{_nativeClassName}')");
+                        sb.Append(CultureInfo.InvariantCulture, $" (Method '{_nativeClassName}')");
                     }
 
                     return sb.ToString();
@@ -131,7 +132,7 @@ namespace Godot
 
                     if (!string.IsNullOrEmpty(_nativeMethodName))
                     {
-                        sb.Append($" (Method '{_nativeMethodName}')");
+                        sb.Append(CultureInfo.InvariantCulture, $" (Method '{_nativeMethodName}')");
                     }
 
                     return sb.ToString();

+ 2 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs

@@ -435,7 +435,9 @@ namespace Godot
         /// <returns>A string representation of this plane.</returns>
         public readonly string ToString(string? format)
         {
+#pragma warning disable CA1305 // Disable warning: "Specify IFormatProvider"
             return $"{_normal.ToString(format)}, {_d.ToString(format)}";
+#pragma warning restore CA1305
         }
     }
 }

+ 2 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs

@@ -1023,10 +1023,12 @@ namespace Godot
         /// <returns>A string representation of this projection.</returns>
         public readonly string ToString(string? format)
         {
+#pragma warning disable CA1305 // Disable warning: "Specify IFormatProvider"
             return $"{X.X.ToString(format)}, {X.Y.ToString(format)}, {X.Z.ToString(format)}, {X.W.ToString(format)}\n" +
                 $"{Y.X.ToString(format)}, {Y.Y.ToString(format)}, {Y.Z.ToString(format)}, {Y.W.ToString(format)}\n" +
                 $"{Z.X.ToString(format)}, {Z.Y.ToString(format)}, {Z.Z.ToString(format)}, {Z.W.ToString(format)}\n" +
                 $"{W.X.ToString(format)}, {W.Y.ToString(format)}, {W.Z.ToString(format)}, {W.W.ToString(format)}\n";
+#pragma warning restore CA1305
         }
     }
 }

+ 2 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs

@@ -822,7 +822,9 @@ namespace Godot
         /// <returns>A string representation of this quaternion.</returns>
         public readonly string ToString(string? format)
         {
+#pragma warning disable CA1305 // Disable warning: "Specify IFormatProvider"
             return $"({X.ToString(format)}, {Y.ToString(format)}, {Z.ToString(format)}, {W.ToString(format)})";
+#pragma warning restore CA1305
         }
     }
 }

+ 14 - 27
modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs

@@ -314,7 +314,7 @@ namespace Godot
         /// <returns>The capitalized string.</returns>
         public static string Capitalize(this string instance)
         {
-            string aux = instance.CamelcaseToUnderscore(true).Replace("_", " ").Trim();
+            string aux = instance.CamelcaseToUnderscore(true).Replace("_", " ", StringComparison.Ordinal).Trim();
             string cap = string.Empty;
 
             for (int i = 0; i < aux.GetSliceCount(" "); i++)
@@ -742,7 +742,7 @@ namespace Godot
             byte[] ret = new byte[len];
             for (int i = 0; i < len; i++)
             {
-                ret[i] = (byte)int.Parse(instance.AsSpan(i * 2, 2), NumberStyles.AllowHexSpecifier);
+                ret[i] = (byte)int.Parse(instance.AsSpan(i * 2, 2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
             }
             return ret;
         }
@@ -821,7 +821,7 @@ namespace Godot
                 instance = instance.Substring(2);
             }
 
-            return sign * int.Parse(instance, NumberStyles.HexNumber);
+            return sign * int.Parse(instance, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
         }
 
         /// <summary>
@@ -878,7 +878,7 @@ namespace Godot
             if (string.IsNullOrEmpty(instance))
                 return false;
             else if (instance.Length > 1)
-                return instance[0] == '/' || instance[0] == '\\' || instance.Contains(":/") || instance.Contains(":\\");
+                return instance[0] == '/' || instance[0] == '\\' || instance.Contains(":/", StringComparison.Ordinal) || instance.Contains(":\\", StringComparison.Ordinal);
             else
                 return instance[0] == '/' || instance[0] == '\\';
         }
@@ -1120,7 +1120,7 @@ namespace Godot
         /// <returns>If the string contains a valid IP address.</returns>
         public static bool IsValidIPAddress(this string instance)
         {
-            if (instance.Contains(':'))
+            if (instance.Contains(':', StringComparison.Ordinal))
             {
                 string[] ip = instance.Split(':');
 
@@ -1404,23 +1404,10 @@ namespace Godot
             return instance + "/" + file;
         }
 
-        /// <summary>
-        /// Replace occurrences of a substring for different ones inside the string.
-        /// </summary>
-        /// <seealso cref="ReplaceN(string, string, string)"/>
-        /// <param name="instance">The string to modify.</param>
-        /// <param name="what">The substring to be replaced in the string.</param>
-        /// <param name="forwhat">The substring that replaces <paramref name="what"/>.</param>
-        /// <returns>The string with the substring occurrences replaced.</returns>
-        public static string Replace(this string instance, string what, string forwhat)
-        {
-            return instance.Replace(what, forwhat);
-        }
-
         /// <summary>
         /// Replace occurrences of a substring for different ones inside the string, but search case-insensitive.
         /// </summary>
-        /// <seealso cref="Replace(string, string, string)"/>
+        /// <seealso cref="string.Replace(string, string, StringComparison)"/>
         /// <param name="instance">The string to modify.</param>
         /// <param name="what">The substring to be replaced in the string.</param>
         /// <param name="forwhat">The substring that replaces <paramref name="what"/>.</param>
@@ -1634,7 +1621,7 @@ namespace Godot
                 if (end < 0)
                     end = len;
                 if (allowEmpty || end > from)
-                    ret.Add(float.Parse(instance.Substring(from)));
+                    ret.Add(float.Parse(instance.Substring(from), CultureInfo.InvariantCulture));
                 if (end == len)
                     break;
 
@@ -1738,7 +1725,7 @@ namespace Godot
         /// <returns>The number representation of the string.</returns>
         public static float ToFloat(this string instance)
         {
-            return float.Parse(instance);
+            return float.Parse(instance, CultureInfo.InvariantCulture);
         }
 
         /// <summary>
@@ -1749,7 +1736,7 @@ namespace Godot
         /// <returns>The number representation of the string.</returns>
         public static int ToInt(this string instance)
         {
-            return int.Parse(instance);
+            return int.Parse(instance, CultureInfo.InvariantCulture);
         }
 
         /// <summary>
@@ -1802,7 +1789,7 @@ namespace Godot
         /// <returns>A copy of the string with the prefix string removed from the start.</returns>
         public static string TrimPrefix(this string instance, string prefix)
         {
-            if (instance.StartsWith(prefix))
+            if (instance.StartsWith(prefix, StringComparison.Ordinal))
                 return instance.Substring(prefix.Length);
 
             return instance;
@@ -1816,7 +1803,7 @@ namespace Godot
         /// <returns>A copy of the string with the suffix string removed from the end.</returns>
         public static string TrimSuffix(this string instance, string suffix)
         {
-            if (instance.EndsWith(suffix))
+            if (instance.EndsWith(suffix, StringComparison.Ordinal))
                 return instance.Substring(0, instance.Length - suffix.Length);
 
             return instance;
@@ -1833,7 +1820,7 @@ namespace Godot
         /// <returns>The unescaped string.</returns>
         public static string URIDecode(this string instance)
         {
-            return Uri.UnescapeDataString(instance.Replace("+", "%20"));
+            return Uri.UnescapeDataString(instance.Replace("+", "%20", StringComparison.Ordinal));
         }
 
         /// <summary>
@@ -1860,10 +1847,10 @@ namespace Godot
         /// <returns>The string sanitized as a valid node name.</returns>
         public static string ValidateNodeName(this string instance)
         {
-            string name = instance.Replace(_invalidNodeNameCharacters[0], "");
+            string name = instance.Replace(_invalidNodeNameCharacters[0], "", StringComparison.Ordinal);
             for (int i = 1; i < _invalidNodeNameCharacters.Length; i++)
             {
-                name = name.Replace(_invalidNodeNameCharacters[i], "");
+                name = name.Replace(_invalidNodeNameCharacters[i], "", StringComparison.Ordinal);
             }
             return name;
         }

+ 2 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs

@@ -1021,7 +1021,9 @@ namespace Godot
         /// <returns>A string representation of this vector.</returns>
         public readonly string ToString(string? format)
         {
+#pragma warning disable CA1305 // Disable warning: "Specify IFormatProvider"
             return $"({X.ToString(format)}, {Y.ToString(format)})";
+#pragma warning restore CA1305
         }
     }
 }

+ 2 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2I.cs

@@ -600,7 +600,9 @@ namespace Godot
         /// <returns>A string representation of this vector.</returns>
         public readonly string ToString(string? format)
         {
+#pragma warning disable CA1305 // Disable warning: "Specify IFormatProvider"
             return $"({X.ToString(format)}, {Y.ToString(format)})";
+#pragma warning restore CA1305
         }
     }
 }

+ 2 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs

@@ -1123,7 +1123,9 @@ namespace Godot
         /// <returns>A string representation of this vector.</returns>
         public readonly string ToString(string? format)
         {
+#pragma warning disable CA1305 // Disable warning: "Specify IFormatProvider"
             return $"({X.ToString(format)}, {Y.ToString(format)}, {Z.ToString(format)})";
+#pragma warning restore CA1305
         }
     }
 }

+ 2 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3I.cs

@@ -655,7 +655,9 @@ namespace Godot
         /// <returns>A string representation of this vector.</returns>
         public readonly string ToString(string? format)
         {
+#pragma warning disable CA1305 // Disable warning: "Specify IFormatProvider"
             return $"({X.ToString(format)}, {Y.ToString(format)}, {Z.ToString(format)})";
+#pragma warning restore CA1305
         }
     }
 }

+ 2 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs

@@ -905,7 +905,9 @@ namespace Godot
         /// <returns>A string representation of this vector.</returns>
         public readonly string ToString(string? format)
         {
+#pragma warning disable CA1305 // Disable warning: "Specify IFormatProvider"
             return $"({X.ToString(format)}, {Y.ToString(format)}, {Z.ToString(format)}, {W.ToString(format)})";
+#pragma warning restore CA1305
         }
     }
 }

+ 3 - 1
modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4I.cs

@@ -676,7 +676,9 @@ namespace Godot
         /// <returns>A string representation of this vector.</returns>
         public readonly string ToString(string? format)
         {
-            return $"({X.ToString(format)}, {Y.ToString(format)}, {Z.ToString(format)}), {W.ToString(format)})";
+#pragma warning disable CA1305 // Disable warning: "Specify IFormatProvider"
+            return $"({X.ToString(format)}, {Y.ToString(format)}, {Z.ToString(format)}, {W.ToString(format)})";
+#pragma warning restore CA1305
         }
     }
 }