diff --git a/c#/Directory.Build.props b/c#/Directory.Build.props
index 24df1573..c684f1c8 100644
--- a/c#/Directory.Build.props
+++ b/c#/Directory.Build.props
@@ -10,6 +10,7 @@
true
Recommended
true
+ true
diff --git a/c#/GlobalSuppressions.cs b/c#/GlobalSuppressions.cs
index 46ca515e..b3c9ec1a 100644
--- a/c#/GlobalSuppressions.cs
+++ b/c#/GlobalSuppressions.cs
@@ -1,59 +1,52 @@
// ReSharper disable once RedundantUsingDirective
using System.Diagnostics.CodeAnalysis;
-[assembly: SuppressMessage("Major Code Smell", "S125:Sections of code should not be commented out")]
-[assembly: SuppressMessage("Minor Code Smell", "S3604:Member initializer values should not be redundant")]
+[assembly: SuppressMessage("Class Design", "AV1008:Class should not be static")]
+[assembly: SuppressMessage("Class Design", "AV1010:Member hides inherited member")]
+[assembly: SuppressMessage("Correctness", "SS019:Switch should have default label.")]
[assembly: SuppressMessage("Critical Bug", "S6674:Log message template should be syntactically correct")]
-[assembly: SuppressMessage("Roslynator", "RCS1001:Add braces (when expression spans over multiple lines).")]
-[assembly: SuppressMessage("Roslynator", "RCS1139:Add summary element to documentation comment.")]
-[assembly: SuppressMessage("Roslynator", "RCS1156:Use string.Length instead of comparison with empty string.")]
-[assembly: SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods")]
-[assembly: SuppressMessage("Style", "CC0061:Asynchronous method can be terminated with the 'Async' keyword.")]
-[assembly: SuppressMessage("Naming", "AV1755:Name of async method should end with Async or TaskAsync")]
-[assembly: SuppressMessage("Naming", "AV1706:Identifier contains an abbreviation or is too short")]
-[assembly: SuppressMessage("Maintainability", "AV1580:Method argument calls a nested method")]
-[assembly: SuppressMessage("Maintainability", "AV1500:Member or local function contains too many statements")]
-[assembly: SuppressMessage("Miscellaneous Design", "AV1210:Catch a specific exception instead of Exception, SystemException or ApplicationException")]
+[assembly: SuppressMessage("Design", "CC0021:Use nameof")]
[assembly: SuppressMessage("Design", "CC0031:Check for null before calling a delegate")]
-[assembly: SuppressMessage("Maintainability", "AV1561:Signature contains too many parameters")]
-[assembly: SuppressMessage("Design", "MA0051:Method is too long")]
-[assembly: SuppressMessage("Usage", "MA0004:Use Task.ConfigureAwait")]
+[assembly: SuppressMessage("Design", "CC0091:Use static method", Justification = "https://github.com/code-cracker/code-cracker/issues/1087")]
[assembly: SuppressMessage("Design", "CC0120:Your Switch maybe include default clause")]
-[assembly: SuppressMessage("Correctness", "SS019:Switch should have default label.")]
-[assembly: SuppressMessage("Maintainability", "AV1555:Avoid using non-(nullable-)boolean named arguments")]
-[assembly: SuppressMessage("Maintainability", "AV1535:Missing block in case or default clause of switch statement")]
-[assembly: SuppressMessage("Maintainability", "CC0097:You have missing/unexistent parameters in Xml Docs")]
[assembly: SuppressMessage("Design", "MA0048:File name must match type name")]
+[assembly: SuppressMessage("Design", "MA0051:Method is too long")]
+[assembly: SuppressMessage("Documentation", "AV2305:Missing XML comment for internally visible type, member or parameter")]
+[assembly: SuppressMessage("Framework", "AV2220:Simple query should be replaced by extension method call")]
+[assembly: SuppressMessage("Maintainability", "AV1500:Member or local function contains too many statements")]
[assembly: SuppressMessage("Maintainability", "AV1507:File contains multiple types")]
-[assembly: SuppressMessage("Style", "MA0007:Add a comma after the last value")]
-[assembly: SuppressMessage("Style", "MA0003:Add parameter name to improve readability")]
-[assembly: SuppressMessage("Design", "CC0021:Use nameof")]
-[assembly: SuppressMessage("Naming", "AV1704:Identifier contains one or more digits in its name")]
-[assembly: SuppressMessage("Usage", "MA0006:Use String.Equals instead of equality operator")]
+[assembly: SuppressMessage("Maintainability", "AV1532:Loop statement contains nested loop")]
+[assembly: SuppressMessage("Maintainability", "AV1535:Missing block in case or default clause of switch statement")]
[assembly: SuppressMessage("Maintainability", "AV1537:If-else-if construct should end with an unconditional else clause")]
[assembly: SuppressMessage("Maintainability", "AV1554:Method contains optional parameter in type hierarchy")]
-[assembly: SuppressMessage("Maintainability", "AV1564:Parameter in public or internal member is of type bool or bool?")]
-[assembly: SuppressMessage("Class Design", "AV1010:Member hides inherited member")]
+[assembly: SuppressMessage("Maintainability", "AV1555:Avoid using non-(nullable-)boolean named arguments")]
+[assembly: SuppressMessage("Maintainability", "AV1561:Signature contains too many parameters")]
[assembly: SuppressMessage("Maintainability", "AV1562:Do not declare a parameter as ref or out")]
-[assembly: SuppressMessage("Maintainability", "AV1532:Loop statement contains nested loop")]
-[assembly: SuppressMessage("Design", "CC0091:Use static method", Justification = "https://github.com/code-cracker/code-cracker/issues/1087")]
-[assembly: SuppressMessage("Usage", "MA0015:Specify the parameter name in ArgumentException")]
-[assembly: SuppressMessage("Class Design", "AV1008:Class should not be static")]
-[assembly: SuppressMessage("Framework", "AV2220:Simple query should be replaced by extension method call")]
+[assembly: SuppressMessage("Maintainability", "AV1564:Parameter in public or internal member is of type bool or bool?")]
+[assembly: SuppressMessage("Maintainability", "AV1580:Method argument calls a nested method")]
+[assembly: SuppressMessage("Maintainability", "CC0097:You have missing/unexistent parameters in Xml Docs")]
+[assembly: SuppressMessage("Major Code Smell", "S125:Sections of code should not be commented out")]
+[assembly: SuppressMessage("Minor Code Smell", "S3604:Member initializer values should not be redundant")]
+[assembly: SuppressMessage("Miscellaneous Design", "AV1210:Catch a specific exception instead of Exception, SystemException or ApplicationException")]
+[assembly: SuppressMessage("Naming", "AV1704:Identifier contains one or more digits in its name")]
+[assembly: SuppressMessage("Naming", "AV1706:Identifier contains an abbreviation or is too short")]
+[assembly: SuppressMessage("Naming", "AV1755:Name of async method should end with Async or TaskAsync")]
+[assembly: SuppressMessage("Naming", "CA1711:Identifiers should not have incorrect suffix")]
+[assembly: SuppressMessage("Naming", "CA1716:Identifiers should not match keywords")]
+[assembly: SuppressMessage("Performance", "CA1848:Use the LoggerMessage delegates")]
+[assembly: SuppressMessage("Roslynator", "RCS1001:Add braces (when expression spans over multiple lines).")]
+[assembly: SuppressMessage("Roslynator", "RCS1139:Add summary element to documentation comment.")]
+[assembly: SuppressMessage("Roslynator", "RCS1156:Use string.Length instead of comparison with empty string.")]
[assembly: SuppressMessage("Style", "CC0001:You should use 'var' whenever possible.")]
[assembly: SuppressMessage("Style", "CC0037:Remove commented code.")]
-[assembly: SuppressMessage("Documentation", "AV2305:Missing XML comment for internally visible type, member or parameter")]
[assembly: SuppressMessage("Style", "CC0061:Asynchronous method can be terminated with the 'Async' keyword.")]
[assembly: SuppressMessage("Style", "MA0003:Add parameter name to improve readability")]
[assembly: SuppressMessage("Style", "MA0007:Add a comma after the last value")]
[assembly: SuppressMessage("Style", "VSTHRD200:Use \"Async\" suffix for async methods")]
+[assembly: SuppressMessage("Usage", "CC0057:Unused parameters")]
[assembly: SuppressMessage("Usage", "MA0004:Use Task.ConfigureAwait")]
[assembly: SuppressMessage("Usage", "MA0006:Use String.Equals instead of equality operator")]
[assembly: SuppressMessage("Usage", "MA0015:Specify the parameter name in ArgumentException")]
-[assembly: SuppressMessage("Performance", "CA1848:Use the LoggerMessage delegates")]
-[assembly: SuppressMessage("Usage", "CC0057:Unused parameters")]
-[assembly: SuppressMessage("Naming", "CA1711:Identifiers should not have incorrect suffix")]
-[assembly: SuppressMessage("Naming", "CA1716:Identifiers should not match keywords")]
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:Prefix local calls with this")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented")]
diff --git a/c#/crawler/src/GlobalUsings.cs b/c#/crawler/src/GlobalUsings.cs
index 14793a8a..5b8250c6 100644
--- a/c#/crawler/src/GlobalUsings.cs
+++ b/c#/crawler/src/GlobalUsings.cs
@@ -7,6 +7,7 @@
global using System.Data;
global using System.Diagnostics;
global using System.Diagnostics.CodeAnalysis;
+global using System.Globalization;
global using System.Linq.Expressions;
global using System.Reflection;
global using System.Text.Json;
diff --git a/c#/crawler/src/SonicPusher.cs b/c#/crawler/src/SonicPusher.cs
index a8010339..1f014a9f 100644
--- a/c#/crawler/src/SonicPusher.cs
+++ b/c#/crawler/src/SonicPusher.cs
@@ -42,7 +42,8 @@ public float PushPost(Fid fid, string type, PostId id, RepeatedField? c
try
{
foreach (var text in contentTexts.Chunk(30000)) // https://github.com/spikensbror-dotnet/nsonic/issues/11
- Ingest.Push($"{CollectionPrefix}{type}_content", $"f{fid}", id.ToString(), text.ToString(), "cmn");
+ Ingest.Push($"{CollectionPrefix}{type}_content", $"f{fid}",
+ id.ToString(CultureInfo.InvariantCulture), text.ToString(), "cmn");
}
catch (Exception e)
{
diff --git a/c#/crawler/src/Tieba/Crawl/CrawlerLocks.cs b/c#/crawler/src/Tieba/Crawl/CrawlerLocks.cs
index 1641dc86..6ef1017b 100644
--- a/c#/crawler/src/Tieba/Crawl/CrawlerLocks.cs
+++ b/c#/crawler/src/Tieba/Crawl/CrawlerLocks.cs
@@ -104,7 +104,8 @@ protected override void LogTrace()
lock (_crawling)
lock (_failed)
{
- logger.LogTrace("Lock: type={} crawlingIdCount={} crawlingPageCount={} crawlingPageCountsKeyById={} failedIdCount={} failedPageCount={} failures={}", LockType,
+ logger.LogTrace("Lock: type={} crawlingIdCount={} crawlingPageCount={} crawlingPageCountsKeyById={}"
+ + " failedIdCount={} failedPageCount={} failures={}", LockType,
_crawling.Count, _crawling.Values.Sum(d => d.Count),
Helper.UnescapedJsonSerialize(_crawling.ToDictionary(pair => pair.Key.ToString(), pair => pair.Value.Count)),
_failed.Count, _failed.Values.Sum(d => d.Count),
diff --git a/c#/crawler/src/Tieba/Crawl/ThreadLateCrawlerAndSaver.cs b/c#/crawler/src/Tieba/Crawl/ThreadLateCrawlerAndSaver.cs
index 7ba534e9..b6605371 100644
--- a/c#/crawler/src/Tieba/Crawl/ThreadLateCrawlerAndSaver.cs
+++ b/c#/crawler/src/Tieba/Crawl/ThreadLateCrawlerAndSaver.cs
@@ -41,7 +41,7 @@ private async Task CrawlThread
var json = await requester.RequestJson(
$"{ClientRequester.LegacyClientApiDomain}/c/f/pb/page", "8.8.8.8", new()
{
- {"kz", tid.ToString()},
+ {"kz", tid.ToString(CultureInfo.InvariantCulture)},
{"pn", "1"},
// rn have to be at least 2
@@ -60,7 +60,7 @@ private async Task CrawlThread
},
JsonValueKind.String => () =>
{ // https://stackoverflow.com/questions/62100000/why-doesnt-system-text-json-jsonelement-have-trygetstring-or-trygetboolean/62100246#62100246
- var r = int.TryParse(errorCodeProp.GetString(), out var p);
+ var r = int.TryParse(errorCodeProp.GetString(), CultureInfo.InvariantCulture, out var p);
return (p, r);
},
_ => () => (0, false)
@@ -85,7 +85,7 @@ private async Task CrawlThread
? threadInfo.TryGetProperty("phone_type", out var phoneType)
? new ThreadPost
{
- Tid = Tid.Parse(thread.GetStrProp("id")),
+ Tid = Tid.Parse(thread.GetStrProp("id"), CultureInfo.InvariantCulture),
AuthorPhoneType = phoneType.GetString().NullIfEmpty()
}
: throw new TiebaException(shouldRetry: false,
diff --git a/c#/crawler/src/Tieba/Crawl/UserParserAndSaver.cs b/c#/crawler/src/Tieba/Crawl/UserParserAndSaver.cs
index aa32ed3e..8b42908a 100644
--- a/c#/crawler/src/Tieba/Crawl/UserParserAndSaver.cs
+++ b/c#/crawler/src/Tieba/Crawl/UserParserAndSaver.cs
@@ -45,7 +45,7 @@ public void ParseUsers(IEnumerable users) =>
{
static (string Portrait, uint? UpdateTime) ExtractPortrait(string portrait) =>
ExtractPortraitRegex().Match(portrait) is {Success: true} m
- ? (m.Groups["portrait"].Value, Time.Parse(m.Groups["timestamp"].ValueSpan))
+ ? (m.Groups["portrait"].Value, Time.Parse(m.Groups["timestamp"].ValueSpan, CultureInfo.InvariantCulture))
: (portrait, null);
var uid = el.Uid;
diff --git a/c#/crawler/src/Worker/ArchiveCrawlWorker.cs b/c#/crawler/src/Worker/ArchiveCrawlWorker.cs
index 7ae16a60..38eea613 100644
--- a/c#/crawler/src/Worker/ArchiveCrawlWorker.cs
+++ b/c#/crawler/src/Worker/ArchiveCrawlWorker.cs
@@ -29,11 +29,12 @@ public class ArchiveCrawlWorker(
public static float GetCumulativeAverage(float currentCa, float previousCa, int currentIndex) =>
(currentCa + ((currentIndex - 1) * previousCa)) / currentIndex;
+ [SuppressMessage("Correctness", "SS002:DateTime.Now was referenced")]
public static (string Relative, string At) GetEta(int total, int completed, float averageDurationInMs)
{
var etaTimeSpan = TimeSpan.FromMilliseconds((total - completed) * averageDurationInMs);
return (etaTimeSpan.Humanize(precision: 5, minUnit: TimeUnit.Second),
- DateTime.Now.Add(etaTimeSpan).ToString("MM-dd HH:mm:ss"));
+ DateTime.Now.Add(etaTimeSpan).ToString("MM-dd HH:mm:ss", CultureInfo.CurrentCulture));
}
protected override async Task DoWork(CancellationToken stoppingToken)
diff --git a/c#/crawler/src/Worker/ForumModeratorRevisionCrawlWorker.cs b/c#/crawler/src/Worker/ForumModeratorRevisionCrawlWorker.cs
index b32ed993..dbf32ee6 100644
--- a/c#/crawler/src/Worker/ForumModeratorRevisionCrawlWorker.cs
+++ b/c#/crawler/src/Worker/ForumModeratorRevisionCrawlWorker.cs
@@ -40,7 +40,7 @@ where e.IsCrawling
{
var type = typeEl.QuerySelector("div.title")?.Children
.Select(el => el.ClassList)
- .First(classNames => classNames.Any(className => className.EndsWith("_icon")))
+ .First(classNames => classNames.Any(className => className.EndsWith("_icon", StringComparison.Ordinal)))
.Select(className => className.Split("_")[0])
.First(className => !string.IsNullOrWhiteSpace(className));
if (string.IsNullOrEmpty(type)) throw new TiebaException();
diff --git a/c#/imagePipeline/src/Consumer/MetadataConsumer.cs b/c#/imagePipeline/src/Consumer/MetadataConsumer.cs
index f0964942..a4bc6867 100644
--- a/c#/imagePipeline/src/Consumer/MetadataConsumer.cs
+++ b/c#/imagePipeline/src/Consumer/MetadataConsumer.cs
@@ -1,4 +1,3 @@
-using System.Globalization;
using System.IO.Hashing;
using System.Text.RegularExpressions;
using NetTopologySuite.Geometries;
@@ -101,7 +100,7 @@ private Func GetImageMetaData
if (rawBytes == null || rawBytes.Length == 0) return null;
if (rawBytes.Length > 65535)
_logger.LogWarning("Embedded {} in image contains {} bytes",
- typeof(TEmbeddedMetadata).Name.ToUpper(), rawBytes.Length);
+ typeof(TEmbeddedMetadata).Name.ToUpperInvariant(), rawBytes.Length);
var xxHash3 = XxHash3.HashToUInt64(rawBytes);
return new()
{
@@ -281,7 +280,7 @@ private static partial class ExifDateTimeTagValuesParser
// https://stackoverflow.com/questions/4483886/how-can-i-get-a-count-of-the-total-number-of-digits-in-a-number/51099524#51099524
[SuppressMessage("Major Code Smell", "S3358:Ternary operators should not be nested")]
static int CountDigits(int n) => n == 0 ? 1 : (n > 0 ? 1 : 2) + (int)Math.Log10(Math.Abs((double)n));
- var hasFractionalSeconds = int.TryParse(exifFractionalSeconds, out var fractionalSeconds);
+ var hasFractionalSeconds = int.TryParse(exifFractionalSeconds, CultureInfo.InvariantCulture, out var fractionalSeconds);
fractionalSeconds = hasFractionalSeconds ? fractionalSeconds : 0;
if (exifDateTime == "00000" && fractionalSeconds == 0) return null;
@@ -290,7 +289,8 @@ private static partial class ExifDateTimeTagValuesParser
?? ParseWithOverflowedTimeParts(exifDateTime)
?? ParseAsUnixTimestamp(exifDateTime)
?? throw new ArgumentException(
- $"Failed to parse provided EXIF date time \"{exifDateTime}\" with fractional seconds {fractionalSeconds}.");
+ $"Failed to parse provided EXIF date time \"{exifDateTime}\""
+ + $" with fractional seconds {fractionalSeconds.ToString(CultureInfo.InvariantCulture)}.");
return fractionalSeconds == 0 ? ret : ret with
{
DateTime = ret.DateTime.AddSeconds(fractionalSeconds / Math.Pow(10, CountDigits(fractionalSeconds)))
@@ -333,7 +333,7 @@ private static partial class ExifDateTimeTagValuesParser
: default;
return dateTimeOffset == default
? null
- : new(dateTimeOffset.DateTime, dateTimeOffset.ToString("zzz"));
+ : new(dateTimeOffset.DateTime, dateTimeOffset.ToString("zzz", CultureInfo.InvariantCulture));
}
[GeneratedRegex(
@@ -349,13 +349,14 @@ private static partial class ExifDateTimeTagValuesParser
ExtractExifDateTimePartsRegex().Match(exifDateTime) is not {Success: true} m
? null
: new(new DateTime(
- int.Parse(m.Groups["year"].ValueSpan),
- int.Parse(m.Groups["month"].ValueSpan),
- int.Parse(m.Groups["day"].ValueSpan),
+ int.Parse(m.Groups["year"].ValueSpan, CultureInfo.InvariantCulture),
+ int.Parse(m.Groups["month"].ValueSpan, CultureInfo.InvariantCulture),
+ int.Parse(m.Groups["day"].ValueSpan, CultureInfo.InvariantCulture),
hour: 0, minute: 0, second: 0, DateTimeKind.Unspecified)
- .AddHours(int.Parse(m.Groups["hour"].ValueSpan))
- .AddMinutes(int.Parse(m.Groups["minute"].ValueSpan))
- .AddSeconds(int.Parse(m.Groups["second"].ValueSpan)), Offset: null);
+ .AddHours(int.Parse(m.Groups["hour"].ValueSpan, CultureInfo.InvariantCulture))
+ .AddMinutes(int.Parse(m.Groups["minute"].ValueSpan, CultureInfo.InvariantCulture))
+ .AddSeconds(int.Parse(m.Groups["second"].ValueSpan, CultureInfo.InvariantCulture)),
+ Offset: null);
private static DateTimeAndOffset? ParseAsUnixTimestamp(string exifDateTime) =>
long.TryParse(exifDateTime, NumberStyles.Integer,
diff --git a/c#/imagePipeline/src/Consumer/OcrConsumer.cs b/c#/imagePipeline/src/Consumer/OcrConsumer.cs
index 3d73f27c..73fa8ea3 100644
--- a/c#/imagePipeline/src/Consumer/OcrConsumer.cs
+++ b/c#/imagePipeline/src/Consumer/OcrConsumer.cs
@@ -51,7 +51,7 @@ protected override IEnumerable ConsumeInternal(
RotationDegrees = result.TextBox.Angle.RoundToUshort(),
Recognizer = result switch
{
- PaddleOcrRecognitionResult r => "PaddleOCR" + Enum.GetName(r.ModelVersion)?.ToLower(),
+ PaddleOcrRecognitionResult r => "PaddleOCR" + Enum.GetName(r.ModelVersion)?.ToLowerInvariant(),
TesseractRecognitionResult {IsVertical: false} => "TesseractHorizontal",
TesseractRecognitionResult {IsVertical: true} => "TesseractVertical",
_ => throw new ArgumentOutOfRangeException(nameof(result), result, null)
diff --git a/c#/imagePipeline/src/GlobalUsings.cs b/c#/imagePipeline/src/GlobalUsings.cs
index a3b7c357..2afe376b 100644
--- a/c#/imagePipeline/src/GlobalUsings.cs
+++ b/c#/imagePipeline/src/GlobalUsings.cs
@@ -1,6 +1,7 @@
#pragma warning disable SA1210 // Using directives should be ordered alphabetically by namespace
global using System.ComponentModel.DataAnnotations;
global using System.Diagnostics.CodeAnalysis;
+global using System.Globalization;
global using System.Linq.Expressions;
global using System.Text.Json;
global using System.Threading.Channels;
@@ -9,12 +10,12 @@
global using Autofac;
global using Autofac.Features.OwnedInstances;
global using CommunityToolkit.Diagnostics;
+global using LanguageExt;
global using Microsoft.EntityFrameworkCore;
global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.Hosting;
global using Microsoft.Extensions.Logging;
global using OpenCvSharp;
-global using LanguageExt;
global using Polly;
global using Polly.Extensions.Http;
global using Polly.Registry;