diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index c30e6aa..36e86be 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -17,7 +17,7 @@
"rollForward": false
},
"squigglecop.tool": {
- "version": "1.0.8",
+ "version": "1.0.13",
"commands": [
"dotnet-squigglecop"
],
diff --git a/build/targets/compiler/Compiler.props b/build/targets/compiler/Compiler.props
index db6ac9b..a6fe393 100644
--- a/build/targets/compiler/Compiler.props
+++ b/build/targets/compiler/Compiler.props
@@ -5,13 +5,6 @@
enable
-
-
- all
- runtime; build; native; contentfiles; analyzers
-
-
-
<_Parameter1>false
diff --git a/build/targets/compiler/Packages.props b/build/targets/compiler/Packages.props
index e1375fe..fbb58f0 100644
--- a/build/targets/compiler/Packages.props
+++ b/build/targets/compiler/Packages.props
@@ -1,5 +1,4 @@
-
-
-
+
+
diff --git a/docs/DOCUMENTING-ANALYZERS.md b/docs/DOCUMENTING-ANALYZERS.md
index de254a6..b04da65 100644
--- a/docs/DOCUMENTING-ANALYZERS.md
+++ b/docs/DOCUMENTING-ANALYZERS.md
@@ -40,7 +40,7 @@ private static readonly DiagnosticDescriptor Rule = new(
category: "Maintainability",
defaultSeverity: DiagnosticSeverity.Info,
isEnabledByDefault: true,
- helpLinkUri: $"https://github.com/rjmurillo/EffectiveCSharp.Analyzers/blob/{ThisAssembly.GitCommitId}/docs/{Id}.md");
+ helpLinkUri: $"https://github.com/rjmurillo/EffectiveCSharp.Analyzers/blob/{ThisAssembly.GitCommitId}/docs/rules/{Id}.md");
```
The documentation for the rule is placed in to `docs/rules/ECS0002.md`.
diff --git a/docs/rules/ECS0005.md b/docs/rules/ECS0005.md
new file mode 100644
index 0000000..e7be7e7
--- /dev/null
+++ b/docs/rules/ECS0005.md
@@ -0,0 +1,72 @@
+# ECS0005: Prefer FormattableString for culture-specific strings
+
+This rule is discussed in detail in [Effective C#: 50 Specific Ways to Improve your C#](https://www.oreilly.com/library/view/effective-c-50/9780134579290/). Guidance about the feature can be found on the [.NET Blog - String Interpolation in C# 10 and .NET 6](https://devblogs.microsoft.com/dotnet/string-interpolation-in-c-10-and-net-6/).
+
+## Cause
+
+The rule is triggered when a `string` is used for an interpolated string that could benefit from culture-specific formatting.
+
+## Rule description
+
+There are still valid use cases for `string.Format` and `StringBuilder.AppendFormat` where the composite format string isn't known at compile time (e.g., localized resources). However, if the string would otherwise be hardcoded into the C#, interpolated are preferred (from a performance perspective). Using `FormattableString` or `string.Create` instead of `string` for interpolated strings ensures that culture-specific formatting is correctly applied, preventing issues where the default culture may lead to incorrect string representations.
+
+## How to fix violations
+
+Replace the `string` with `FormattableString` where culture-specific formatting is required. For example, use `FormattableString.Invariant(...)` or `FormattableString.ToString(IFormatProvider)` as needed.
+
+## When to suppress warnings
+
+Suppress warnings if the default culture is explicitly intended, or the string does not require culture-specific formatting.
+
+### Suppress a warning
+
+If you want to suppress a single violation, add preprocessor directives to your source file to disable and then re-enable the rule.
+
+```csharp
+#pragma warning disable ECS0005
+// The code that's violating the rule
+#pragma warning restore ECS0005
+```
+
+To disable the rule for a file, folder, or project, set its severity to none in the [configuration file](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/configuration-files).
+
+```ini
+[*.cs]
+dotnet_diagnostic.ECS0005.severity = none
+```
+
+## Example of a violation
+
+### Description
+
+Using `string` for an interpolated string that requires culture-specific formatting.
+
+### Code
+
+```csharp
+public string GetMessage()
+{
+ double value = 299792.458;
+ return $"The speed of light is {value:N3} km/s."; // ECS0005 triggers here
+}
+```
+
+## Example of how to fix
+
+### Description
+
+Replace `string` with `FormattableString` to handle culture-specific formatting.
+
+### Code
+
+```csharp
+public string GetMessage()
+{
+ double value = 299792.458;
+ return FormattableString.Invariant($"The speed of light is {value:N3} km/s.");
+}
+```
+
+## Related rules
+
+[ECS0004: Replace string.Format with interpolated string](./ECS0004.md)
\ No newline at end of file
diff --git a/effectivecsharpanalyzers.sln.DotSettings b/effectivecsharpanalyzers.sln.DotSettings
index e8c1502..96f255b 100644
--- a/effectivecsharpanalyzers.sln.DotSettings
+++ b/effectivecsharpanalyzers.sln.DotSettings
@@ -1,2 +1,3 @@
- True
\ No newline at end of file
+ True
+ True
\ No newline at end of file
diff --git a/src/EffectiveCSharp.Analyzers/AnalyzerReleases.Unshipped.md b/src/EffectiveCSharp.Analyzers/AnalyzerReleases.Unshipped.md
index ffcfe32..6c9dbdc 100644
--- a/src/EffectiveCSharp.Analyzers/AnalyzerReleases.Unshipped.md
+++ b/src/EffectiveCSharp.Analyzers/AnalyzerReleases.Unshipped.md
@@ -7,6 +7,7 @@ Rule ID | Category | Severity | Notes
ECS0001 | Style | Info | PreferImplicitlyTypedLocalVariablesAnalyzer, [Documentation](https://github.com/rjmurillo/EffectiveCSharp.Analyzers/blob/e7c151c721c3039011356d6012838f46e4b60a21/docs/rules/ECS0001.md)
ECS0002 | Maintainability | Info | PreferReadonlyOverConstAnalyzer, [Documentation](https://github.com/rjmurillo/EffectiveCSharp.Analyzers/blob/10c2d53afd688efe5a59097f76cb4edf33f6a474/docs/rules/ECS0002.md)
ECS0004 | Style | Info | ReplaceStringFormatAnalyzer, [Documentation](https://github.com/rjmurillo/EffectiveCSharp.Analyzers5da647e447fad4eb0a9e3db287e1d16cce316114/docs/rules/ECS0004.md)
+ECS0005 | Globalization | Info | FormattableStringForCultureSpecificStringsAnalyzer, [Documentation](https://github.com/rjmurillo/EffectiveCSharp.Analyzers/blob/4741a337436bf0b94fc8274d5765a804396affd8/docs/rules/ECS0005.md)
ECS0006 | Refactoring | Info | AvoidStringlyTypedApisAnalyzer, [Documentation](https://github.com/rjmurillo/EffectiveCSharp.Analyzers6213cba8473dac61d6132e205550884eae1c94bf/docs/rules/ECS0006.md)
ECS0007 | Design | Info | ExpressCallbacksWithDelegatesAnalyzer, [Documentation](https://github.com/rjmurillo/EffectiveCSharp.Analyzers/blob/dc194bbde030e9c40d8d9cdb1e0b5ff8919fe5a8/docs/rules/ECS0007.md)
ECS0008 | Usage | Info | EventInvocationAnalyzer, [Documentation](https://github.com/rjmurillo/EffectiveCSharp.Analyzers/blob/cc7d91eb81f6781851c09732db1268be7dab402b/docs/rules/ECS0008.md)
diff --git a/src/EffectiveCSharp.Analyzers/Common/Categories.cs b/src/EffectiveCSharp.Analyzers/Common/Categories.cs
index 3ce334e..f9fb1c7 100644
--- a/src/EffectiveCSharp.Analyzers/Common/Categories.cs
+++ b/src/EffectiveCSharp.Analyzers/Common/Categories.cs
@@ -6,4 +6,5 @@ internal static class Categories
internal static readonly string Performance = nameof(Performance);
internal static readonly string Style = nameof(Style);
internal static readonly string Usage = nameof(Usage);
+ internal static readonly string Globalization = nameof(Globalization);
}
diff --git a/src/EffectiveCSharp.Analyzers/Common/CompilationExtensions.cs b/src/EffectiveCSharp.Analyzers/Common/CompilationExtensions.cs
new file mode 100644
index 0000000..f210139
--- /dev/null
+++ b/src/EffectiveCSharp.Analyzers/Common/CompilationExtensions.cs
@@ -0,0 +1,142 @@
+using System.Runtime.CompilerServices;
+
+namespace EffectiveCSharp.Analyzers.Common;
+
+internal static class CompilationExtensions
+{
+ ///
+ /// Gets the language version the compiler is capable of parsing.
+ ///
+ /// The compilation.
+ /// A if there is a ; otherwise, null.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static LanguageVersion? GetLanguageVersionFromCompilation(this Compilation compilation)
+ {
+ return compilation.SyntaxTrees.FirstOrDefault()?.Options is not CSharpParseOptions parseOptions
+ ? null
+ : parseOptions.LanguageVersion;
+ }
+
+ ///
+ /// Gets the specific .
+ ///
+ /// The instance.
+ /// Fully qualified name of the type.
+ /// True if the type can be located; otherwise, false.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static bool SupportsType(this Compilation compilation, string typeName)
+ {
+ INamedTypeSymbol? symbol = compilation.GetTypeByMetadataName(typeName);
+ return symbol != null;
+ }
+
+ ///
+ /// Gets the specific .
+ ///
+ /// The instance.
+ /// Fully qualified name of the type.
+ /// Name of the member on the type.
+ /// True if the type with the specified member can be located; otherwise, false.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static bool SupportsType(this Compilation compilation, string typeName, string memberName)
+ {
+ INamedTypeSymbol? symbol = compilation.GetTypeByMetadataName(typeName);
+ return symbol?.GetMembers(memberName).Length > 0;
+ }
+
+ ///
+ /// Gets the version from the compilation's referenced types.
+ ///
+ /// The compilation.
+ /// A if there is a that can be located; otherwise, performs feature detection.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static Version? GetDotNetVersionFromCompilation(this Compilation compilation)
+ {
+ Version mscorlibVersion = compilation.GetSpecialType(SpecialType.System_Object).ContainingAssembly.Identity.Version;
+
+ // If we detect a .NET version newer than 4, then just return that.
+ if (mscorlibVersion > DotNet.Versions.DotNet40)
+ {
+ return mscorlibVersion;
+ }
+
+ // The assembly version of mscorlib would be `4.0.0.0` regardless of the .NET Framework version
+ // To differentiate, we need to sniff for specific types or methods that exist only in those .NET versions
+
+ // Check for .NET Framework 4.8+ (introduced System.Runtime.GCLargeObjectHeapCompactionMode)
+ bool gcLargeObjectHeapCompactionModeType = compilation.SupportsType("System.Runtime.GCLargeObjectHeapCompactionMode");
+
+ if (gcLargeObjectHeapCompactionModeType)
+ {
+ // .NET Framework 4.8+
+ return DotNet.Versions.DotNet48;
+ }
+
+ // Check for .NET Framework 4.7+ (introduced System.ValueTuple)
+ bool valueTupleType = compilation.SupportsType("System.ValueTuple");
+
+ if (valueTupleType)
+ {
+ // .NET Framework 4.7+
+ return DotNet.Versions.DotNet47;
+ }
+
+ // Check for .NET Framework 4.6.2+ (introduced System.AppContext.SetSwitch)
+ bool hasSetSwitch = compilation.SupportsType("System.AppContext", "SetSwitch");
+
+ if (hasSetSwitch)
+ {
+ // .NET Framework 4.6.2+
+ return DotNet.Versions.DotNet46;
+ }
+
+ return null;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static bool IsCSharpVersionOrLater(this Compilation compilation, LanguageVersion desiredLanguageVersion)
+ {
+ LanguageVersion? languageVersion = GetLanguageVersionFromCompilation(compilation);
+
+ return languageVersion.HasValue && languageVersion.Value >= desiredLanguageVersion;
+ }
+
+ ///
+ /// Gets the .NET runtime version and the supported compiler language version.
+ ///
+ /// The compilation.
+ ///
+ /// A :
+ ///
+ /// -
+ ///
+ /// Contains the version of the assembly containing the type .
+ ///
+ /// -
+ ///
+ /// The documented default language version for a given .NET version from .
+ ///
+ /// -
+ ///
+ /// The effective language version, which the compiler uses to produce the .
+ ///
+ ///
+ ///
+ ///
+ /// The .NET version and the language version can differ, for example we can downgrade the language version
+ /// on a newer runtime, or downgrade the runtime with a newer language. When picking up issues, we want to
+ /// make sure analyzers and code fix providers are finding legitimate issues and offering compatible solutions.
+ ///
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static (Version? DotNetVersion, LanguageVersion? DotNetLanguageVersion, LanguageVersion? CompilerLanguageVersion) GetVersions(this Compilation compilation)
+ {
+ Version? version = GetDotNetVersionFromCompilation(compilation);
+ LanguageVersion? compilerLanguageVersion = GetLanguageVersionFromCompilation(compilation);
+ LanguageVersion? dotnetLanguageVersion = DotNet.LangVersion.FromDotNetVersion(version);
+
+ return (version, dotnetLanguageVersion, compilerLanguageVersion);
+ }
+}
diff --git a/src/EffectiveCSharp.Analyzers/Common/DiagnosticIds.cs b/src/EffectiveCSharp.Analyzers/Common/DiagnosticIds.cs
index d1e9665..b3a47f0 100644
--- a/src/EffectiveCSharp.Analyzers/Common/DiagnosticIds.cs
+++ b/src/EffectiveCSharp.Analyzers/Common/DiagnosticIds.cs
@@ -8,6 +8,7 @@ internal static class DiagnosticIds
internal const string PreferImplicitlyTypedLocalVariables = "ECS0001";
internal const string PreferReadonlyOverConst = "ECS0002";
internal const string ReplaceStringFormatWithInterpolatedString = "ECS0004";
+ internal const string PreferFormattableStringForCultureSpecificStrings = "ECS0005";
internal const string AvoidStringlyTypedApis = "ECS0006";
internal const string ExpressCallbacksWithDelegates = "ECS0007";
internal const string UseNullConditionalOperatorForEventInvocations = "ECS0008";
diff --git a/src/EffectiveCSharp.Analyzers/Common/DotNet.cs b/src/EffectiveCSharp.Analyzers/Common/DotNet.cs
new file mode 100644
index 0000000..3a18cf7
--- /dev/null
+++ b/src/EffectiveCSharp.Analyzers/Common/DotNet.cs
@@ -0,0 +1,93 @@
+namespace EffectiveCSharp.Analyzers.Common;
+
+internal static class DotNet
+{
+ /// For use with feature detection.
+ /// When using this with .
+ internal static class Versions
+ {
+ internal static readonly Version DotNet6 = new(6, 0, 0, 0);
+ internal static readonly Version DotNet5 = new(5, 0, 0, 0);
+ internal static readonly Version DotNet46 = new(4, 6);
+ internal static readonly Version DotNet47 = new(4, 7);
+ internal static readonly Version DotNet48 = new(4, 8);
+ internal static readonly Version DotNet40 = new(4, 0, 0, 0);
+ }
+
+ /*
+ References
+ https://learn.microsoft.com/en-us/lifecycle/products/microsoft-net-framework
+ https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-framework
+ https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core
+ https://stackoverflow.com/questions/247621/what-are-the-correct-version-numbers-for-c/38506668#38506668
+
+ | C# Version | VS Version | .NET Version | CLR Version | Release Date | End of Support |
+ |------------|-------------------|---------------|-------------|--------------|----------------|
+ | 1.0 | 2002 | 1.0 | 1.0 | Feb 2002 | N/A |
+ | 1.2 | 2003 | 1.1 | 1.1 | Apr 2003 | N/A |
+ | 2.0 | 2005 | 2.0 | 2.0 | Nov 2005 | N/A |
+ | 3.0 | 2008 | 3.5 | 2.0 | Nov 2007 | N/A |
+ | 4.0 | 2010 | 4.0 | 4 | Apr 2010 | N/A |
+ | 5.0 | 2012 | 4.5 | 4 | Aug 2012 | N/A |
+ | 6.0 | 2015 | 4.6 | 4 | Jul 2015 | N/A |
+ | 7.0 | 2017 | 4.6.2 | 4 | Mar 2017 | N/A |
+ | 7.0 | 2017 | .NET Core 1.0 | N/A | Mar 2017 | Jun 2019 |
+ | 7.1 | 2017 (v15.3) | 4.6.2 | 4 | Aug 2017 | N/A |
+ | 7.1 | 2017 (v15.3) | .NET Core 2.0 | N/A | Aug 2017 | Oct 2018 |
+ | 7.2 | 2017 (v15.5) | 4.7.2 | 4 | Dec 2017 | N/A |
+ | 7.3 | 2017 (v15.7) | 4.7.2 | 4 | May 2018 | N/A |
+ | 7.3 | 2017 (v15.7) | .NET Core 2.1 | N/A | May 2018 | Aug 2021 |
+ | 8.0 | 2019 | 4.8 | 4 | Apr 2019 | N/A |
+ | 8.0 | 2019 | .NET Core 3.0 | N/A | Sep 2019 | Mar 2020 |
+ | 8.0 | 2019 | .NET Core 3.1 | N/A | Dec 2019 | Dec 2022 |
+ | 9.0 | 2020 | .NET 5 | N/A | Nov 2020 | May 2022 |
+ | 10.0 | 2021 | .NET 6 | N/A | Nov 2021 | Nov 2024 |
+ | 11.0 | 2022 (17.4) | .NET 7 | N/A | Nov 2022 | May 2024 |
+ | 12.0 | 2023 (17.8) | .NET 8 | N/A | Nov 2023 | Nov 2026 |
+ */
+ internal static class LangVersion
+ {
+ internal static LanguageVersion? FromDotNetVersion(Version? version)
+ {
+ return version?.Major switch
+ {
+ 3 when version.Minor == 5 => LanguageVersion.CSharp3,
+
+ // .NET Framework versions
+ 4 when version.Minor == 8 => LanguageVersion.CSharp8,
+ 4 when version is { Minor: 7, Build: 2 } => LanguageVersion.CSharp7_3,
+ 4 when version is { Minor: 7, Build: 1 } => LanguageVersion.CSharp7_1,
+ 4 when version is { Minor: 7 } => LanguageVersion.CSharp7,
+ 4 when version is { Minor: 6, Build: 2 } => LanguageVersion.CSharp7,
+ 4 when version is { Minor: 6 } => LanguageVersion.CSharp6,
+
+ // .NET Core versions
+ 5 => LanguageVersion.CSharp9,
+ 6 => LanguageVersion.CSharp10,
+ 7 =>
+
+ // REVIEW: This should be CSharp11, but it's not available in the enum
+ LanguageVersion.CSharp10,
+ 8 =>
+
+ // REVIEW: This should be CSharp12, but it's not available in the enum
+ LanguageVersion.CSharp10,
+
+ 9 =>
+
+ // REVIEW: This should be CSharp12, but it's not available in the enum
+ LanguageVersion.CSharp10,
+
+ // .NET Core specific versions
+ 3 when version.Minor == 1 => LanguageVersion.CSharp8,
+ 3 when version.Minor == 0 => LanguageVersion.CSharp8,
+ 2 when version.Minor == 2 => LanguageVersion.CSharp7_3,
+ 2 when version.Minor == 1 => LanguageVersion.CSharp7_3,
+ 2 when version.Minor == 0 => LanguageVersion.CSharp7_1,
+ 1 when version.Minor == 1 => LanguageVersion.CSharp7,
+ 1 when version.Minor == 0 => LanguageVersion.CSharp7,
+ _ => null,
+ };
+ }
+ }
+}
diff --git a/src/EffectiveCSharp.Analyzers/EffectiveCSharp.Analyzers.csproj b/src/EffectiveCSharp.Analyzers/EffectiveCSharp.Analyzers.csproj
index aaf9b0b..3850718 100644
--- a/src/EffectiveCSharp.Analyzers/EffectiveCSharp.Analyzers.csproj
+++ b/src/EffectiveCSharp.Analyzers/EffectiveCSharp.Analyzers.csproj
@@ -52,4 +52,8 @@
+
+
+
+
diff --git a/src/EffectiveCSharp.Analyzers/FormattableStringForCultureSpecificStringsAnalyzer.cs b/src/EffectiveCSharp.Analyzers/FormattableStringForCultureSpecificStringsAnalyzer.cs
new file mode 100644
index 0000000..3c645eb
--- /dev/null
+++ b/src/EffectiveCSharp.Analyzers/FormattableStringForCultureSpecificStringsAnalyzer.cs
@@ -0,0 +1,191 @@
+namespace EffectiveCSharp.Analyzers;
+
+///
+/// A for Effective C# Item #5 - Use FormattableString for culture specific strings.
+///
+///
+[DiagnosticAnalyzer(LanguageNames.CSharp)]
+public class FormattableStringForCultureSpecificStringsAnalyzer : DiagnosticAnalyzer
+{
+ private static readonly LocalizableString Title = "Prefer FormattableString or string.Create for culture-specific strings";
+ private static readonly LocalizableString MessageFormat = "Use '{0}' instead of '{1}' for culture-specific interpolated strings";
+ private static readonly DiagnosticDescriptor Rule = new(
+ DiagnosticIds.PreferFormattableStringForCultureSpecificStrings,
+ Title,
+ MessageFormat,
+ Categories.Globalization,
+ DiagnosticSeverity.Info,
+ isEnabledByDefault: true,
+ helpLinkUri: $"https://github.com/rjmurillo/EffectiveCSharp.Analyzers/blob/{ThisAssembly.GitCommitId}/docs/rules/{DiagnosticIds.PreferFormattableStringForCultureSpecificStrings}.md");
+
+ ///
+ public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule);
+
+ ///
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+
+ context.RegisterCompilationStartAction(compilationContext =>
+ {
+ // String interpolation was introduced in C# 6
+ if (!compilationContext.Compilation.IsCSharpVersionOrLater(LanguageVersion.CSharp6))
+ {
+ return;
+ }
+
+ compilationContext.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.InterpolatedStringExpression);
+ });
+ }
+
+ private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
+ {
+ InterpolatedStringExpressionSyntax interpolatedString = (InterpolatedStringExpressionSyntax)context.Node;
+ SyntaxNode? parent = interpolatedString.Parent;
+
+ if (parent == null || IsSimpleStringConcatenation(interpolatedString, context))
+ {
+ return;
+ }
+
+ ITypeSymbol? targetType = parent switch
+ {
+ AssignmentExpressionSyntax assignment => GetAssignmentTargetType(context, assignment),
+ EqualsValueClauseSyntax equalsValueClause => GetEqualsValueClauseTargetType(context, equalsValueClause),
+ ConditionalExpressionSyntax conditionalExpression => GetConditionalExpressionTargetType(context, conditionalExpression),
+ ParenthesizedLambdaExpressionSyntax or SimpleLambdaExpressionSyntax => GetLambdaTargetType(context, parent),
+ ArgumentSyntax argument => GetArgumentTargetType(context, argument),
+ _ => null,
+ };
+
+ if (targetType?.SpecialType == SpecialType.System_String)
+ {
+ (Version? dotNetVersion, _, LanguageVersion? compilerLanguageVersion) = context.Compilation.GetVersions();
+ if (compilerLanguageVersion is null || dotNetVersion is null)
+ {
+ return;
+ }
+
+ /*
+ * To align the analyzer with the guidance provided by Stephen Toub for .NET 6 and later,
+ * we should favor `string.Create` over `FormattableString` when formatting culture-specific
+ * strings.
+ *
+ * See https://devblogs.microsoft.com/dotnet/string-interpolation-in-c-10-and-net-6/
+ */
+ switch (compilerLanguageVersion)
+ {
+ // string.Create was introduced in C# 10 and .NET 6
+ case >= LanguageVersion.CSharp10 when dotNetVersion >= DotNet.Versions.DotNet6:
+ // Favor `string.Create`
+ ReportDiagnostic(context, interpolatedString, "string.Create", "string");
+ break;
+
+ // Pre-.NET 6, favor FormattableString
+ case >= LanguageVersion.CSharp9 when dotNetVersion >= DotNet.Versions.DotNet5:
+ ReportDiagnostic(context, interpolatedString, "FormattableString", "string");
+ break;
+
+ // Interpolated strings were introduced in C# 6 and .NET Framework 4.6, but we don't have fancy features
+ case >= LanguageVersion.CSharp6 when dotNetVersion >= DotNet.Versions.DotNet46:
+ ReportDiagnostic(context, interpolatedString, "string.Format", "string");
+ break;
+ }
+ }
+ }
+
+ private static bool IsSimpleStringConcatenation(InterpolatedStringExpressionSyntax interpolatedString, SyntaxNodeAnalysisContext context)
+ {
+ for (int i = 0; i < interpolatedString.Contents.Count; i++)
+ {
+ InterpolatedStringContentSyntax content = interpolatedString.Contents[i];
+ if (content is not InterpolationSyntax interpolation)
+ {
+ continue;
+ }
+
+ TypeInfo typeInfo = context.SemanticModel.GetTypeInfo(interpolation.Expression, context.CancellationToken);
+ if (typeInfo.Type?.SpecialType != SpecialType.System_String
+ || ContainsComplexFormatting(interpolation.Expression))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static bool ContainsComplexFormatting(ExpressionSyntax expression)
+ {
+ // Check if the expression contains any method invocations or more complex operations
+ return expression is InvocationExpressionSyntax or BinaryExpressionSyntax;
+ }
+
+ private static ITypeSymbol? GetAssignmentTargetType(SyntaxNodeAnalysisContext context, AssignmentExpressionSyntax assignment)
+ {
+ return context.SemanticModel.GetTypeInfo(assignment.Left, context.CancellationToken).Type;
+ }
+
+ private static ITypeSymbol? GetEqualsValueClauseTargetType(SyntaxNodeAnalysisContext context, EqualsValueClauseSyntax equalsValueClause)
+ {
+ SyntaxNode? declaration = equalsValueClause.Parent;
+ switch (declaration)
+ {
+ case VariableDeclaratorSyntax variableDeclarator:
+ ISymbol? symbol = context.SemanticModel.GetDeclaredSymbol(variableDeclarator, context.CancellationToken);
+ ITypeSymbol? typeSymbol = (symbol as ILocalSymbol)?.Type ?? (symbol as IFieldSymbol)?.Type;
+
+ // Check if the type is a generic (like Func) and unpack the target type
+ if (typeSymbol is INamedTypeSymbol { IsGenericType: true } namedTypeSymbol)
+ {
+ ITypeSymbol? returnType = namedTypeSymbol.TypeArguments.FirstOrDefault();
+ if (returnType?.SpecialType == SpecialType.System_String)
+ {
+ return returnType;
+ }
+ }
+
+ return typeSymbol;
+ case PropertyDeclarationSyntax propertyDeclaration:
+ return context.SemanticModel.GetTypeInfo(propertyDeclaration.Type, context.CancellationToken).Type;
+ default:
+ return null;
+ }
+ }
+
+ private static ITypeSymbol? GetConditionalExpressionTargetType(SyntaxNodeAnalysisContext context, ConditionalExpressionSyntax conditionalExpression)
+ {
+ return context.SemanticModel.GetTypeInfo(conditionalExpression, context.CancellationToken).Type;
+ }
+
+ private static ITypeSymbol? GetLambdaTargetType(SyntaxNodeAnalysisContext context, SyntaxNode parent)
+ {
+ // Lambdas are usually within EqualsValueClauseSyntax, so we can reuse the method
+ if (parent.Parent is EqualsValueClauseSyntax lambdaParent)
+ {
+ return GetEqualsValueClauseTargetType(context, lambdaParent);
+ }
+
+ return null;
+ }
+
+ private static ITypeSymbol? GetArgumentTargetType(SyntaxNodeAnalysisContext context, ArgumentSyntax argument)
+ {
+ if (argument.Parent?.Parent is InvocationExpressionSyntax methodInvocation
+ && context.SemanticModel.GetSymbolInfo(methodInvocation, context.CancellationToken).Symbol is IMethodSymbol methodSymbol
+ && string.Equals(methodSymbol.ContainingType.Name, "StringBuilder", StringComparison.Ordinal)
+ && string.Equals(methodSymbol.ContainingNamespace.ToDisplayString(), "System.Text", StringComparison.Ordinal))
+ {
+ return context.Compilation.GetTypeByMetadataName("System.String");
+ }
+
+ return null;
+ }
+
+ private static void ReportDiagnostic(SyntaxNodeAnalysisContext context, InterpolatedStringExpressionSyntax interpolatedString, string suggestion, string original)
+ {
+ Diagnostic diagnostic = interpolatedString.GetLocation().CreateDiagnostic(Rule, suggestion, original);
+ context.ReportDiagnostic(diagnostic);
+ }
+}
diff --git a/src/EffectiveCSharp.Analyzers/FormattableStringForCultureSpecificStringsCodeFixProvider.cs b/src/EffectiveCSharp.Analyzers/FormattableStringForCultureSpecificStringsCodeFixProvider.cs
new file mode 100644
index 0000000..7f658a2
--- /dev/null
+++ b/src/EffectiveCSharp.Analyzers/FormattableStringForCultureSpecificStringsCodeFixProvider.cs
@@ -0,0 +1,144 @@
+namespace EffectiveCSharp.Analyzers;
+
+///
+/// A that provides a code fix for the .
+///
+///
+[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(FormattableStringForCultureSpecificStringsCodeFixProvider))]
+[Shared]
+public class FormattableStringForCultureSpecificStringsCodeFixProvider : CodeFixProvider
+{
+ private static readonly string Title = "Use string.Create or FormattableString";
+
+ ///
+ public sealed override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(DiagnosticIds.PreferFormattableStringForCultureSpecificStrings);
+
+ ///
+ public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
+
+ ///
+ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
+ {
+ SemanticModel? semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
+
+ if (semanticModel == null)
+ {
+ return;
+ }
+
+ SyntaxNode? root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
+ Diagnostic diagnostic = context.Diagnostics[0];
+ TextSpan diagnosticSpan = diagnostic.Location.SourceSpan;
+
+ // Find the interpolated string expression identified by the diagnostic
+ InterpolatedStringExpressionSyntax? interpolatedString = root?.FindToken(diagnosticSpan.Start).Parent?.AncestorsAndSelf().OfType().First();
+
+ if (interpolatedString == null)
+ {
+ return;
+ }
+
+ Compilation compilation = semanticModel.Compilation;
+
+ (Version? dotNetVersion, _, LanguageVersion? compilerLanguageVersion) = compilation.GetVersions();
+ if (compilerLanguageVersion is null || dotNetVersion is null)
+ {
+ return;
+ }
+
+ // REVIEW: A similar version of this logic is in the analyzer as well
+ switch (compilerLanguageVersion)
+ {
+ // string.Create was introduced in C# 10 and .NET 6
+ // .NET 6+, favor `string.Create`
+ case >= LanguageVersion.CSharp10 when dotNetVersion >= DotNet.Versions.DotNet6:
+
+ // Pre-.NET 6, favor FormattableString
+ case >= LanguageVersion.CSharp9 when dotNetVersion >= DotNet.Versions.DotNet5:
+ context.RegisterCodeFix(
+ CodeAction.Create(
+ title: Title,
+ createChangedSolution: c => UseCultureSpecificStringAsync(context.Document, semanticModel, interpolatedString, c),
+ equivalenceKey: Title),
+ diagnostic);
+ break;
+ }
+ }
+
+ private static async Task UseCultureSpecificStringAsync(
+ Document document,
+ SemanticModel semanticModel,
+ InterpolatedStringExpressionSyntax interpolatedString,
+ CancellationToken cancellationToken)
+ {
+ SyntaxNode? root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
+
+ if (root == null)
+ {
+ return document.Project.Solution;
+ }
+
+ Compilation compilation = semanticModel.Compilation;
+
+ (Version? dotNetVersion, _, LanguageVersion? compilerLanguageVersion) = compilation.GetVersions();
+ if (compilerLanguageVersion is null || dotNetVersion is null)
+ {
+ return document.Project.Solution;
+ }
+
+ ExpressionSyntax? newExpression = null;
+
+ // REVIEW: A similar version of this logic is in the analyzer as well
+ switch (compilerLanguageVersion)
+ {
+ // string.Create was introduced in C# 10 and .NET 6
+ // .NET 6+, favor `string.Create`
+ case >= LanguageVersion.CSharp10 when dotNetVersion >= DotNet.Versions.DotNet6:
+
+ newExpression = CreateStringCreateExpression(interpolatedString);
+ break;
+
+ // Pre-.NET 6, favor FormattableString
+ case >= LanguageVersion.CSharp9 when dotNetVersion >= DotNet.Versions.DotNet5:
+ newExpression = CreateFormattableStringWithCurrentCultureExpression(interpolatedString);
+ break;
+ }
+
+ if (newExpression == null)
+ {
+ return document.Project.Solution;
+ }
+
+ SyntaxNode newRoot = root.ReplaceNode(interpolatedString, newExpression);
+
+ return document.WithSyntaxRoot(newRoot).Project.Solution;
+ }
+
+ private static InvocationExpressionSyntax CreateStringCreateExpression(InterpolatedStringExpressionSyntax interpolatedString)
+ {
+ // Replace with string.Create(CultureInfo.CurrentCulture, ...) for .NET 6 and later
+ ArgumentListSyntax arguments = SyntaxFactory.ArgumentList(
+ SyntaxFactory.SeparatedList(
+ new[]
+ {
+ SyntaxFactory.Argument(SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName("CultureInfo"), SyntaxFactory.IdentifierName("CurrentCulture"))),
+ SyntaxFactory.Argument(interpolatedString),
+ }));
+
+ InvocationExpressionSyntax invocation = SyntaxFactory.InvocationExpression(
+ SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.StringKeyword)), SyntaxFactory.IdentifierName("Create")),
+ arguments);
+
+ return invocation;
+ }
+
+ private static InvocationExpressionSyntax CreateFormattableStringWithCurrentCultureExpression(InterpolatedStringExpressionSyntax interpolatedString)
+ {
+ // Replace with FormattableString.CurrentCulture(...) just to be explicit about what was happening before
+ InvocationExpressionSyntax invocation = SyntaxFactory.InvocationExpression(
+ SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName("FormattableString"), SyntaxFactory.IdentifierName("CurrentCulture")),
+ SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(new[] { SyntaxFactory.Argument(interpolatedString) })));
+
+ return invocation;
+ }
+}
diff --git a/src/EffectiveCSharp.Analyzers/SquiggleCop.Baseline.yaml b/src/EffectiveCSharp.Analyzers/SquiggleCop.Baseline.yaml
index 6dd906e..512403d 100644
--- a/src/EffectiveCSharp.Analyzers/SquiggleCop.Baseline.yaml
+++ b/src/EffectiveCSharp.Analyzers/SquiggleCop.Baseline.yaml
@@ -158,7 +158,7 @@
- {Id: CA2013, Title: Do not use ReferenceEquals with value types, Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2014, Title: Do not use stackalloc in loops, Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2015, Title: Do not define finalizers for types derived from MemoryManager, Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: CA2016, Title: Forward the 'CancellationToken' parameter to methods, Category: Reliability, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: false}
+- {Id: CA2016, Title: Forward the 'CancellationToken' parameter to methods, Category: Reliability, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2017, Title: Parameter count mismatch, Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2018, Title: "'Buffer.BlockCopy' expects the number of bytes to be copied for the 'count' argument", Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2019, Title: Improper 'ThreadStatic' field initialization, Category: Reliability, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
@@ -444,7 +444,7 @@
- {Id: MA0023, Title: Add RegexOptions.ExplicitCapture, Category: Performance, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0024, Title: Use an explicit StringComparer when possible, Category: Usage, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0025, Title: Implement the functionality instead of throwing NotImplementedException, Category: Design, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: MA0026, Title: Fix TODO comment, Category: Design, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, None], IsEverSuppressed: true}
+- {Id: MA0026, Title: Fix TODO comment, Category: Design, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: MA0027, Title: Prefer rethrowing an exception implicitly, Category: Usage, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0028, Title: Optimize StringBuilder usage, Category: Performance, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0029, Title: Combine LINQ methods, Category: Performance, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
@@ -457,7 +457,7 @@
- {Id: MA0037, Title: Remove empty statement, Category: Usage, DefaultSeverity: Error, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0038, Title: 'Make method static (deprecated, use CA1822 instead)', Category: Design, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0039, Title: Do not write your own certificate validation method, Category: Security, DefaultSeverity: Error, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: MA0040, Title: Forward the CancellationToken parameter to methods that take one, Category: Usage, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: false}
+- {Id: MA0040, Title: Forward the CancellationToken parameter to methods that take one, Category: Usage, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0041, Title: 'Make property static (deprecated, use CA1822 instead)', Category: Design, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0042, Title: Do not use blocking calls in an async method, Category: Design, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0043, Title: Use nameof operator in ArgumentException, Category: Usage, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
@@ -578,7 +578,6 @@
- {Id: MA0158, Title: Use System.Threading.Lock, Category: Performance, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0159, Title: Use 'Order' instead of 'OrderBy', Category: Performance, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0160, Title: Use ContainsKey instead of TryGetValue, Category: Performance, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
-- {Id: POLYSP0003, Title: Unsupported C# language version, Category: Microsoft.CodeAnalysis.CSharp.CSharpParseOptions, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: RCS1001, Title: Add braces (when expression spans over multiple lines), Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: RCS1002, Title: Remove braces, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: false}
- {Id: RCS1002FadeOut, Title: Remove braces, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
@@ -689,8 +688,8 @@
- {Id: RCS1114FadeOut, Title: Remove redundant delegate creation, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: RCS1118, Title: Mark local variable as const, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: RCS1123, Title: Add parentheses when necessary, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
-- {Id: RCS1124, Title: Inline local variable, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
-- {Id: RCS1124FadeOut, Title: Inline local variable, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
+- {Id: RCS1124, Title: Inline local variable, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: true}
+- {Id: RCS1124FadeOut, Title: Inline local variable, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: true}
- {Id: RCS1126, Title: Add braces to if-else, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: RCS1128, Title: Use coalesce expression, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: RCS1129, Title: Remove redundant field initialization, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: true}
@@ -899,7 +898,7 @@
- {Id: S113, Title: Files should end with a newline, Category: Minor Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: S1133, Title: Deprecated code should be removed, Category: Info Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: S1134, Title: Track uses of "FIXME" tags, Category: Major Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: S1135, Title: Track uses of "TODO" tags, Category: Info Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: false}
+- {Id: S1135, Title: Track uses of "TODO" tags, Category: Info Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: S1144, Title: Unused private types or members should be removed, Category: Major Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: S1147, Title: Exit methods should not be called, Category: Blocker Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: S1151, Title: '"switch case" clauses should not have too many lines of code', Category: Major Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
@@ -1371,7 +1370,7 @@
- {Id: SA1027, Title: Use tabs correctly, Category: StyleCop.CSharp.SpacingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1028, Title: Code should not contain trailing whitespace, Category: StyleCop.CSharp.SpacingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1100, Title: Do not prefix calls with base unless local implementation exists, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: SA1101, Title: Prefix local calls with this, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: false}
+- {Id: SA1101, Title: Prefix local calls with this, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: SA1102, Title: Query clause should follow previous clause, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1103, Title: Query clauses should be on separate lines or all on one line, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1104, Title: Query clause should begin on new line when previous clause spans multiple lines, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
@@ -1438,7 +1437,7 @@
- {Id: SA1306, Title: Field names should begin with lower-case letter, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1307, Title: Accessible fields should begin with upper-case letter, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1308, Title: Variable names should not be prefixed, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: SA1309, Title: Field names should not begin with underscore, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, None], IsEverSuppressed: true}
+- {Id: SA1309, Title: Field names should not begin with underscore, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: SA1310, Title: Field names should not contain underscore, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1311, Title: Static readonly fields should begin with upper-case letter, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1312, Title: Variable names should begin with lower-case letter, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
@@ -1481,7 +1480,7 @@
- {Id: SA1518, Title: Use line endings correctly at end of file, Category: StyleCop.CSharp.LayoutRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1519, Title: Braces should not be omitted from multi-line child statement, Category: StyleCop.CSharp.LayoutRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1520, Title: Use braces consistently, Category: StyleCop.CSharp.LayoutRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: SA1600, Title: Elements should be documented, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: true}
+- {Id: SA1600, Title: Elements should be documented, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: true}
- {Id: SA1601, Title: Partial elements should be documented, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1602, Title: Enumeration items should be documented, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1603, Title: Documentation should contain valid XML, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
@@ -1514,7 +1513,7 @@
- {Id: SA1630, Title: Documentation text should contain whitespace, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: SA1631, Title: Documentation should meet character percentage, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: SA1632, Title: Documentation text should meet minimum character length, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
-- {Id: SA1633, Title: File should have header, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: true}
+- {Id: SA1633, Title: File should have header, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: true}
- {Id: SA1634, Title: File header should show copyright, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1635, Title: File header should have copyright text, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1636, Title: File header copyright text should match, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
diff --git a/src/tools/Dogfood/SquiggleCop.Baseline.yaml b/src/tools/Dogfood/SquiggleCop.Baseline.yaml
index 87019e0..af7048a 100644
--- a/src/tools/Dogfood/SquiggleCop.Baseline.yaml
+++ b/src/tools/Dogfood/SquiggleCop.Baseline.yaml
@@ -158,7 +158,7 @@
- {Id: CA2013, Title: Do not use ReferenceEquals with value types, Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2014, Title: Do not use stackalloc in loops, Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2015, Title: Do not define finalizers for types derived from MemoryManager, Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: CA2016, Title: Forward the 'CancellationToken' parameter to methods, Category: Reliability, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: false}
+- {Id: CA2016, Title: Forward the 'CancellationToken' parameter to methods, Category: Reliability, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2017, Title: Parameter count mismatch, Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2018, Title: "'Buffer.BlockCopy' expects the number of bytes to be copied for the 'count' argument", Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2019, Title: Improper 'ThreadStatic' field initialization, Category: Reliability, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
@@ -301,6 +301,7 @@
- {Id: ECS0001, Title: Prefer implicitly typed local variables, Category: Style, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: ECS0002, Title: Prefer readonly over const, Category: Maintainability, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: true}
- {Id: ECS0004, Title: Replace string.Format with interpolated string, Category: Style, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
+- {Id: ECS0005, Title: Prefer FormattableString or string.Create for culture-specific strings, Category: Globalization, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: ECS0006, Title: Avoid stringly-typed APIs, Category: Refactoring, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: ECS0007, Title: Express callbacks with delegates, Category: Design, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: ECS0008, Title: Use the Null Conditional Operator for Event Invocations, Category: Usage, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
@@ -451,7 +452,7 @@
- {Id: MA0023, Title: Add RegexOptions.ExplicitCapture, Category: Performance, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0024, Title: Use an explicit StringComparer when possible, Category: Usage, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0025, Title: Implement the functionality instead of throwing NotImplementedException, Category: Design, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: MA0026, Title: Fix TODO comment, Category: Design, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, None], IsEverSuppressed: true}
+- {Id: MA0026, Title: Fix TODO comment, Category: Design, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: MA0027, Title: Prefer rethrowing an exception implicitly, Category: Usage, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0028, Title: Optimize StringBuilder usage, Category: Performance, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0029, Title: Combine LINQ methods, Category: Performance, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
@@ -464,7 +465,7 @@
- {Id: MA0037, Title: Remove empty statement, Category: Usage, DefaultSeverity: Error, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0038, Title: 'Make method static (deprecated, use CA1822 instead)', Category: Design, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0039, Title: Do not write your own certificate validation method, Category: Security, DefaultSeverity: Error, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: MA0040, Title: Forward the CancellationToken parameter to methods that take one, Category: Usage, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: false}
+- {Id: MA0040, Title: Forward the CancellationToken parameter to methods that take one, Category: Usage, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0041, Title: 'Make property static (deprecated, use CA1822 instead)', Category: Design, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0042, Title: Do not use blocking calls in an async method, Category: Design, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0043, Title: Use nameof operator in ArgumentException, Category: Usage, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
@@ -585,7 +586,6 @@
- {Id: MA0158, Title: Use System.Threading.Lock, Category: Performance, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0159, Title: Use 'Order' instead of 'OrderBy', Category: Performance, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0160, Title: Use ContainsKey instead of TryGetValue, Category: Performance, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
-- {Id: POLYSP0003, Title: Unsupported C# language version, Category: Microsoft.CodeAnalysis.CSharp.CSharpParseOptions, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: RCS1001, Title: Add braces (when expression spans over multiple lines), Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: RCS1002, Title: Remove braces, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: false}
- {Id: RCS1002FadeOut, Title: Remove braces, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
@@ -696,8 +696,8 @@
- {Id: RCS1114FadeOut, Title: Remove redundant delegate creation, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: RCS1118, Title: Mark local variable as const, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: RCS1123, Title: Add parentheses when necessary, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
-- {Id: RCS1124, Title: Inline local variable, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
-- {Id: RCS1124FadeOut, Title: Inline local variable, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
+- {Id: RCS1124, Title: Inline local variable, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: true}
+- {Id: RCS1124FadeOut, Title: Inline local variable, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: true}
- {Id: RCS1126, Title: Add braces to if-else, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: RCS1128, Title: Use coalesce expression, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: RCS1129, Title: Remove redundant field initialization, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: true}
@@ -906,7 +906,7 @@
- {Id: S113, Title: Files should end with a newline, Category: Minor Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: S1133, Title: Deprecated code should be removed, Category: Info Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: S1134, Title: Track uses of "FIXME" tags, Category: Major Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: S1135, Title: Track uses of "TODO" tags, Category: Info Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: false}
+- {Id: S1135, Title: Track uses of "TODO" tags, Category: Info Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: S1144, Title: Unused private types or members should be removed, Category: Major Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: S1147, Title: Exit methods should not be called, Category: Blocker Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: S1151, Title: '"switch case" clauses should not have too many lines of code', Category: Major Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
@@ -1378,7 +1378,7 @@
- {Id: SA1027, Title: Use tabs correctly, Category: StyleCop.CSharp.SpacingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1028, Title: Code should not contain trailing whitespace, Category: StyleCop.CSharp.SpacingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1100, Title: Do not prefix calls with base unless local implementation exists, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: SA1101, Title: Prefix local calls with this, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: false}
+- {Id: SA1101, Title: Prefix local calls with this, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: SA1102, Title: Query clause should follow previous clause, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1103, Title: Query clauses should be on separate lines or all on one line, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1104, Title: Query clause should begin on new line when previous clause spans multiple lines, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
@@ -1445,7 +1445,7 @@
- {Id: SA1306, Title: Field names should begin with lower-case letter, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1307, Title: Accessible fields should begin with upper-case letter, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1308, Title: Variable names should not be prefixed, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: SA1309, Title: Field names should not begin with underscore, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, None], IsEverSuppressed: true}
+- {Id: SA1309, Title: Field names should not begin with underscore, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: SA1310, Title: Field names should not contain underscore, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1311, Title: Static readonly fields should begin with upper-case letter, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1312, Title: Variable names should begin with lower-case letter, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
@@ -1488,7 +1488,7 @@
- {Id: SA1518, Title: Use line endings correctly at end of file, Category: StyleCop.CSharp.LayoutRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1519, Title: Braces should not be omitted from multi-line child statement, Category: StyleCop.CSharp.LayoutRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1520, Title: Use braces consistently, Category: StyleCop.CSharp.LayoutRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: SA1600, Title: Elements should be documented, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: true}
+- {Id: SA1600, Title: Elements should be documented, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: true}
- {Id: SA1601, Title: Partial elements should be documented, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1602, Title: Enumeration items should be documented, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1603, Title: Documentation should contain valid XML, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
@@ -1521,7 +1521,7 @@
- {Id: SA1630, Title: Documentation text should contain whitespace, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: SA1631, Title: Documentation should meet character percentage, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: SA1632, Title: Documentation text should meet minimum character length, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
-- {Id: SA1633, Title: File should have header, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: true}
+- {Id: SA1633, Title: File should have header, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: true}
- {Id: SA1634, Title: File header should show copyright, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1635, Title: File header should have copyright text, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1636, Title: File header copyright text should match, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
diff --git a/src/tools/PerfDiff/SquiggleCop.Baseline.yaml b/src/tools/PerfDiff/SquiggleCop.Baseline.yaml
index 8ee2064..8a1eeb1 100644
--- a/src/tools/PerfDiff/SquiggleCop.Baseline.yaml
+++ b/src/tools/PerfDiff/SquiggleCop.Baseline.yaml
@@ -158,7 +158,7 @@
- {Id: CA2013, Title: Do not use ReferenceEquals with value types, Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2014, Title: Do not use stackalloc in loops, Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2015, Title: Do not define finalizers for types derived from MemoryManager, Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: CA2016, Title: Forward the 'CancellationToken' parameter to methods, Category: Reliability, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: false}
+- {Id: CA2016, Title: Forward the 'CancellationToken' parameter to methods, Category: Reliability, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2017, Title: Parameter count mismatch, Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2018, Title: "'Buffer.BlockCopy' expects the number of bytes to be copied for the 'count' argument", Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2019, Title: Improper 'ThreadStatic' field initialization, Category: Reliability, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
@@ -445,7 +445,7 @@
- {Id: MA0023, Title: Add RegexOptions.ExplicitCapture, Category: Performance, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0024, Title: Use an explicit StringComparer when possible, Category: Usage, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0025, Title: Implement the functionality instead of throwing NotImplementedException, Category: Design, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: MA0026, Title: Fix TODO comment, Category: Design, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, None], IsEverSuppressed: true}
+- {Id: MA0026, Title: Fix TODO comment, Category: Design, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: MA0027, Title: Prefer rethrowing an exception implicitly, Category: Usage, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0028, Title: Optimize StringBuilder usage, Category: Performance, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0029, Title: Combine LINQ methods, Category: Performance, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
@@ -458,7 +458,7 @@
- {Id: MA0037, Title: Remove empty statement, Category: Usage, DefaultSeverity: Error, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0038, Title: 'Make method static (deprecated, use CA1822 instead)', Category: Design, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0039, Title: Do not write your own certificate validation method, Category: Security, DefaultSeverity: Error, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: MA0040, Title: Forward the CancellationToken parameter to methods that take one, Category: Usage, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: false}
+- {Id: MA0040, Title: Forward the CancellationToken parameter to methods that take one, Category: Usage, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0041, Title: 'Make property static (deprecated, use CA1822 instead)', Category: Design, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0042, Title: Do not use blocking calls in an async method, Category: Design, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0043, Title: Use nameof operator in ArgumentException, Category: Usage, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
@@ -579,7 +579,6 @@
- {Id: MA0158, Title: Use System.Threading.Lock, Category: Performance, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0159, Title: Use 'Order' instead of 'OrderBy', Category: Performance, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0160, Title: Use ContainsKey instead of TryGetValue, Category: Performance, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
-- {Id: POLYSP0003, Title: Unsupported C# language version, Category: Microsoft.CodeAnalysis.CSharp.CSharpParseOptions, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: RCS1001, Title: Add braces (when expression spans over multiple lines), Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: RCS1002, Title: Remove braces, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: false}
- {Id: RCS1002FadeOut, Title: Remove braces, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
@@ -900,7 +899,7 @@
- {Id: S113, Title: Files should end with a newline, Category: Minor Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: S1133, Title: Deprecated code should be removed, Category: Info Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: S1134, Title: Track uses of "FIXME" tags, Category: Major Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: S1135, Title: Track uses of "TODO" tags, Category: Info Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note, None], IsEverSuppressed: true}
+- {Id: S1135, Title: Track uses of "TODO" tags, Category: Info Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Note, None], IsEverSuppressed: true}
- {Id: S1144, Title: Unused private types or members should be removed, Category: Major Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: S1147, Title: Exit methods should not be called, Category: Blocker Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: S1151, Title: '"switch case" clauses should not have too many lines of code', Category: Major Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
@@ -1372,7 +1371,7 @@
- {Id: SA1027, Title: Use tabs correctly, Category: StyleCop.CSharp.SpacingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1028, Title: Code should not contain trailing whitespace, Category: StyleCop.CSharp.SpacingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1100, Title: Do not prefix calls with base unless local implementation exists, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: SA1101, Title: Prefix local calls with this, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: true}
+- {Id: SA1101, Title: Prefix local calls with this, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: true}
- {Id: SA1102, Title: Query clause should follow previous clause, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1103, Title: Query clauses should be on separate lines or all on one line, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1104, Title: Query clause should begin on new line when previous clause spans multiple lines, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
@@ -1439,7 +1438,7 @@
- {Id: SA1306, Title: Field names should begin with lower-case letter, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1307, Title: Accessible fields should begin with upper-case letter, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1308, Title: Variable names should not be prefixed, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, None], IsEverSuppressed: true}
-- {Id: SA1309, Title: Field names should not begin with underscore, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, None], IsEverSuppressed: true}
+- {Id: SA1309, Title: Field names should not begin with underscore, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: SA1310, Title: Field names should not contain underscore, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1311, Title: Static readonly fields should begin with upper-case letter, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1312, Title: Variable names should begin with lower-case letter, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
@@ -1482,7 +1481,7 @@
- {Id: SA1518, Title: Use line endings correctly at end of file, Category: StyleCop.CSharp.LayoutRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1519, Title: Braces should not be omitted from multi-line child statement, Category: StyleCop.CSharp.LayoutRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1520, Title: Use braces consistently, Category: StyleCop.CSharp.LayoutRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: SA1600, Title: Elements should be documented, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note, None], IsEverSuppressed: true}
+- {Id: SA1600, Title: Elements should be documented, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Note, None], IsEverSuppressed: true}
- {Id: SA1601, Title: Partial elements should be documented, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, None], IsEverSuppressed: true}
- {Id: SA1602, Title: Enumeration items should be documented, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1603, Title: Documentation should contain valid XML, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
@@ -1515,7 +1514,7 @@
- {Id: SA1630, Title: Documentation text should contain whitespace, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: SA1631, Title: Documentation should meet character percentage, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: SA1632, Title: Documentation text should meet minimum character length, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
-- {Id: SA1633, Title: File should have header, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: true}
+- {Id: SA1633, Title: File should have header, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: true}
- {Id: SA1634, Title: File header should show copyright, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1635, Title: File header should have copyright text, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1636, Title: File header copyright text should match, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
diff --git a/tests/EffectiveCSharp.Analyzers.Benchmarks/SquiggleCop.Baseline.yaml b/tests/EffectiveCSharp.Analyzers.Benchmarks/SquiggleCop.Baseline.yaml
index 558190c..e7b8210 100644
--- a/tests/EffectiveCSharp.Analyzers.Benchmarks/SquiggleCop.Baseline.yaml
+++ b/tests/EffectiveCSharp.Analyzers.Benchmarks/SquiggleCop.Baseline.yaml
@@ -158,7 +158,7 @@
- {Id: CA2013, Title: Do not use ReferenceEquals with value types, Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2014, Title: Do not use stackalloc in loops, Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2015, Title: Do not define finalizers for types derived from MemoryManager, Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: CA2016, Title: Forward the 'CancellationToken' parameter to methods, Category: Reliability, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: false}
+- {Id: CA2016, Title: Forward the 'CancellationToken' parameter to methods, Category: Reliability, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2017, Title: Parameter count mismatch, Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2018, Title: "'Buffer.BlockCopy' expects the number of bytes to be copied for the 'count' argument", Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2019, Title: Improper 'ThreadStatic' field initialization, Category: Reliability, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
@@ -445,7 +445,7 @@
- {Id: MA0023, Title: Add RegexOptions.ExplicitCapture, Category: Performance, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0024, Title: Use an explicit StringComparer when possible, Category: Usage, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0025, Title: Implement the functionality instead of throwing NotImplementedException, Category: Design, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: MA0026, Title: Fix TODO comment, Category: Design, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, None], IsEverSuppressed: true}
+- {Id: MA0026, Title: Fix TODO comment, Category: Design, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: MA0027, Title: Prefer rethrowing an exception implicitly, Category: Usage, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0028, Title: Optimize StringBuilder usage, Category: Performance, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0029, Title: Combine LINQ methods, Category: Performance, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
@@ -458,7 +458,7 @@
- {Id: MA0037, Title: Remove empty statement, Category: Usage, DefaultSeverity: Error, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0038, Title: 'Make method static (deprecated, use CA1822 instead)', Category: Design, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0039, Title: Do not write your own certificate validation method, Category: Security, DefaultSeverity: Error, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: MA0040, Title: Forward the CancellationToken parameter to methods that take one, Category: Usage, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: false}
+- {Id: MA0040, Title: Forward the CancellationToken parameter to methods that take one, Category: Usage, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0041, Title: 'Make property static (deprecated, use CA1822 instead)', Category: Design, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0042, Title: Do not use blocking calls in an async method, Category: Design, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0043, Title: Use nameof operator in ArgumentException, Category: Usage, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
@@ -579,7 +579,6 @@
- {Id: MA0158, Title: Use System.Threading.Lock, Category: Performance, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0159, Title: Use 'Order' instead of 'OrderBy', Category: Performance, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0160, Title: Use ContainsKey instead of TryGetValue, Category: Performance, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
-- {Id: POLYSP0003, Title: Unsupported C# language version, Category: Microsoft.CodeAnalysis.CSharp.CSharpParseOptions, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: RCS1001, Title: Add braces (when expression spans over multiple lines), Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: RCS1002, Title: Remove braces, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: false}
- {Id: RCS1002FadeOut, Title: Remove braces, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
@@ -900,7 +899,7 @@
- {Id: S113, Title: Files should end with a newline, Category: Minor Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: S1133, Title: Deprecated code should be removed, Category: Info Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: S1134, Title: Track uses of "FIXME" tags, Category: Major Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: S1135, Title: Track uses of "TODO" tags, Category: Info Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: false}
+- {Id: S1135, Title: Track uses of "TODO" tags, Category: Info Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: S1144, Title: Unused private types or members should be removed, Category: Major Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: S1147, Title: Exit methods should not be called, Category: Blocker Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: S1151, Title: '"switch case" clauses should not have too many lines of code', Category: Major Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
@@ -1372,7 +1371,7 @@
- {Id: SA1027, Title: Use tabs correctly, Category: StyleCop.CSharp.SpacingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1028, Title: Code should not contain trailing whitespace, Category: StyleCop.CSharp.SpacingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1100, Title: Do not prefix calls with base unless local implementation exists, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: SA1101, Title: Prefix local calls with this, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: true}
+- {Id: SA1101, Title: Prefix local calls with this, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: true}
- {Id: SA1102, Title: Query clause should follow previous clause, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1103, Title: Query clauses should be on separate lines or all on one line, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1104, Title: Query clause should begin on new line when previous clause spans multiple lines, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
@@ -1439,7 +1438,7 @@
- {Id: SA1306, Title: Field names should begin with lower-case letter, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1307, Title: Accessible fields should begin with upper-case letter, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1308, Title: Variable names should not be prefixed, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: SA1309, Title: Field names should not begin with underscore, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, None], IsEverSuppressed: true}
+- {Id: SA1309, Title: Field names should not begin with underscore, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: SA1310, Title: Field names should not contain underscore, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1311, Title: Static readonly fields should begin with upper-case letter, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1312, Title: Variable names should begin with lower-case letter, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
@@ -1482,7 +1481,7 @@
- {Id: SA1518, Title: Use line endings correctly at end of file, Category: StyleCop.CSharp.LayoutRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1519, Title: Braces should not be omitted from multi-line child statement, Category: StyleCop.CSharp.LayoutRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1520, Title: Use braces consistently, Category: StyleCop.CSharp.LayoutRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: SA1600, Title: Elements should be documented, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: true}
+- {Id: SA1600, Title: Elements should be documented, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: true}
- {Id: SA1601, Title: Partial elements should be documented, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1602, Title: Enumeration items should be documented, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1603, Title: Documentation should contain valid XML, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
@@ -1515,7 +1514,7 @@
- {Id: SA1630, Title: Documentation text should contain whitespace, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: SA1631, Title: Documentation should meet character percentage, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: SA1632, Title: Documentation text should meet minimum character length, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
-- {Id: SA1633, Title: File should have header, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: true}
+- {Id: SA1633, Title: File should have header, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: true}
- {Id: SA1634, Title: File header should show copyright, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1635, Title: File header should have copyright text, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1636, Title: File header copyright text should match, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
diff --git a/tests/EffectiveCSharp.Analyzers.Tests/FormattableStringForCultureSpecificStringsTests.cs b/tests/EffectiveCSharp.Analyzers.Tests/FormattableStringForCultureSpecificStringsTests.cs
new file mode 100644
index 0000000..d14d9f7
--- /dev/null
+++ b/tests/EffectiveCSharp.Analyzers.Tests/FormattableStringForCultureSpecificStringsTests.cs
@@ -0,0 +1,191 @@
+using CodeFixVerifier = EffectiveCSharp.Analyzers.Tests.Helpers.AnalyzerAndCodeFixVerifier;
+using Verifier = EffectiveCSharp.Analyzers.Tests.Helpers.AnalyzerVerifier;
+
+namespace EffectiveCSharp.Analyzers.Tests;
+
+#pragma warning disable IDE0028 // We cannot simply object creation on TheoryData because we need to convert from object[] to string, the way it is now is cleaner
+
+public class FormattableStringForCultureSpecificStringsTests(ITestOutputHelper output)
+{
+ public static TheoryData TestData()
+ {
+ TheoryData data = new()
+ {
+ // This should trigger the analyzer because it implicitly uses the current culture of the machine
+ """
+ private readonly string _message = {|ECS0005:$"The speed of light is {SpeedOfLight:N3} km/s."|};
+ """,
+
+ // Should trigger the analyzer: property initialization
+ """
+ public string Message { get; set; } = {|ECS0005:$"The speed of light is {SpeedOfLight:N3} km/s."|};
+ """,
+
+ // Should trigger the analyzer: local variable assignment
+ """
+ public string M()
+ {
+ string message = {|ECS0005:$"The speed of light is {SpeedOfLight:N3} km/s."|};
+ return message;
+ }
+ """,
+
+ // This should not trigger the analyzer because no actual formatting has taken place, just instructions about the format
+ """
+ private readonly FormattableString _message = $"The speed of light is {SpeedOfLight:N3} km/s.";
+ """,
+
+ // This should not trigger the analyzer because strings are lowered to a string.Concat
+ """
+ public string M()
+ {
+ string h = "hello";
+ string w = "world";
+ return $"{h}, {w}!";
+ }
+ """,
+ """
+ private static readonly string h = "hello";
+ private static readonly string w = "world";
+ private static readonly string hw = $"{h}, {w}!";
+ """,
+
+ // Conditional interpolated string
+ """
+ private const bool condition = false;
+ private readonly string? _message = condition ? {|ECS0005:$"The speed of light is {SpeedOfLight:N3} km/s."|} : null;
+ """,
+
+ // StringBuilder
+ """
+ private string M()
+ {
+ var builder = new StringBuilder();
+ builder.Append({|ECS0005:$"The speed of light is {SpeedOfLight:N3} km/s."|});
+ return builder.ToString();
+ }
+ """,
+
+ // With formatting
+ """
+ private readonly string _message = {|ECS0005:$"The speed of light is {SpeedOfLight,10:N3} km/s."|};
+ """,
+
+ // Nested interpolated strings
+ """
+ private readonly string _message = {|ECS0005:$"The speed of light is {string.Create(CultureInfo.InvariantCulture, $"{SpeedOfLight:N3}")} km/s."|};
+ """,
+
+ // Complex expressions in interpolated strings
+ """
+ private string M()
+ {
+ return $"The speed of light is {Math.Round(SpeedOfLight, 2):N2} km/s.";
+ }
+ """,
+
+ // Local functions and lambdas
+ """
+ Func lambda = () => {|ECS0005:$"The speed of light is {SpeedOfLight,10:N3} km/s."|};
+ """,
+ };
+
+ // FormattableString was introduced in C# 6.0 and .NET 4.6 with extended capabilities up through C# 9.0
+ // string.Create was introduced in C# 10 and .NET 6
+
+ // REVIEW: There's a similar version of this logic in the analyzer and code fix provider as well
+ return data.WithReferenceAssemblyGroups(p => ReferenceAssemblyCatalog.DotNetCore.Contains(p, StringComparer.Ordinal));
+ }
+
+ [Theory]
+ [MemberData(nameof(TestData))]
+ public async Task Analyzer(string referenceAssemblyGroup, string source)
+ {
+ string code = $$"""
+ public class C
+ {
+ private const double SpeedOfLight = 299_792.458;
+
+ {{source}}
+ }
+ """;
+
+ output.WriteLine(code);
+
+ await Verifier.VerifyAnalyzerAsync(
+ code,
+ referenceAssemblyGroup);
+ }
+
+ [Fact]
+ public async Task CodeFix_Net6()
+ {
+ const string testCode = """
+ public class C
+ {
+ private const double SpeedOfLight = 299_792.458;
+ private readonly string _message = {|ECS0005:$"The speed of light is {SpeedOfLight:N3} km/s."|};
+ public string Message { get; set; } = {|ECS0005:$"The speed of light is {SpeedOfLight:N3} km/s."|};
+ public string M()
+ {
+ string message = {|ECS0005:$"The speed of light is {SpeedOfLight:N3} km/s."|};
+ return message;
+ }
+ public string S()
+ {
+ string h = "hello";
+ string w = "world";
+ return $"{h}, {w}!";
+ }
+ }
+ """;
+
+ const string fixedCode = """
+ public class C
+ {
+ private const double SpeedOfLight = 299_792.458;
+ private readonly string _message = string.Create(CultureInfo.CurrentCulture, $"The speed of light is {SpeedOfLight:N3} km/s.");
+ public string Message { get; set; } = string.Create(CultureInfo.CurrentCulture, $"The speed of light is {SpeedOfLight:N3} km/s.");
+ public string M()
+ {
+ string message = string.Create(CultureInfo.CurrentCulture, $"The speed of light is {SpeedOfLight:N3} km/s.");
+ return message;
+ }
+ public string S()
+ {
+ string h = "hello";
+ string w = "world";
+ return $"{h}, {w}!";
+ }
+ }
+ """;
+
+ await CodeFixVerifier.VerifyCodeFixAsync(testCode, fixedCode, ReferenceAssemblyCatalog.Net60);
+ }
+
+ [Fact]
+ public async Task Analyzer_Net462()
+ {
+ const string testCode = """
+ public class C
+ {
+ private const double SpeedOfLight = 299_792.458;
+ private readonly string _message = {|ECS0005:$"The speed of light is {SpeedOfLight:N3} km/s."|};
+ public string Message { get; set; } = {|ECS0005:$"The speed of light is {SpeedOfLight:N3} km/s."|};
+ public string M()
+ {
+ string message = {|ECS0005:$"The speed of light is {SpeedOfLight:N3} km/s."|};
+ return message;
+ }
+ public string S()
+ {
+ string h = "hello";
+ string w = "world";
+ return $"{h}, {w}!";
+ }
+ }
+ """;
+
+ await Verifier.VerifyAnalyzerAsync(testCode, ReferenceAssemblyCatalog.Net462);
+ }
+}
diff --git a/tests/EffectiveCSharp.Analyzers.Tests/GlobalUsings.cs b/tests/EffectiveCSharp.Analyzers.Tests/GlobalUsings.cs
index b02581c..adada25 100644
--- a/tests/EffectiveCSharp.Analyzers.Tests/GlobalUsings.cs
+++ b/tests/EffectiveCSharp.Analyzers.Tests/GlobalUsings.cs
@@ -1,4 +1,6 @@
global using EffectiveCSharp.Analyzers.Tests.Helpers;
global using Microsoft.CodeAnalysis.CodeFixes;
+global using Microsoft.CodeAnalysis.CSharp;
global using Microsoft.CodeAnalysis.Diagnostics;
global using Microsoft.CodeAnalysis.Testing;
+global using Xunit.Abstractions;
diff --git a/tests/EffectiveCSharp.Analyzers.Tests/Helpers/Test.cs b/tests/EffectiveCSharp.Analyzers.Tests/Helpers/Test.cs
index 26c08fc..a2c8a4b 100644
--- a/tests/EffectiveCSharp.Analyzers.Tests/Helpers/Test.cs
+++ b/tests/EffectiveCSharp.Analyzers.Tests/Helpers/Test.cs
@@ -17,9 +17,13 @@ public Test()
// Add common usings to all test cases to avoid test authoring errors.
const string globalUsings =
"""
- global using System;
- global using System.Collections.Generic;
- global using System.Linq;
+ global using global::System;
+ global using global::System.Collections.Generic;
+ global using global::System.Globalization;
+ global using global::System.IO;
+ global using global::System.Linq;
+ global using global::System.Text;
+ global using global::System.Threading;
""";
TestState.Sources.Add(globalUsings);
diff --git a/tests/EffectiveCSharp.Analyzers.Tests/LanguageVersionTests.cs b/tests/EffectiveCSharp.Analyzers.Tests/LanguageVersionTests.cs
new file mode 100644
index 0000000..590c10a
--- /dev/null
+++ b/tests/EffectiveCSharp.Analyzers.Tests/LanguageVersionTests.cs
@@ -0,0 +1,51 @@
+using EffectiveCSharp.Analyzers.Common;
+
+namespace EffectiveCSharp.Analyzers.Tests;
+
+public class LanguageVersionTests
+{
+ [Theory]
+ [InlineData("3.5", LanguageVersion.CSharp3)]
+ [InlineData("4.6", LanguageVersion.CSharp6)]
+ [InlineData("4.6.2", LanguageVersion.CSharp7)]
+ [InlineData("4.7.2", LanguageVersion.CSharp7_3)] // Maps to the last 7.x version
+ [InlineData("4.8", LanguageVersion.CSharp8)]
+ [InlineData("Core 1.0", LanguageVersion.CSharp7)] // Maps to C# 7
+ [InlineData("Core 2.0", LanguageVersion.CSharp7_1)]
+ [InlineData("Core 2.1", LanguageVersion.CSharp7_3)]
+ [InlineData("Core 3.0", LanguageVersion.CSharp8)]
+ [InlineData("Core 3.1", LanguageVersion.CSharp8)]
+ [InlineData("5.0", LanguageVersion.CSharp9)]
+ [InlineData("6.0", LanguageVersion.CSharp10)]
+ [InlineData("7.0", LanguageVersion.CSharp10)]
+ [InlineData("8.0", LanguageVersion.CSharp10)]
+ [InlineData("9.0", LanguageVersion.CSharp10)]
+ public void FromDotNetVersion_ShouldReturnExpectedLanguageVersion(string versionString, LanguageVersion expectedLanguageVersion)
+ {
+ // Arrange
+ Version version = ParseVersion(versionString);
+
+ // Act
+ LanguageVersion? result = DotNet.LangVersion.FromDotNetVersion(version);
+
+ // Assert
+ Assert.Equal(expectedLanguageVersion, result);
+ }
+
+ private static Version ParseVersion(ReadOnlySpan versionString)
+ {
+ const string coreIdentifier = "Core";
+
+ if (versionString.Contains(coreIdentifier.AsSpan(), StringComparison.Ordinal))
+ {
+ int spaceIndex = versionString.IndexOf(' ');
+ if (spaceIndex != -1)
+ {
+ ReadOnlySpan versionPart = versionString[(spaceIndex + 1)..];
+ return new Version(versionPart.ToString());
+ }
+ }
+
+ return new Version(versionString.ToString());
+ }
+}
diff --git a/tests/EffectiveCSharp.Analyzers.Tests/SquiggleCop.Baseline.yaml b/tests/EffectiveCSharp.Analyzers.Tests/SquiggleCop.Baseline.yaml
index 2cc4f16..99ddc5f 100644
--- a/tests/EffectiveCSharp.Analyzers.Tests/SquiggleCop.Baseline.yaml
+++ b/tests/EffectiveCSharp.Analyzers.Tests/SquiggleCop.Baseline.yaml
@@ -158,7 +158,7 @@
- {Id: CA2013, Title: Do not use ReferenceEquals with value types, Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2014, Title: Do not use stackalloc in loops, Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2015, Title: Do not define finalizers for types derived from MemoryManager, Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: CA2016, Title: Forward the 'CancellationToken' parameter to methods, Category: Reliability, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: false}
+- {Id: CA2016, Title: Forward the 'CancellationToken' parameter to methods, Category: Reliability, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2017, Title: Parameter count mismatch, Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2018, Title: "'Buffer.BlockCopy' expects the number of bytes to be copied for the 'count' argument", Category: Reliability, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: CA2019, Title: Improper 'ThreadStatic' field initialization, Category: Reliability, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
@@ -445,7 +445,7 @@
- {Id: MA0023, Title: Add RegexOptions.ExplicitCapture, Category: Performance, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0024, Title: Use an explicit StringComparer when possible, Category: Usage, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0025, Title: Implement the functionality instead of throwing NotImplementedException, Category: Design, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: MA0026, Title: Fix TODO comment, Category: Design, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, None], IsEverSuppressed: true}
+- {Id: MA0026, Title: Fix TODO comment, Category: Design, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: MA0027, Title: Prefer rethrowing an exception implicitly, Category: Usage, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0028, Title: Optimize StringBuilder usage, Category: Performance, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0029, Title: Combine LINQ methods, Category: Performance, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
@@ -458,7 +458,7 @@
- {Id: MA0037, Title: Remove empty statement, Category: Usage, DefaultSeverity: Error, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0038, Title: 'Make method static (deprecated, use CA1822 instead)', Category: Design, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0039, Title: Do not write your own certificate validation method, Category: Security, DefaultSeverity: Error, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: MA0040, Title: Forward the CancellationToken parameter to methods that take one, Category: Usage, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: false}
+- {Id: MA0040, Title: Forward the CancellationToken parameter to methods that take one, Category: Usage, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0041, Title: 'Make property static (deprecated, use CA1822 instead)', Category: Design, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0042, Title: Do not use blocking calls in an async method, Category: Design, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0043, Title: Use nameof operator in ArgumentException, Category: Usage, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
@@ -579,7 +579,6 @@
- {Id: MA0158, Title: Use System.Threading.Lock, Category: Performance, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: MA0159, Title: Use 'Order' instead of 'OrderBy', Category: Performance, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: MA0160, Title: Use ContainsKey instead of TryGetValue, Category: Performance, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
-- {Id: POLYSP0003, Title: Unsupported C# language version, Category: Microsoft.CodeAnalysis.CSharp.CSharpParseOptions, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: RCS1001, Title: Add braces (when expression spans over multiple lines), Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: RCS1002, Title: Remove braces, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: false}
- {Id: RCS1002FadeOut, Title: Remove braces, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
@@ -785,7 +784,7 @@
- {Id: RCS1228, Title: Unused element in a documentation comment, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: RCS1229, Title: Use async/await when necessary, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: RCS1230, Title: Unnecessary explicit use of enumerator, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
-- {Id: RCS1231, Title: Make parameter ref read-only, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: false}
+- {Id: RCS1231, Title: Make parameter ref read-only, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: RCS1232, Title: Order elements in documentation comment, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: RCS1233, Title: Use short-circuiting operator, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: RCS1234, Title: Duplicate enum value, Category: Roslynator, DefaultSeverity: Note, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
@@ -900,7 +899,7 @@
- {Id: S113, Title: Files should end with a newline, Category: Minor Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: S1133, Title: Deprecated code should be removed, Category: Info Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: S1134, Title: Track uses of "FIXME" tags, Category: Major Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: S1135, Title: Track uses of "TODO" tags, Category: Info Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: false}
+- {Id: S1135, Title: Track uses of "TODO" tags, Category: Info Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: false}
- {Id: S1144, Title: Unused private types or members should be removed, Category: Major Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: S1147, Title: Exit methods should not be called, Category: Blocker Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: S1151, Title: '"switch case" clauses should not have too many lines of code', Category: Major Code Smell, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
@@ -1372,7 +1371,7 @@
- {Id: SA1027, Title: Use tabs correctly, Category: StyleCop.CSharp.SpacingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1028, Title: Code should not contain trailing whitespace, Category: StyleCop.CSharp.SpacingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1100, Title: Do not prefix calls with base unless local implementation exists, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: SA1101, Title: Prefix local calls with this, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: true}
+- {Id: SA1101, Title: Prefix local calls with this, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: true}
- {Id: SA1102, Title: Query clause should follow previous clause, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1103, Title: Query clauses should be on separate lines or all on one line, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1104, Title: Query clause should begin on new line when previous clause spans multiple lines, Category: StyleCop.CSharp.ReadabilityRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
@@ -1439,7 +1438,7 @@
- {Id: SA1306, Title: Field names should begin with lower-case letter, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1307, Title: Accessible fields should begin with upper-case letter, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1308, Title: Variable names should not be prefixed, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: SA1309, Title: Field names should not begin with underscore, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, None], IsEverSuppressed: true}
+- {Id: SA1309, Title: Field names should not begin with underscore, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: SA1310, Title: Field names should not contain underscore, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1311, Title: Static readonly fields should begin with upper-case letter, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1312, Title: Variable names should begin with lower-case letter, Category: StyleCop.CSharp.NamingRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
@@ -1482,7 +1481,7 @@
- {Id: SA1518, Title: Use line endings correctly at end of file, Category: StyleCop.CSharp.LayoutRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1519, Title: Braces should not be omitted from multi-line child statement, Category: StyleCop.CSharp.LayoutRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1520, Title: Use braces consistently, Category: StyleCop.CSharp.LayoutRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
-- {Id: SA1600, Title: Elements should be documented, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: true}
+- {Id: SA1600, Title: Elements should be documented, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: true}
- {Id: SA1601, Title: Partial elements should be documented, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1602, Title: Enumeration items should be documented, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1603, Title: Documentation should contain valid XML, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
@@ -1515,7 +1514,7 @@
- {Id: SA1630, Title: Documentation text should contain whitespace, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: SA1631, Title: Documentation should meet character percentage, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
- {Id: SA1632, Title: Documentation text should meet minimum character length, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: false, EffectiveSeverities: [None], IsEverSuppressed: true}
-- {Id: SA1633, Title: File should have header, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error, Note], IsEverSuppressed: true}
+- {Id: SA1633, Title: File should have header, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Note], IsEverSuppressed: true}
- {Id: SA1634, Title: File header should show copyright, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1635, Title: File header should have copyright text, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
- {Id: SA1636, Title: File header copyright text should match, Category: StyleCop.CSharp.DocumentationRules, DefaultSeverity: Warning, IsEnabledByDefault: true, EffectiveSeverities: [Error], IsEverSuppressed: false}
diff --git a/tests/EffectiveCSharp.Analyzers.Tests/SupportsTypeTests.cs b/tests/EffectiveCSharp.Analyzers.Tests/SupportsTypeTests.cs
new file mode 100644
index 0000000..c7b3fcf
--- /dev/null
+++ b/tests/EffectiveCSharp.Analyzers.Tests/SupportsTypeTests.cs
@@ -0,0 +1,51 @@
+using EffectiveCSharp.Analyzers.Common;
+using Microsoft.CodeAnalysis;
+
+namespace EffectiveCSharp.Analyzers.Tests;
+
+public class SupportsTypeTests
+{
+ [Fact]
+ public void Supports_FormattableString()
+ {
+ Compilation compilation = CreateCompilation();
+
+ bool result = compilation.SupportsType("System.FormattableString");
+
+ Assert.True(result);
+ }
+
+ [Fact]
+ public void Supports_StringCreate()
+ {
+ Compilation compilation = CreateCompilation();
+
+ bool result = compilation.SupportsType("System.String", "Create");
+
+ Assert.True(result);
+ }
+
+ private static Compilation CreateCompilation()
+ {
+ const string testCode = """
+ public class C
+ {
+ public void M()
+ {
+ }
+ }
+ """;
+
+ Compilation compilation = CreateCompilation(testCode);
+ return compilation;
+ }
+
+ private static Compilation CreateCompilation(string sourceCode)
+ {
+ return CSharpCompilation.Create(
+ "TestAssembly",
+ new[] { CSharpSyntaxTree.ParseText(sourceCode) },
+ new[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location) },
+ new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
+ }
+}