Skip to content

Commit 96c8a99

Browse files
authored
Fix use of invariant culture strings (#106)
* add culture analyzers * register in release notes
1 parent ca58fda commit 96c8a99

File tree

11 files changed

+368
-21
lines changed

11 files changed

+368
-21
lines changed

.editorconfig

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,3 +238,28 @@ dotnet_diagnostic.IDE1006.severity = none # IDE1006: Naming rule violation
238238
dotnet_diagnostic.CS1998.severity = error # CS1998: Async method lacks 'await' operators and will run synchronously
239239
dotnet_diagnostic.CS8618.severity = error # CS8618: Non-nullable field is uninitialized. Consider declaring as nullable.
240240
dotnet_diagnostic.CS4014.severity = error # CS4014: Because this call is not awaited, execution of the current method continues before the call is completed
241+
242+
# MA0002: IEqualityComparer<string> or IComparer<string> is missing
243+
dotnet_diagnostic.MA0002.severity = none
244+
# MA0003: Add parameter name to improve readability
245+
dotnet_diagnostic.MA0003.severity = none
246+
# MA0006: Use String.Equals instead of equality operator
247+
dotnet_diagnostic.MA0006.severity = none
248+
# MA0007: Add a comma after the last value
249+
dotnet_diagnostic.MA0007.severity = none
250+
# MA0009: Add regex evaluation timeout
251+
dotnet_diagnostic.MA0009.severity = none
252+
# MA0026: Fix TODO comment
253+
dotnet_diagnostic.MA0026.severity = none
254+
# MA0038: Make method static (deprecated, use CA1822 instead)
255+
dotnet_diagnostic.MA0038.severity = none
256+
# MA0041: Make property static (deprecated, use CA1822 instead)
257+
dotnet_diagnostic.MA0041.severity = none
258+
# MA0048: File name must match type name
259+
dotnet_diagnostic.MA0048.severity = none
260+
# MA0051: Method is too long
261+
dotnet_diagnostic.MA0051.severity = none
262+
# MA0111: Use string.Create instead of FormattableString
263+
dotnet_diagnostic.MA0111.severity = none
264+
# MA0144: Use System.OperatingSystem to check the current OS
265+
dotnet_diagnostic.MA0144.severity = none

Directory.Packages.props

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77

88
<PackageVersion Include="PolySharp" Version="1.14.1" />
99

10+
<PackageVersion Include="Meziantou.Analyzer" Version="2.0.139" />
11+
<PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.11.0-beta1.23525.2" />
12+
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0" />
13+
1014
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
1115
<PackageVersion Include="System.ValueTuple" Version="4.5.0" />
1216
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.5.4" />

Source/BannedSymbols.txt

Lines changed: 286 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#if NETFRAMEWORK
2+
namespace System.Text
3+
{
4+
internal static class StringBuilderExtensions
5+
{
6+
public static StringBuilder Append(
7+
this StringBuilder sb,
8+
IFormatProvider? provider,
9+
FormattableString formattableString)
10+
{
11+
sb.Append(formattableString.ToString(provider));
12+
return sb;
13+
}
14+
15+
public static StringBuilder AppendLine(
16+
this StringBuilder sb,
17+
IFormatProvider? provider,
18+
FormattableString formattableString)
19+
{
20+
sb.AppendLine(formattableString.ToString(provider));
21+
return sb;
22+
}
23+
}
24+
}
25+
#endif

Source/Drivers/DriverHelper.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,14 +149,14 @@ static Action<TraceInfo> GetSqlLogAction(QueryExecutionManager executionManager)
149149
break;
150150
case TraceInfoStep.Completed:
151151
// log data reader execution stats
152-
executionManager.SqlTranslationWriter.WriteLine($"-- Data read time: {info.ExecutionTime}. Records fetched: {info.RecordsAffected}.\r\n");
152+
executionManager.SqlTranslationWriter.WriteLine(FormattableString.Invariant($"-- Data read time: {info.ExecutionTime}. Records fetched: {info.RecordsAffected}.\r\n"));
153153
break;
154154
case TraceInfoStep.AfterExecute:
155155
// log query execution stats
156156
if (info.RecordsAffected != null)
157-
executionManager.SqlTranslationWriter.WriteLine($"-- Execution time: {info.ExecutionTime}. Records affected: {info.RecordsAffected}.\r\n");
157+
executionManager.SqlTranslationWriter.WriteLine(FormattableString.Invariant($"-- Execution time: {info.ExecutionTime}. Records affected: {info.RecordsAffected}.\r\n"));
158158
else
159-
executionManager.SqlTranslationWriter.WriteLine($"-- Execution time: {info.ExecutionTime}\r\n");
159+
executionManager.SqlTranslationWriter.WriteLine(FormattableString.Invariant($"-- Execution time: {info.ExecutionTime}\r\n"));
160160
break;
161161
}
162162
};

Source/Drivers/DynamicLinqToDBDriver.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,9 @@ public override List<ExplorerItem> GetSchemaAndBuildAssembly(IConnectionInfo cxI
126126
#if NETFRAMEWORK
127127
MetadataReference.CreateFromFile(typeof(object). Assembly.Location),
128128
MetadataReference.CreateFromFile(typeof(Enumerable). Assembly.Location),
129+
#pragma warning disable RS0030 // Do not use banned APIs
129130
MetadataReference.CreateFromFile(typeof(IDbConnection). Assembly.Location),
131+
#pragma warning restore RS0030 // Do not use banned APIs
130132
MetadataReference.CreateFromFile(typeof(PhysicalAddress) .Assembly.Location),
131133
MetadataReference.CreateFromFile(typeof(BigInteger) .Assembly.Location),
132134
MetadataReference.CreateFromFile(typeof(IAsyncDisposable) .Assembly.Location),

Source/Drivers/StaticSchemaGenerator.cs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public TableInfo(PropertyInfo propertyInfo)
2020
Type = propertyInfo.PropertyType.GetItemType()!;
2121
TypeAccessor = TypeAccessor.GetAccessor(Type);
2222

23-
var tableAttr = Type.GetCustomAttribute<TableAttribute>();
23+
var tableAttr = Type.GetAttribute<TableAttribute>();
2424

2525
if (tableAttr != null)
2626
{
@@ -63,7 +63,7 @@ public static List<ExplorerItem> GetSchema(Type customContextType)
6363
// tables discovered using table access properties in context:
6464
// ITable<TableRecord> Prop or // IQueryable<TableRecord> Prop
6565
var tables = customContextType.GetProperties()
66-
.Where(static p => p.GetCustomAttribute<ObsoleteAttribute>() == null && typeof(IQueryable<>).IsSameOrParentOf(p.PropertyType))
66+
.Where(static p => !p.HasAttribute<ObsoleteAttribute>() && typeof(IQueryable<>).IsSameOrParentOf(p.PropertyType))
6767
.OrderBy(static p => p.Name)
6868
.Select(static p => new TableInfo(p));
6969

@@ -80,9 +80,7 @@ public static List<ExplorerItem> GetSchema(Type customContextType)
8080
// add association nodes
8181
foreach (var ma in table.TypeAccessor.Members)
8282
{
83-
var aa = ma.MemberInfo.GetCustomAttribute<AssociationAttribute>();
84-
85-
if (aa != null)
83+
if (ma.MemberInfo.HasAttribute<AssociationAttribute>())
8684
{
8785
var isToMany = ma.Type is IEnumerable;
8886
// TODO: try to infer this information?
@@ -132,11 +130,10 @@ static ExplorerItem GetTable(ExplorerIcon icon, TableInfo table)
132130
var columns =
133131
(
134132
from ma in table.TypeAccessor.Members
135-
let aa = ma.MemberInfo.GetCustomAttribute<AssociationAttribute>()
136-
where aa == null
137-
let ca = ma.MemberInfo.GetCustomAttribute<ColumnAttribute>()
138-
let id = ma.MemberInfo.GetCustomAttribute<IdentityAttribute>()
139-
let pk = ma.MemberInfo.GetCustomAttribute<PrimaryKeyAttribute>()
133+
where !ma.MemberInfo.HasAttribute<AssociationAttribute>()
134+
let ca = ma.MemberInfo.GetAttribute<ColumnAttribute>()
135+
let id = ma.MemberInfo.GetAttribute<IdentityAttribute>()
136+
let pk = ma.MemberInfo.GetAttribute<PrimaryKeyAttribute>()
140137
orderby
141138
ca == null ? 1 : ca.Order >= 0 ? 0 : 2,
142139
ca?.Order,

Source/Drivers/ValueFormatter.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ static ValueFormatter()
3838
{
3939
var typeConverters = new Dictionary<Type, Func<object, object>>();
4040
var baseTypeConverters = new Dictionary<Type, Func<object, object>>();
41-
var byTypeNameConverters = new Dictionary<string, Func<object, object>>();
41+
var byTypeNameConverters = new Dictionary<string, Func<object, object>>(StringComparer.Ordinal);
4242

4343
// generic types
4444
typeConverters.Add(typeof(BigInteger) , ConvertToString);
@@ -188,7 +188,7 @@ private static bool IsNull(object value)
188188
// INullable implemented by System.Data.SqlTypes.Sql* types
189189
return (value is System.Data.SqlTypes.INullable nullable && nullable.IsNull)
190190
|| (value is Oracle.ManagedDataAccess.Types.INullable onull && onull.IsNull)
191-
|| (value.GetType().FullName!.StartsWith("IBM.Data.DB2Types.") && IsDB2Null(value));
191+
|| (value.GetType().FullName!.StartsWith("IBM.Data.DB2Types.", StringComparison.Ordinal) && IsDB2Null(value));
192192

193193
// moved to function to avoid assembly load errors when loaded with wrong process bitness
194194
static bool IsDB2Null(object value) => value is IBM.Data.DB2Types.INullable db2null && db2null.IsNull;
@@ -266,7 +266,7 @@ private static object Format(byte[] value)
266266
int i;
267267

268268
for (i = 0; i < value.Length && i < 10; i++)
269-
sb.Append($"{value[i]:X2}:");
269+
sb.Append(CultureInfo.InvariantCulture, $"{value[i]:X2}:");
270270

271271
if (i > 0)
272272
sb.Length--;
@@ -280,9 +280,9 @@ private static object Format(byte[] value)
280280
private static object Format(char chr)
281281
{
282282
if (!XmlConvert.IsXmlChar(chr) && !char.IsHighSurrogate(chr) && !char.IsLowSurrogate(chr))
283-
return new XElement("span", new XElement("i", new XAttribute("style", "font-style: italic"), chr <= 255 ? $"\\x{((short)chr):X2}" : $"\\u{((short)chr):X4}"));
284-
else
285-
return chr.ToString();
283+
return new XElement("span", new XElement("i", new XAttribute("style", "font-style: italic"), chr <= 255 ? FormattableString.Invariant($"\\x{((short)chr):X2}") : FormattableString.Invariant($"\\u{((short)chr):X4}")));
284+
285+
return chr.ToString();
286286
}
287287

288288
private static object Format(bool value) => Util.RawHtml(new XElement("span", value.ToString()));
@@ -318,7 +318,7 @@ private static object ConvertNpgsqlInterval(object value)
318318
var val = (NpgsqlInterval)value;
319319
// let's use ISO8601 duration format
320320
// Time is microseconds
321-
return $"P{val.Months}M{val.Days}DT{((decimal)val.Time) / 1_000_000}S";
321+
return FormattableString.Invariant($"P{val.Months}M{val.Days}DT{((decimal)val.Time) / 1_000_000}S");
322322
}
323323
#endregion
324324

Source/UI/Model/AboutModel.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Windows;
22
using System.Windows.Media.Imaging;
3+
using LinqToDB.Extensions;
34

45
namespace LinqToDB.LINQPad.UI;
56

@@ -17,7 +18,7 @@ private AboutModel()
1718
var assembly = typeof(AboutModel).Assembly;
1819
Logo = new BitmapImage(new Uri($"pack://application:,,,/{assembly.FullName};component/resources/logo.png"));
1920
Project = $"Linq To DB LINQPad Driver v{assembly.GetName().Version!.ToString(3)}";
20-
Copyright = assembly.GetCustomAttribute<AssemblyCopyrightAttribute>()!.Copyright;
21+
Copyright = assembly.GetAttribute<AssemblyCopyrightAttribute>()!.Copyright;
2122
}
2223

2324
public BitmapImage Logo { get; }

Source/linq2db.LINQPad.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@
5959
<PackageReference Include="ClickHouse.Client" />
6060
<!--<PackageReference Include="linq2db4iSeries" />-->
6161
<PackageReference Include="Microsoft.SqlServer.Types" />
62+
63+
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" />
64+
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" />
65+
<PackageReference Include="Meziantou.Analyzer" />
66+
67+
<AdditionalFiles Include="$(MSBuildThisFileDirectory)\BannedSymbols.txt" />
6268
</ItemGroup>
6369

6470
<ItemGroup Condition=" '$(TargetFramework)' == 'net48'">

0 commit comments

Comments
 (0)