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)); + } +}