From d903bb767c00a2ea9fa0ff4ba4b60293727f946f Mon Sep 17 00:00:00 2001 From: Meir Blachman Date: Sat, 27 Sep 2025 19:10:24 +0300 Subject: [PATCH 01/38] docs: enhance MA0090 and MA0098 rule documentation (#874) --- docs/Rules/MA0090.md | 64 ++++++++++++++++++++++++++++++++++++++++++++ docs/Rules/MA0098.md | 38 ++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/docs/Rules/MA0090.md b/docs/Rules/MA0090.md index 30420efe1..3f20269fb 100644 --- a/docs/Rules/MA0090.md +++ b/docs/Rules/MA0090.md @@ -1 +1,65 @@ # MA0090 - Remove empty else/finally block + +This rule detects empty `else` and `finally` blocks that contain no executable code and suggests removing them to improve code readability and reduce clutter. Empty blocks serve no functional purpose and can make code harder to read by adding unnecessary visual noise. + +````csharp +// non-compliant +if (condition) +{ + DoSomething(); +} +else +{ +} + +// compliant +if (condition) +{ + DoSomething(); +} + +// non-compliant +try +{ + DoSomething(); +} +catch (Exception ex) +{ + HandleException(ex); +} +finally +{ +} + +// compliant +try +{ + DoSomething(); +} +catch (Exception ex) +{ + HandleException(ex); +} + +// compliant - blocks with comments are not flagged +if (condition) +{ + DoSomething(); +} +else +{ + // TODO: Implement this feature later +} + +// compliant - blocks with preprocessor directives are not flagged +try +{ + DoSomething(); +} +finally +{ +#if DEBUG + Console.WriteLine("Debug mode"); +#endif +} +```` diff --git a/docs/Rules/MA0098.md b/docs/Rules/MA0098.md index 9b18a33b1..48a0f87a8 100644 --- a/docs/Rules/MA0098.md +++ b/docs/Rules/MA0098.md @@ -1 +1,39 @@ # MA0098 - Use indexer instead of LINQ methods + +When working with collections that support indexing (such as arrays, `List`, `IList`, or `IReadOnlyList`), using the indexer syntax `[index]` is more efficient than LINQ methods like `ElementAt()`, `First()`, and `Last()`. This rule suggests replacing these LINQ methods with direct indexer access for better performance. + +The indexer approach provides direct access to elements without the overhead of LINQ extension methods and enumeration, resulting in faster execution and reduced memory allocations. + +## Examples + +````csharp +var list = new List(); + +_ = list.ElementAt(5); // non-compliant +_ = list[5]; // compliant + +_ = list.First(); // non-compliant +_ = list[0]; // compliant + +_ = list.Last(); // non-compliant +_ = list[^1]; // compliant +_ = list[list.Count - 1]; // compliant + +_ = list.FirstOrDefault(); // compliant +_ = list.First(x => x > 1); // compliant +```` + +## C# 8+ Index and Range Support + +Starting with C# 8.0 and when targeting frameworks that support the Index and Range feature (.NET Core 3.0+, .NET 5+), the analyzer can suggest using the hat operator (`^`) for accessing elements from the end: + +````csharp +// C# 8+ with compatible target framework +var array = new int[10]; + +// The analyzer suggests using the hat operator for Last() +var last = array[^1]; + +// For older language versions or target frameworks, it uses the traditional syntax +var last = array[array.Length - 1]; +```` From 0a2736faf9d3bba503558afc47e4fc4cca72e968 Mon Sep 17 00:00:00 2001 From: Alexey Dubrovin Date: Tue, 30 Sep 2025 18:47:48 +0500 Subject: [PATCH 02/38] MA0042 fixer creates generic method call if generic method was called (#881) --- ...DoNotUseBlockingCallInAsyncContextFixer.cs | 7 ++- ...nAsyncContextAnalyzer_AsyncContextTests.cs | 44 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseBlockingCallInAsyncContextFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseBlockingCallInAsyncContextFixer.cs index d16a1941e..1f2bb7431 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseBlockingCallInAsyncContextFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseBlockingCallInAsyncContextFixer.cs @@ -125,7 +125,12 @@ private static async Task ReplaceWithMethodName(Document document, Syn if (nodeToReplace is null) return document; - var newNode = nodeToFix.ReplaceNode(nodeToReplace, generator.IdentifierName(methodName)); + var newMethodName = nodeToReplace switch + { + GenericNameSyntax genericName => generator.GenericName(methodName, genericName.TypeArgumentList.Arguments), + _ => generator.IdentifierName(methodName), + }; + var newNode = nodeToFix.ReplaceNode(nodeToReplace, newMethodName); var newExpression = generator.AwaitExpression(newNode).Parentheses(); editor.ReplaceNode(nodeToFix, newExpression); diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer_AsyncContextTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer_AsyncContextTests.cs index 58e81ab17..b22d7995b 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer_AsyncContextTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer_AsyncContextTests.cs @@ -72,6 +72,50 @@ public async Task A() .ValidateAsync(); } + [Fact] + public async Task FixerKeepsGenericArgument() + { + await CreateProjectBuilder() + .WithSourceCode( + @"using System.Threading.Tasks; +class Buz +{ + private static async Task Do() + { + [||]Bar.Foo(); + } +} + +class Bar +{ + public static T Foo() + => default; + + public static Task FooAsync() + => Task.FromResult(default(T)); +}") + .ShouldFixCodeWith( + @"using System.Threading.Tasks; +class Buz +{ + private static async Task Do() + { + await Bar.FooAsync(); + } +} + +class Bar +{ + public static T Foo() + => default; + + public static Task FooAsync() + => Task.FromResult(default(T)); +}") + .ValidateAsync(); + } + + [Fact] public async Task Async_Wait_Int32_Diagnostic() { From 534be3586c9d26d92cae1cf188be3e8f9abd6eb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Sat, 4 Oct 2025 21:55:42 -0400 Subject: [PATCH 03/38] MA0016 skips conversion methods (#883) --- ...tractionInsteadOfImplementationAnalyzer.cs | 3 ++ .../Helpers/ProjectBuilder.Validation.cs | 9 ----- ...ionInsteadOfImplementationAnalyzerTests.cs | 40 +++++++++++++++++++ 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/src/Meziantou.Analyzer/Rules/PreferReturningCollectionAbstractionInsteadOfImplementationAnalyzer.cs b/src/Meziantou.Analyzer/Rules/PreferReturningCollectionAbstractionInsteadOfImplementationAnalyzer.cs index 5b55340cc..c825703c4 100644 --- a/src/Meziantou.Analyzer/Rules/PreferReturningCollectionAbstractionInsteadOfImplementationAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/PreferReturningCollectionAbstractionInsteadOfImplementationAnalyzer.cs @@ -146,6 +146,9 @@ public void AnalyzeSymbol(SymbolAnalysisContext context) break; case IMethodSymbol methodSymbol when methodSymbol.MethodKind is not MethodKind.PropertyGet and not MethodKind.PropertySet: + if (context.Symbol is IMethodSymbol { MethodKind: MethodKind.Conversion }) + return; + if (!IsValidType(methodSymbol.ReturnType)) { context.ReportDiagnostic(Rule, methodSymbol, DiagnosticMethodReportOptions.ReportOnReturnType); diff --git a/tests/Meziantou.Analyzer.Test/Helpers/ProjectBuilder.Validation.cs b/tests/Meziantou.Analyzer.Test/Helpers/ProjectBuilder.Validation.cs index 96686d6c7..6210a9d82 100755 --- a/tests/Meziantou.Analyzer.Test/Helpers/ProjectBuilder.Validation.cs +++ b/tests/Meziantou.Analyzer.Test/Helpers/ProjectBuilder.Validation.cs @@ -1,13 +1,5 @@ -using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Test.Helpers; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; @@ -17,7 +9,6 @@ using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Text; -using Xunit; namespace TestHelper; diff --git a/tests/Meziantou.Analyzer.Test/Rules/PreferReturningCollectionAbstractionInsteadOfImplementationAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/PreferReturningCollectionAbstractionInsteadOfImplementationAnalyzerTests.cs index cb6a62267..2f9eff6ab 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/PreferReturningCollectionAbstractionInsteadOfImplementationAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/PreferReturningCollectionAbstractionInsteadOfImplementationAnalyzerTests.cs @@ -371,4 +371,44 @@ public class Test : ITest """) .ValidateAsync(); } + + [Fact] + public async Task ConversionOperator() + { + await CreateProjectBuilder() + .WithSourceCode(""" + public class Sample + { + public static implicit operator Sample(System.Collections.Generic.List _) => throw null; + public static implicit operator System.Collections.Generic.List(Sample _) => throw null; + } + """) + .ValidateAsync(); + } + + [Fact] + public async Task AddOperator() + { + await CreateProjectBuilder() + .WithSourceCode(""" + public class Sample + { + public static Sample operator+(Sample instance, [|System.Collections.Generic.List|] value) => throw null; + } + """) + .ValidateAsync(); + } + + [Fact] + public async Task AddOperator_Instance() + { + await CreateProjectBuilder() + .WithSourceCode(""" + public class Sample : System.Collections.Generic.List + { + public static Sample operator+(Sample instance, int value) => throw null; + } + """) + .ValidateAsync(); + } } From 17d4f61c4707519fe43e7d110b25e5aac45bdeff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Sun, 5 Oct 2025 16:39:40 -0400 Subject: [PATCH 04/38] New rule: MA0176 - Optimize guid creation (#884) --- README.md | 1 + docs/README.md | 7 ++ docs/Rules/MA0176.md | 10 ++ .../Rules/OptimizeGuidCreationFixer.cs | 94 +++++++++++++++++++ .../configuration/default.editorconfig | 3 + .../configuration/none.editorconfig | 3 + src/Meziantou.Analyzer/RuleIdentifiers.cs | 1 + .../Rules/OptimizeGuidCreationAnalyzer.cs | 72 ++++++++++++++ .../Rules/OptimizeGuidParsingAnalyzerTests.cs | 63 +++++++++++++ 9 files changed, 254 insertions(+) create mode 100644 docs/Rules/MA0176.md create mode 100644 src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeGuidCreationFixer.cs create mode 100644 src/Meziantou.Analyzer/Rules/OptimizeGuidCreationAnalyzer.cs create mode 100644 tests/Meziantou.Analyzer.Test/Rules/OptimizeGuidParsingAnalyzerTests.cs diff --git a/README.md b/README.md index 4200d6850..89b3efe12 100755 --- a/README.md +++ b/README.md @@ -191,6 +191,7 @@ If you are already using other analyzers, you can check [which rules are duplica |[MA0173](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0173.md)|Design|Use LazyInitializer.EnsureInitialize|ℹ️|✔️|❌| |[MA0174](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0174.md)|Style|Record should use explicit 'class' keyword|ℹ️|❌|❌| |[MA0175](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0175.md)|Style|Record should not use explicit 'class' keyword|ℹ️|❌|❌| +|[MA0176](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0176.md)|Performance|Optimize guid creation|ℹ️|✔️|✔️| diff --git a/docs/README.md b/docs/README.md index 9732a7452..6797c171a 100755 --- a/docs/README.md +++ b/docs/README.md @@ -175,6 +175,7 @@ |[MA0173](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0173.md)|Design|Use LazyInitializer.EnsureInitialize|ℹ️|✔️|❌| |[MA0174](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0174.md)|Style|Record should use explicit 'class' keyword|ℹ️|❌|❌| |[MA0175](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0175.md)|Style|Record should not use explicit 'class' keyword|ℹ️|❌|❌| +|[MA0176](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0176.md)|Performance|Optimize guid creation|ℹ️|✔️|✔️| |Id|Suppressed rule|Justification| |--|---------------|-------------| @@ -708,6 +709,9 @@ dotnet_diagnostic.MA0174.severity = none # MA0175: Record should not use explicit 'class' keyword dotnet_diagnostic.MA0175.severity = none + +# MA0176: Optimize guid creation +dotnet_diagnostic.MA0176.severity = suggestion ``` # .editorconfig - all rules disabled @@ -1234,4 +1238,7 @@ dotnet_diagnostic.MA0174.severity = none # MA0175: Record should not use explicit 'class' keyword dotnet_diagnostic.MA0175.severity = none + +# MA0176: Optimize guid creation +dotnet_diagnostic.MA0176.severity = none ``` diff --git a/docs/Rules/MA0176.md b/docs/Rules/MA0176.md new file mode 100644 index 000000000..45db76ada --- /dev/null +++ b/docs/Rules/MA0176.md @@ -0,0 +1,10 @@ +# MA0176 - Optimize guid creation + +Use the constructor overload of `Guid` that takes the individual components of a GUID instead of using the string-based constructor or `Guid.Parse` method. + +````c# +_ = new Guid("d3c4d2f1-5f6a-4b7c-8e9f-0a1b2c3d4e5f"); // non-compliant +_ = Guid.Parse("d3c4d2f1-5f6a-4b7c-8e9f-0a1b2c3d4e5f"); // non-compliant + +_ = new Guid(0xd3c4d2f1, 0x5f6a, 0x4b7c, 0x8e, 0x9f, 0x0a, 0x1b, 0x2c, 0x3d, 0x4e, 0x5f); // compliant +```` diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeGuidCreationFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeGuidCreationFixer.cs new file mode 100644 index 000000000..b415af910 --- /dev/null +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeGuidCreationFixer.cs @@ -0,0 +1,94 @@ +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Net.NetworkInformation; +using System.Threading; +using System.Threading.Tasks; +using Meziantou.Analyzer.Internals; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Operations; + +namespace Meziantou.Analyzer.Rules; + +[ExportCodeFixProvider(LanguageNames.CSharp), Shared] +public sealed class OptimizeGuidCreationFixer : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(RuleIdentifiers.OptimizeGuidCreation); + + public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + var nodeToFix = root?.FindNode(context.Span, getInnermostNodeForTie: true); + if (nodeToFix is null) + return; + + var semanticMode = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); + if (semanticMode is null) + return; + + var operation = semanticMode.GetOperation(nodeToFix, context.CancellationToken); + string? guidString = null; + if (operation is IObjectCreationOperation creation) + { + guidString = creation.Arguments.FirstOrDefault()?.Value.ConstantValue.Value as string; + } + else if (operation is IInvocationOperation invocation) + { + guidString = invocation.Arguments.FirstOrDefault()?.Value.ConstantValue.Value as string; + } + + if (guidString is null || !Guid.TryParse(guidString, out var guid)) + return; + + + var title = "Optimize guid creation"; + var codeAction = CodeAction.Create( + title, + ct => Update(context.Document, operation!, guid, guidString, ct), + equivalenceKey: title); + context.RegisterCodeFix(codeAction, context.Diagnostics); + } + + private static async Task Update(Document document, IOperation nodeToFix, Guid guid, string rawGuid, CancellationToken cancellationToken) + { + var parts = guid.ToByteArray(); + var data1 = BitConverter.ToInt32(parts, 0); + var data2 = BitConverter.ToInt16(parts, 4); + var data3 = BitConverter.ToInt16(parts, 6); + var data4 = new byte[8]; + Array.Copy(parts, 8, data4, 0, 8); + + var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + var generator = editor.Generator; + var uppercase = rawGuid.All(c => !char.IsLetter(c) || char.IsUpper(c)); + var newExpression = generator.ObjectCreationExpression(editor.SemanticModel.Compilation.GetBestTypeByMetadataName("System.Guid")!, + [ + CreateHexLiteral(data1, uppercase), + CreateHexLiteral(data2, uppercase), + CreateHexLiteral(data3, uppercase), + CreateHexLiteral(data4[0], uppercase), + CreateHexLiteral(data4[1], uppercase), + CreateHexLiteral(data4[2], uppercase), + CreateHexLiteral(data4[3], uppercase), + CreateHexLiteral(data4[4], uppercase), + CreateHexLiteral(data4[5], uppercase), + CreateHexLiteral(data4[6], uppercase), + CreateHexLiteral(data4[7], uppercase), + ]) + .WithTrailingTrivia(SyntaxFactory.Comment($" /* {rawGuid} */")); + + editor.ReplaceNode(nodeToFix.Syntax, newExpression); + return editor.GetChangedDocument(); + } + + private static LiteralExpressionSyntax CreateHexLiteral(int value, bool uppercase) => SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal("0x" + value.ToString(uppercase ? "X" : "x", CultureInfo.InvariantCulture), value)); + private static LiteralExpressionSyntax CreateHexLiteral(short value, bool uppercase) => SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal("0x" + value.ToString(uppercase ? "X" : "x", CultureInfo.InvariantCulture), value)); + private static LiteralExpressionSyntax CreateHexLiteral(byte value, bool uppercase) => SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal("0x" + value.ToString(uppercase ? "X" : "x", CultureInfo.InvariantCulture), value)); +} diff --git a/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig b/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig index 02b9eeeed..a996f92f7 100644 --- a/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig +++ b/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig @@ -523,3 +523,6 @@ dotnet_diagnostic.MA0174.severity = none # MA0175: Record should not use explicit 'class' keyword dotnet_diagnostic.MA0175.severity = none + +# MA0176: Optimize guid creation +dotnet_diagnostic.MA0176.severity = suggestion diff --git a/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig b/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig index b1856ed37..b93fcd8d3 100644 --- a/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig +++ b/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig @@ -523,3 +523,6 @@ dotnet_diagnostic.MA0174.severity = none # MA0175: Record should not use explicit 'class' keyword dotnet_diagnostic.MA0175.severity = none + +# MA0176: Optimize guid creation +dotnet_diagnostic.MA0176.severity = none diff --git a/src/Meziantou.Analyzer/RuleIdentifiers.cs b/src/Meziantou.Analyzer/RuleIdentifiers.cs index 91e44d7fc..0afe37099 100755 --- a/src/Meziantou.Analyzer/RuleIdentifiers.cs +++ b/src/Meziantou.Analyzer/RuleIdentifiers.cs @@ -178,6 +178,7 @@ internal static class RuleIdentifiers public const string UseLazyInitializerEnsureInitialize = "MA0173"; public const string RecordClassDeclarationShouldBeExplicit = "MA0174"; public const string RecordClassDeclarationShouldBeImplicit = "MA0175"; + public const string OptimizeGuidCreation = "MA0176"; public static string GetHelpUri(string identifier) { diff --git a/src/Meziantou.Analyzer/Rules/OptimizeGuidCreationAnalyzer.cs b/src/Meziantou.Analyzer/Rules/OptimizeGuidCreationAnalyzer.cs new file mode 100644 index 000000000..7b0a0c021 --- /dev/null +++ b/src/Meziantou.Analyzer/Rules/OptimizeGuidCreationAnalyzer.cs @@ -0,0 +1,72 @@ +using System.Collections.Immutable; +using Meziantou.Analyzer.Internals; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Meziantou.Analyzer.Rules; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public sealed class OptimizeGuidCreationAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor Rule = new( + RuleIdentifiers.OptimizeGuidCreation, + title: "Optimize guid creation", + messageFormat: "Optimize guid creation", + RuleCategories.Performance, + DiagnosticSeverity.Info, + isEnabledByDefault: true, + description: "", + helpLinkUri: RuleIdentifiers.GetHelpUri(RuleIdentifiers.OptimizeGuidCreation)); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.ReportDiagnostics); + + context.RegisterCompilationStartAction(ctx => + { + var type = ctx.Compilation.GetBestTypeByMetadataName("System.Guid"); + if (type is null) + return; + + ctx.RegisterOperationAction(symbolContext => AnalyzeInvocation(symbolContext, type), OperationKind.Invocation); // Guid.Parse(""), Guid.TryParse("") + ctx.RegisterOperationAction(symbolContext => AnalyzeObjectCreation(symbolContext, type), OperationKind.ObjectCreation); // new Guid("") + }); + } + + private static void AnalyzeObjectCreation(OperationAnalysisContext symbolContext, INamedTypeSymbol type) + { + var creation = (IObjectCreationOperation)symbolContext.Operation; + if (creation.Constructor is null) + return; + + if (!creation.Constructor.ContainingType.IsEqualTo(type)) + return; + + if (creation is { Arguments: [{ Value.Type.SpecialType: SpecialType.System_String, Value.ConstantValue: { HasValue: true, Value: string value } }] }) + { + if (Guid.TryParse(value, out _)) + { + symbolContext.ReportDiagnostic(Rule, creation); + } + } + } + + private static void AnalyzeInvocation(OperationAnalysisContext context, INamedTypeSymbol guidType) + { + var invocation = (IInvocationOperation)context.Operation; + if (!invocation.TargetMethod.ContainingType.IsEqualTo(guidType)) + return; + + if (invocation is { TargetMethod.Name: "Parse", Arguments: [{ Value.Type.SpecialType: SpecialType.System_String, Value.ConstantValue: { HasValue: true, Value: string value } }] }) + { + if (Guid.TryParse(value, out _)) + { + context.ReportDiagnostic(Rule, invocation); + } + } + } +} diff --git a/tests/Meziantou.Analyzer.Test/Rules/OptimizeGuidParsingAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/OptimizeGuidParsingAnalyzerTests.cs new file mode 100644 index 000000000..276825e1d --- /dev/null +++ b/tests/Meziantou.Analyzer.Test/Rules/OptimizeGuidParsingAnalyzerTests.cs @@ -0,0 +1,63 @@ +using Meziantou.Analyzer.Rules; +using TestHelper; +using Xunit; + +namespace Meziantou.Analyzer.Test.Rules; + +public sealed class OptimizeGuidParsingAnalyzerTests +{ + private static ProjectBuilder CreateProjectBuilder() + { + return new ProjectBuilder() + .WithAnalyzer() + .WithCodeFixProvider() + .WithOutputKind(Microsoft.CodeAnalysis.OutputKind.ConsoleApplication); + } + + [Fact] + public async Task CtorConstantString() + { + await CreateProjectBuilder() + .WithSourceCode(""" + _ = [|new System.Guid("10752bc4-c151-50f5-f27b-df92d8af5a61")|]; + """) + .ShouldFixCodeWith(""" + _ = new System.Guid(0x10752bc4, 0xc151, 0x50f5, 0xf2, 0x7b, 0xdf, 0x92, 0xd8, 0xaf, 0x5a, 0x61) /* 10752bc4-c151-50f5-f27b-df92d8af5a61 */; + """) + .ValidateAsync(); + } + + [Fact] + public async Task ParseConstantString() + { + await CreateProjectBuilder() + .WithSourceCode(""" + _ = [|System.Guid.Parse("10752BC4-C151-50F5-F27B-DF92D8AF5A61")|]; + """) + .ShouldFixCodeWith(""" + _ = new System.Guid(0x10752BC4, 0xC151, 0x50F5, 0xF2, 0x7B, 0xDF, 0x92, 0xD8, 0xAF, 0x5A, 0x61) /* 10752BC4-C151-50F5-F27B-DF92D8AF5A61 */; + """) + .ValidateAsync(); + } + + [Fact] + public async Task ParseNonConstantString() + { + await CreateProjectBuilder() + .WithSourceCode(""" + var value = "10752BC4-C151-50F5-F27B-DF92D8AF5A61"; + _ = System.Guid.Parse(value); + """) + .ValidateAsync(); + } + + [Fact] + public async Task ParseInvalidGuid() + { + await CreateProjectBuilder() + .WithSourceCode(""" + _ = System.Guid.Parse("dummy"); + """) + .ValidateAsync(); + } +} From b0ff4f8502a5555c946e857af45184249e863084 Mon Sep 17 00:00:00 2001 From: Gert Driesen Date: Sun, 12 Oct 2025 18:31:22 +0200 Subject: [PATCH 05/38] Document CA1849 as being similar to MA0042 (#886) --- docs/comparison-with-other-analyzers.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/comparison-with-other-analyzers.md b/docs/comparison-with-other-analyzers.md index 7b3b55464..722eb4c1c 100644 --- a/docs/comparison-with-other-analyzers.md +++ b/docs/comparison-with-other-analyzers.md @@ -60,6 +60,7 @@ | [CA1822](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1822?WT.mc_id=DT-MVP-5003978) | [MA0041](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0041.md) | CA supports more cases and can be configured to only run against specific API surfaces | | [CA1826](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1826?WT.mc_id=DT-MVP-5003978) | [MA0020](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0020.md) | MA reports more cases such as `List.Find` instead of `FirstOrDefault` | | [CA1827](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1827?WT.mc_id=DT-MVP-5003978) | [MA0031](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0031.md) | MA reports more `Count()` optimizations | +| [CA1849](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1849?WT.mc_id=DT-MVP-5003978) | [MA0042](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0042.md) | MA reports less false-positives and supports [CreateAsyncScope](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.serviceproviderserviceextensions.createasyncscope?WT.mc_id=DT-MVP-5003978) | [CA1852](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1852?WT.mc_id=DT-MVP-5003978) | [MA0053](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0053.md) | CA only applies to internal types in assemblies that do not expose internal types and members and - by default - report types that inherit from [Exception] (https://learn.microsoft.com/en-us/dotnet/api/system.exception?WT.mc_id=DT-MVP-5003978), but cannot be configured to report types that define virtual members | | [CA2007](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2007?WT.mc_id=DT-MVP-5003978) | [MA0004](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0004.md) | MA does not report when the context is needed (WPF, WinForms, Blazor, etc.) | | [CA2016](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2016?WT.mc_id=DT-MVP-5003978) | [MA0040](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0040.md) | MA rules detects more CancellationToken | From 5fb8237eaa58462e9cd4e69a0638d72ccc851f8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Mon, 13 Oct 2025 11:43:47 -0400 Subject: [PATCH 06/38] Added support for analyzing methods with LoggerMessage attributes (#888) --- .../Rules/LoggerParameterTypeAnalyzer.cs | 76 +++++- .../Rules/LoggerParameterTypeAnalyzerTests.cs | 247 +++++++++++++++++- 2 files changed, 321 insertions(+), 2 deletions(-) diff --git a/src/Meziantou.Analyzer/Rules/LoggerParameterTypeAnalyzer.cs b/src/Meziantou.Analyzer/Rules/LoggerParameterTypeAnalyzer.cs index c0a463b5a..dc57bac1b 100644 --- a/src/Meziantou.Analyzer/Rules/LoggerParameterTypeAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/LoggerParameterTypeAnalyzer.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Globalization; @@ -82,6 +82,7 @@ public override void Initialize(AnalysisContext context) return; context.RegisterOperationAction(ctx.AnalyzeInvocationDeclaration, OperationKind.Invocation); + context.RegisterSymbolAction(ctx.AnalyzeMethodSymbol, SymbolKind.Method); }); } @@ -104,6 +105,7 @@ public AnalyzerContext(CompilationStartAnalysisContext context) LoggerExtensionsSymbol = compilation.GetBestTypeByMetadataName("Microsoft.Extensions.Logging.LoggerExtensions"); LoggerMessageSymbol = compilation.GetBestTypeByMetadataName("Microsoft.Extensions.Logging.LoggerMessage"); + LoggerMessageAttributeSymbol = compilation.GetBestTypeByMetadataName("Microsoft.Extensions.Logging.LoggerMessageAttribute"); StructuredLogFieldAttributeSymbol = compilation.GetBestTypeByMetadataName("Meziantou.Analyzer.Annotations.StructuredLogFieldAttribute"); SerilogLoggerEnrichmentConfigurationWithPropertySymbol = DocumentationCommentId.GetFirstSymbolForDeclarationId("M:Serilog.Configuration.LoggerEnrichmentConfiguration.WithProperty(System.String,System.Object,System.Boolean)", compilation); @@ -230,6 +232,7 @@ static Location CreateLocation(AdditionalText file, SourceText sourceText, TextL public INamedTypeSymbol? LoggerSymbol { get; } public INamedTypeSymbol? LoggerExtensionsSymbol { get; } public INamedTypeSymbol? LoggerMessageSymbol { get; } + public INamedTypeSymbol? LoggerMessageAttributeSymbol { get; } public INamedTypeSymbol? SerilogLogSymbol { get; } public INamedTypeSymbol? SerilogILoggerSymbol { get; } @@ -242,6 +245,68 @@ static Location CreateLocation(AdditionalText file, SourceText sourceText, TextL public bool IsValid => Configuration.Count > 0; + public void AnalyzeMethodSymbol(SymbolAnalysisContext context) + { + var method = (IMethodSymbol)context.Symbol; + + // Check if method has LoggerMessageAttribute + var loggerMessageAttribute = method.GetAttribute(LoggerMessageAttributeSymbol); + if (loggerMessageAttribute is null) + return; + + // Get the message format string from the attribute + string? formatString = null; + foreach (var arg in loggerMessageAttribute.ConstructorArguments) + { + if (arg.Type.IsString() && arg.Value is string str) + { + formatString = str; + break; + } + } + + if (string.IsNullOrEmpty(formatString)) + return; + + // Parse the format string to get template parameter names + var logFormat = new LogValuesFormatter(formatString); + if (logFormat.ValueNames.Count == 0) + return; + + // Create a dictionary mapping parameter names to their types + var parameterMap = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var parameter in method.Parameters) + { + // Skip the ILogger parameter + if (parameter.Type.IsEqualTo(LoggerSymbol)) + continue; + + parameterMap[parameter.Name] = (parameter.Type, parameter); + } + + // Validate each template parameter + foreach (var templateParamName in logFormat.ValueNames) + { + if (parameterMap.TryGetValue(templateParamName, out var paramInfo)) + { + // Validate the parameter type + ValidateParameterName(context, paramInfo.Parameter, templateParamName); + + if (!Configuration.IsValid(context.Compilation, templateParamName, paramInfo.Type, out var ruleFound)) + { + var expectedSymbols = Configuration.GetSymbols(templateParamName) ?? []; + var expectedSymbolsStr = $"must be of type {string.Join(" or ", expectedSymbols.Select(s => $"'{FormatType(s)}'"))} but is of type '{FormatType(paramInfo.Type)}'"; + context.ReportDiagnostic(Rule, paramInfo.Parameter, templateParamName, expectedSymbolsStr); + } + + if (!ruleFound) + { + context.ReportDiagnostic(RuleMissingConfiguration, paramInfo.Parameter, templateParamName); + } + } + } + } + public void AnalyzeInvocationDeclaration(OperationAnalysisContext context) { var operation = (IInvocationOperation)context.Operation; @@ -440,6 +505,15 @@ private void ValidateParameterName(OperationAnalysisContext context, IOperation } } + private void ValidateParameterName(SymbolAnalysisContext context, IParameterSymbol parameter, string name) + { + var expectedSymbols = Configuration.GetSymbols(name); + if (expectedSymbols is []) + { + context.ReportDiagnostic(Rule, parameter, name, "is not allowed by configuration"); + } + } + private static string RemovePrefix(string name, char[]? potentialNamePrefixes) { if (potentialNamePrefixes is not null) diff --git a/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzerTests.cs index cf538ae59..bab9041d6 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzerTests.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; @@ -475,6 +475,251 @@ await CreateProjectBuilder() .WithSourceCode(SourceCode) .AddAdditionalFile("LoggerParameterTypes.txt", """ Prop;System.Int32 +""") + .ValidateAsync(); + } + + [Fact] + public async Task LoggerMessageAttribute_ValidParameterTypes() + { + const string SourceCode = """ +using Microsoft.Extensions.Logging; +using System.Runtime.CompilerServices; + +partial class LoggerExtensions +{ + [LoggerMessage(10_004, LogLevel.Trace, "Test message with {Prop} and {Name}")] + static partial void LogTestMessage(ILogger logger, string Prop, int Name); +} + +class Program { static void Main() { } } +"""; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .AddAdditionalFile("LoggerParameterTypes.txt", """ +Prop;System.String +Name;System.Int32 +""") + .ValidateAsync(); + } + + [Fact] + public async Task LoggerMessageAttribute_InvalidParameterType() + { + const string SourceCode = """ +using Microsoft.Extensions.Logging; +using System.Runtime.CompilerServices; + +partial class LoggerExtensions +{ + [LoggerMessage(10_004, LogLevel.Trace, "Test message with {Prop} and {Name}")] + static partial void LogTestMessage(ILogger logger, int [|Prop|], string Name); +} +"""; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .WithOutputKind(Microsoft.CodeAnalysis.OutputKind.DynamicallyLinkedLibrary) + .AddAdditionalFile("LoggerParameterTypes.txt", """ +Prop;System.String +Name;System.String +""") + .ValidateAsync(); + } + + [Fact] + public async Task LoggerMessageAttribute_MultipleInvalidParameterTypes() + { + const string SourceCode = """ +using Microsoft.Extensions.Logging; +using System.Runtime.CompilerServices; + +partial class LoggerExtensions +{ + [LoggerMessage(10_004, LogLevel.Trace, "Test message with {Prop} and {Name}")] + static partial void LogTestMessage(ILogger logger, int [|Prop|], int [|Name|]); +} +"""; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .WithOutputKind(Microsoft.CodeAnalysis.OutputKind.DynamicallyLinkedLibrary) + .AddAdditionalFile("LoggerParameterTypes.txt", """ +Prop;System.String +Name;System.String +""") + .ValidateAsync(); + } + + [Fact] + public async Task LoggerMessageAttribute_MissingConfiguration() + { + const string SourceCode = """ +using Microsoft.Extensions.Logging; +using System.Runtime.CompilerServices; + +partial class LoggerExtensions +{ + [LoggerMessage(10_004, LogLevel.Trace, "Test message with {Prop} and {Name}")] + static partial void LogTestMessage(ILogger logger, string [|Prop|], int Name); +} +"""; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .WithOutputKind(Microsoft.CodeAnalysis.OutputKind.DynamicallyLinkedLibrary) + .AddAdditionalFile("LoggerParameterTypes.txt", """ +Name;System.Int32 +""") + .ShouldReportDiagnosticWithMessage("Log parameter 'Prop' has no configured type") + .ValidateAsync(); + } + + [Fact] + public async Task LoggerMessageAttribute_DeniedParameter() + { + const string SourceCode = """ +using Microsoft.Extensions.Logging; +using System.Runtime.CompilerServices; + +partial class LoggerExtensions +{ + [LoggerMessage(10_004, LogLevel.Trace, "Test message with {Prop} and {Name}")] + static partial void LogTestMessage(ILogger logger, string [|Prop|], int Name); +} +"""; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .WithOutputKind(Microsoft.CodeAnalysis.OutputKind.DynamicallyLinkedLibrary) + .AddAdditionalFile("LoggerParameterTypes.txt", """ +Name;System.Int32 +Prop; +""") + .ShouldReportDiagnosticWithMessage("Log parameter 'Prop' is not allowed by configuration") + .ValidateAsync(); + } + + [Fact] + public async Task LoggerMessageAttribute_SkipILoggerParameter() + { + const string SourceCode = """ +using Microsoft.Extensions.Logging; + +partial class LoggerExtensions +{ + [LoggerMessage(10_004, LogLevel.Trace, "Test message with {Name}")] + static partial void LogTestMessage(ILogger logger, int Name); +} +"""; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .WithOutputKind(Microsoft.CodeAnalysis.OutputKind.DynamicallyLinkedLibrary) + .AddAdditionalFile("LoggerParameterTypes.txt", """ +Name;System.Int32 +""") + .ValidateAsync(); + } + + [Fact] + public async Task LoggerMessageAttribute_WithCallerMemberName() + { + const string SourceCode = """ +using Microsoft.Extensions.Logging; +using System.Runtime.CompilerServices; + +partial class LoggerExtensions +{ + [LoggerMessage(10_004, LogLevel.Trace, "Test message from {Method} with {Name}")] + static partial void LogTestMessage(ILogger logger, int Name, [CallerMemberName] string Method = ""); +} +"""; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .WithOutputKind(Microsoft.CodeAnalysis.OutputKind.DynamicallyLinkedLibrary) + .AddAdditionalFile("LoggerParameterTypes.txt", """ +Method;System.String +Name;System.Int32 +""") + .ValidateAsync(); + } + + [Fact] + public async Task LoggerMessageAttribute_NullableParameterType() + { + const string SourceCode = """ +using Microsoft.Extensions.Logging; + +partial class LoggerExtensions +{ + [LoggerMessage(10_004, LogLevel.Trace, "Test with {Value}")] + static partial void LogTestMessage(ILogger logger, int Value); + + [LoggerMessage(10_005, LogLevel.Trace, "Test with {Value}")] + static partial void LogTestMessage2(ILogger logger, int? Value); +} +"""; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .WithOutputKind(Microsoft.CodeAnalysis.OutputKind.DynamicallyLinkedLibrary) + .AddAdditionalFile("LoggerParameterTypes.txt", """ +Value;System.Nullable{System.Int32} +""") + .ValidateAsync(); + } + + [Fact] + public async Task LoggerMessageAttribute_NoConfiguration() + { + const string SourceCode = """ +using Microsoft.Extensions.Logging; + +partial class LoggerExtensions +{ + [LoggerMessage(10_004, LogLevel.Trace, "Test message with {Prop}")] + static partial void LogTestMessage(ILogger logger, string Prop); +} +"""; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .WithOutputKind(Microsoft.CodeAnalysis.OutputKind.DynamicallyLinkedLibrary) + .ValidateAsync(); + } + + [Fact] + public async Task LoggerMessageAttribute_EmptyFormatString() + { + const string SourceCode = """ +using Microsoft.Extensions.Logging; + +partial class LoggerExtensions +{ + [LoggerMessage(10_004, LogLevel.Trace, "")] + static partial void LogTestMessage(ILogger logger); +} +"""; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .WithOutputKind(Microsoft.CodeAnalysis.OutputKind.DynamicallyLinkedLibrary) + .AddAdditionalFile("LoggerParameterTypes.txt", """ +Name;System.Int32 +""") + .ValidateAsync(); + } + + [Fact] + public async Task LoggerMessageAttribute_NoFormatParameters() + { + const string SourceCode = """ +using Microsoft.Extensions.Logging; + +partial class LoggerExtensions +{ + [LoggerMessage(10_004, LogLevel.Trace, "Test message without parameters")] + static partial void LogTestMessage(ILogger logger); +} +"""; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) + .WithOutputKind(Microsoft.CodeAnalysis.OutputKind.DynamicallyLinkedLibrary) + .AddAdditionalFile("LoggerParameterTypes.txt", """ +Name;System.Int32 """) .ValidateAsync(); } From 7b801d93d0668b9fa6208360e829b9d3d6157ad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Tue, 14 Oct 2025 00:03:22 -0400 Subject: [PATCH 07/38] Remove NRT annotation in the error message to remove confusion --- .../Rules/LoggerParameterTypeAnalyzer.cs | 1 - .../Rules/LoggerParameterTypeAnalyzerTests.cs | 20 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Meziantou.Analyzer/Rules/LoggerParameterTypeAnalyzer.cs b/src/Meziantou.Analyzer/Rules/LoggerParameterTypeAnalyzer.cs index dc57bac1b..78194787b 100644 --- a/src/Meziantou.Analyzer/Rules/LoggerParameterTypeAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/LoggerParameterTypeAnalyzer.cs @@ -613,7 +613,6 @@ private static string FormatType(ISymbol? typeSymbol) #if ROSLYN_4_6_OR_GREATER SymbolDisplayMiscellaneousOptions.ExpandValueTuple | #endif - SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier | SymbolDisplayMiscellaneousOptions.UseErrorTypeSymbolName); return typeSymbol.ToDisplayString(format); } diff --git a/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzerTests.cs index bab9041d6..c03775935 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzerTests.cs @@ -319,6 +319,26 @@ await CreateProjectBuilder() .ValidateAsync(); } + [Fact] + public async Task ErrorMessageDoesNotAddNullableAnnotation() + { + const string SourceCode = """ +using Microsoft.Extensions.Logging; + +ILogger logger = null; +logger.LogInformation("{Prop}", [|(string?)null|]); +"""; + await CreateProjectBuilder() + .WithSourceCode(SourceCode) +#if ROSLYN_4_6_OR_GREATER + .ShouldReportDiagnosticWithMessage("""Log parameter 'Prop' must be of type 'global::System.Nullable' but is of type 'global::System.String'""") +#endif + .AddAdditionalFile("LoggerParameterTypes.txt", """ +Prop;System.Nullable{System.String} +""") + .ValidateAsync(); + } + [Fact] public async Task Logger_LogTrace_ValidParameterType_NullableInt32AllowsInt32() { From 39d0decc6b2cbd525fb43e9ae7964ba8f3ad4bb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Thu, 16 Oct 2025 12:55:54 -0400 Subject: [PATCH 08/38] Allow specifying culture invariant formats --- .../CultureInsensitiveTypeAttribute.cs | 7 ++- .../CultureSensitiveFormattingContext.cs | 48 +++++++++++---- .../Internals/TypeSymbolExtensions.cs | 15 +++-- .../Rules/UseIFormatProviderAnalyzer.cs | 4 +- ...itCultureSensitiveToStringAnalyzerTests.cs | 59 +++++++++++++++++++ 5 files changed, 110 insertions(+), 23 deletions(-) diff --git a/src/Meziantou.Analyzer.Annotations/CultureInsensitiveTypeAttribute.cs b/src/Meziantou.Analyzer.Annotations/CultureInsensitiveTypeAttribute.cs index 0284ffe95..d25bded94 100644 --- a/src/Meziantou.Analyzer.Annotations/CultureInsensitiveTypeAttribute.cs +++ b/src/Meziantou.Analyzer.Annotations/CultureInsensitiveTypeAttribute.cs @@ -1,7 +1,4 @@ #pragma warning disable CS1591 - -using System; - namespace Meziantou.Analyzer.Annotations; /// @@ -14,7 +11,11 @@ namespace Meziantou.Analyzer.Annotations; public sealed class CultureInsensitiveTypeAttribute : System.Attribute { public CultureInsensitiveTypeAttribute() { } + public CultureInsensitiveTypeAttribute(string format) => Format = format; public CultureInsensitiveTypeAttribute(System.Type type) => Type = type; + public CultureInsensitiveTypeAttribute(System.Type type, string format) + => (Type, Format) = (type, format); public Type? Type { get; } + public string? Format { get; } } diff --git a/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs b/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs index 3cae22484..c0c04845b 100755 --- a/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs +++ b/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs @@ -1,5 +1,7 @@ -using System.Linq; +using System; +using Meziantou.Analyzer.Rules; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Meziantou.Analyzer.Internals; @@ -256,23 +258,40 @@ private bool IsCultureSensitiveType(ITypeSymbol? typeSymbol, CultureSensitiveOpt if (!typeSymbol.Implements(SystemIFormattableSymbol)) return false; - if (typeSymbol.HasAttribute(CultureInsensitiveTypeAttributeSymbol)) + if (!IsCultureSensitiveTypeUsingAttribute(typeSymbol, format: null)) return false; - foreach (var attribute in compilation.Assembly.GetAttributes()) + return true; + } + + private bool IsCultureSensitiveTypeUsingAttribute(ITypeSymbol typeSymbol, string? format) + { + var attributes = typeSymbol.GetAttributes(CultureInsensitiveTypeAttributeSymbol); + foreach (var attr in attributes) { - if (attribute.ConstructorArguments.Length != 1) - continue; + if (attr.ConstructorArguments.IsEmpty) + return false; // no format is set, so the type is culture insensitive - if (!attribute.AttributeClass.IsEqualTo(CultureInsensitiveTypeAttributeSymbol)) + var attrFormat = attr.ConstructorArguments[0].Value; + if (attrFormat is null || (attrFormat is string attrFormatValue && (string.IsNullOrEmpty(attrFormatValue) || attrFormatValue == format))) + return false; // no format is set, so the type is culture insensitive + } + + foreach (var attribute in compilation.Assembly.GetAttributes(CultureInsensitiveTypeAttributeSymbol)) + { + if (attribute.ConstructorArguments.IsEmpty) continue; if (attribute.ConstructorArguments[0].Value is INamedTypeSymbol attributeType && attributeType.IsEqualTo(typeSymbol)) - return false; - } + { + if (attribute.ConstructorArguments.Length == 1) + return false; - if (compilation.Assembly.HasAttribute(CultureInsensitiveTypeAttributeSymbol)) - return false; + var attrFormat = attribute.ConstructorArguments[1].Value; + if (attrFormat is null || (attrFormat is string attrFormatValue && (string.IsNullOrEmpty(attrFormatValue) || attrFormatValue == format))) + return false; // no format is set, so the type is culture insensitive + } + } return true; } @@ -282,13 +301,15 @@ private bool IsCultureSensitiveType(ITypeSymbol? symbol, IOperation? format, IOp if (!IsCultureSensitiveType(symbol, options)) return false; + var formatString = format?.ConstantValue.Value as string; + if (instance is not null) { - if (IsConstantPositiveNumber(instance) && format is null or { ConstantValue: { HasValue: true, Value: "" } }) + if (IsConstantPositiveNumber(instance) && string.IsNullOrEmpty(formatString)) return false; } - if (symbol.IsNumberType() && format is { ConstantValue: { HasValue: true, Value: string formatString } } && formatString is "B" or ['x', ..] or ['X', ..]) + if (symbol.IsNumberType() && formatString is "B" or ['x', ..] or ['X', ..]) return false; if (symbol.IsDateTime() || symbol.IsEqualToAny(DateTimeOffsetSymbol, DateOnlySymbol, TimeOnlySymbol)) @@ -302,6 +323,9 @@ private bool IsCultureSensitiveType(ITypeSymbol? symbol, IOperation? format, IOp return false; } + if (symbol is not null && !IsCultureSensitiveTypeUsingAttribute(symbol, formatString)) + return false; + return true; } diff --git a/src/Meziantou.Analyzer/Internals/TypeSymbolExtensions.cs b/src/Meziantou.Analyzer/Internals/TypeSymbolExtensions.cs index 805f08058..7e26287d8 100755 --- a/src/Meziantou.Analyzer/Internals/TypeSymbolExtensions.cs +++ b/src/Meziantou.Analyzer/Internals/TypeSymbolExtensions.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.CodeAnalysis; @@ -59,10 +59,10 @@ public static bool IsOrImplements(this ITypeSymbol symbol, ITypeSymbol? interfac return GetAllInterfacesIncludingThis(symbol).Any(interfaceType.IsEqualTo); } - public static AttributeData? GetAttribute(this ISymbol symbol, ITypeSymbol? attributeType, bool inherits = true) + public static IEnumerable GetAttributes(this ISymbol symbol, ITypeSymbol? attributeType, bool inherits = true) { if (attributeType is null) - return null; + yield break; if (attributeType.IsSealed) { @@ -77,17 +77,20 @@ public static bool IsOrImplements(this ITypeSymbol symbol, ITypeSymbol? interfac if (inherits) { if (attribute.AttributeClass.IsOrInheritFrom(attributeType)) - return attribute; + yield return attribute; } else { if (attributeType.IsEqualTo(attribute.AttributeClass)) - return attribute; + yield return attribute; } } + } - return null; + public static AttributeData? GetAttribute(this ISymbol symbol, ITypeSymbol? attributeType, bool inherits = true) + { + return GetAttributes(symbol, attributeType, inherits).FirstOrDefault(); } public static bool HasAttribute(this ISymbol symbol, ITypeSymbol? attributeType, bool inherits = true) diff --git a/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs index a68f3aa06..5136e300b 100644 --- a/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Collections.Immutable; using Meziantou.Analyzer.Configurations; using Meziantou.Analyzer.Internals; @@ -66,7 +66,7 @@ public void AnalyzeInvocation(OperationAnalysisContext context) if (IsExcludedMethod(context, operation)) return; - + var options = MustUnwrapNullableTypes(context, operation) ? CultureSensitiveOptions.UnwrapNullableOfT : CultureSensitiveOptions.None; if (!_cultureSensitiveContext.IsCultureSensitiveOperation(operation, options)) return; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseImplicitCultureSensitiveToStringAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseImplicitCultureSensitiveToStringAnalyzerTests.cs index 5818d39db..ebd461e1d 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseImplicitCultureSensitiveToStringAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseImplicitCultureSensitiveToStringAnalyzerTests.cs @@ -378,6 +378,65 @@ await CreateProjectBuilder() .ValidateAsync(); } + [Fact] + public async Task IgnoreTypeUsingAssemblyAttribute_MultipleAttributes() + { + var sourceCode = """ +[assembly: Meziantou.Analyzer.Annotations.CultureInsensitiveTypeAttribute(typeof(System.DateTime), "a")] +[assembly: Meziantou.Analyzer.Annotations.CultureInsensitiveTypeAttribute(typeof(System.DateTime), "b")] +[assembly: Meziantou.Analyzer.Annotations.CultureInsensitiveTypeAttribute(typeof(System.DateTime), "c")] + +class Test +{ + void A() + { + _ = $"abc{new System.DateTime():b}"; + } +} +"""; + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task IgnoreTypeUsingAssemblyAttribute_WithFormat() + { + var sourceCode = """ +[assembly: Meziantou.Analyzer.Annotations.CultureInsensitiveTypeAttribute(typeof(System.DateTime), "abc")] + +class Test +{ + void A() + { + _ = $"abc{new System.DateTime():abc}"; + } +} +"""; + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task IgnoreTypeUsingAssemblyAttribute_WithFormatNotMatchingAttribute() + { + var sourceCode = """ +[assembly: Meziantou.Analyzer.Annotations.CultureInsensitiveTypeAttribute(typeof(System.DateTime), "abc")] + +class Test +{ + void A() + { + _ = $"abc[|{new System.DateTime():other}|]"; + } +} +"""; + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + [Fact] public async Task IgnoreTypeUsingAttribute() { From 3d7df2e31637295e94ce664284e75a2378725e95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Mon, 20 Oct 2025 17:02:08 -0400 Subject: [PATCH 09/38] Update documentation of MA0171 --- README.md | 2 +- docs/README.md | 6 +++--- docs/Rules/MA0171.md | 2 +- .../configuration/default.editorconfig | 2 +- src/Meziantou.Analyzer.Pack/configuration/none.editorconfig | 2 +- .../Rules/UsePatternMatchingInsteadOfHasValueAnalyzer.cs | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 89b3efe12..9d141deba 100755 --- a/README.md +++ b/README.md @@ -186,7 +186,7 @@ If you are already using other analyzers, you can check [which rules are duplica |[MA0168](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0168.md)|Performance|Use readonly struct for in or ref readonly parameter|ℹ️|❌|❌| |[MA0169](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0169.md)|Design|Use Equals method instead of operator|⚠️|✔️|❌| |[MA0170](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0170.md)|Design|Type cannot be used as an attribute argument|⚠️|❌|❌| -|[MA0171](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0171.md)|Usage|Use pattern matching instead of inequality operators for discrete value|ℹ️|❌|✔️| +|[MA0171](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0171.md)|Usage|Use pattern matching instead of HasValue for Nullable\ check|ℹ️|❌|✔️| |[MA0172](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0172.md)|Usage|Both sides of the logical operation are identical|⚠️|❌|❌| |[MA0173](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0173.md)|Design|Use LazyInitializer.EnsureInitialize|ℹ️|✔️|❌| |[MA0174](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0174.md)|Style|Record should use explicit 'class' keyword|ℹ️|❌|❌| diff --git a/docs/README.md b/docs/README.md index 6797c171a..40e15d7e6 100755 --- a/docs/README.md +++ b/docs/README.md @@ -170,7 +170,7 @@ |[MA0168](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0168.md)|Performance|Use readonly struct for in or ref readonly parameter|ℹ️|❌|❌| |[MA0169](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0169.md)|Design|Use Equals method instead of operator|⚠️|✔️|❌| |[MA0170](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0170.md)|Design|Type cannot be used as an attribute argument|⚠️|❌|❌| -|[MA0171](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0171.md)|Usage|Use pattern matching instead of inequality operators for discrete value|ℹ️|❌|✔️| +|[MA0171](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0171.md)|Usage|Use pattern matching instead of HasValue for Nullable\ check|ℹ️|❌|✔️| |[MA0172](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0172.md)|Usage|Both sides of the logical operation are identical|⚠️|❌|❌| |[MA0173](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0173.md)|Design|Use LazyInitializer.EnsureInitialize|ℹ️|✔️|❌| |[MA0174](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0174.md)|Style|Record should use explicit 'class' keyword|ℹ️|❌|❌| @@ -695,7 +695,7 @@ dotnet_diagnostic.MA0169.severity = warning # MA0170: Type cannot be used as an attribute argument dotnet_diagnostic.MA0170.severity = none -# MA0171: Use pattern matching instead of inequality operators for discrete value +# MA0171: Use pattern matching instead of HasValue for Nullable check dotnet_diagnostic.MA0171.severity = none # MA0172: Both sides of the logical operation are identical @@ -1224,7 +1224,7 @@ dotnet_diagnostic.MA0169.severity = none # MA0170: Type cannot be used as an attribute argument dotnet_diagnostic.MA0170.severity = none -# MA0171: Use pattern matching instead of inequality operators for discrete value +# MA0171: Use pattern matching instead of HasValue for Nullable check dotnet_diagnostic.MA0171.severity = none # MA0172: Both sides of the logical operation are identical diff --git a/docs/Rules/MA0171.md b/docs/Rules/MA0171.md index 7d7f684f2..a6ca21a0b 100644 --- a/docs/Rules/MA0171.md +++ b/docs/Rules/MA0171.md @@ -1,4 +1,4 @@ -# MA0171 - Use pattern matching instead of inequality operators for discrete value +# MA0171 - Use pattern matching instead of HasValue for Nullable\ check Use pattern matching instead of the `HasValue` property to check for non-nullable value types or nullable value types. diff --git a/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig b/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig index a996f92f7..b3dd70576 100644 --- a/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig +++ b/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig @@ -509,7 +509,7 @@ dotnet_diagnostic.MA0169.severity = warning # MA0170: Type cannot be used as an attribute argument dotnet_diagnostic.MA0170.severity = none -# MA0171: Use pattern matching instead of inequality operators for discrete value +# MA0171: Use pattern matching instead of HasValue for Nullable check dotnet_diagnostic.MA0171.severity = none # MA0172: Both sides of the logical operation are identical diff --git a/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig b/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig index b93fcd8d3..03c7f6b3f 100644 --- a/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig +++ b/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig @@ -509,7 +509,7 @@ dotnet_diagnostic.MA0169.severity = none # MA0170: Type cannot be used as an attribute argument dotnet_diagnostic.MA0170.severity = none -# MA0171: Use pattern matching instead of inequality operators for discrete value +# MA0171: Use pattern matching instead of HasValue for Nullable check dotnet_diagnostic.MA0171.severity = none # MA0172: Both sides of the logical operation are identical diff --git a/src/Meziantou.Analyzer/Rules/UsePatternMatchingInsteadOfHasValueAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UsePatternMatchingInsteadOfHasValueAnalyzer.cs index 1978734b6..72bc9720a 100644 --- a/src/Meziantou.Analyzer/Rules/UsePatternMatchingInsteadOfHasValueAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UsePatternMatchingInsteadOfHasValueAnalyzer.cs @@ -12,8 +12,8 @@ public sealed class UsePatternMatchingInsteadOfHasValueAnalyzer : DiagnosticAnal { private static readonly DiagnosticDescriptor Rule = new( RuleIdentifiers.UsePatternMatchingInsteadOfHasvalue, - title: "Use pattern matching instead of inequality operators for discrete value", - messageFormat: "Use pattern matching instead of inequality operators for discrete values", + title: "Use pattern matching instead of HasValue for Nullable check", + messageFormat: "Use pattern matching instead of HasValue for Nullable check", RuleCategories.Usage, DiagnosticSeverity.Info, isEnabledByDefault: false, From 7776a158a0e654b77bf4c3475fc0958a19c19b3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Wed, 22 Oct 2025 14:22:50 -0400 Subject: [PATCH 10/38] Bump version to 1.1.0 in project file --- .../Meziantou.Analyzer.Annotations.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Meziantou.Analyzer.Annotations/Meziantou.Analyzer.Annotations.csproj b/src/Meziantou.Analyzer.Annotations/Meziantou.Analyzer.Annotations.csproj index 3bf3b6f41..46d6cd038 100644 --- a/src/Meziantou.Analyzer.Annotations/Meziantou.Analyzer.Annotations.csproj +++ b/src/Meziantou.Analyzer.Annotations/Meziantou.Analyzer.Annotations.csproj @@ -2,8 +2,8 @@ netstandard2.0 - 1.0.0 - 1.0.0 + 1.1.0 + 1.1.0 Annotations to configure Meziantou.Analyzer Meziantou.Analyzer, analyzers True From ce060a1bce4e0eb7d02ec4d68ffb4cc71e6b05bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Wed, 22 Oct 2025 20:43:12 -0400 Subject: [PATCH 11/38] Apply style suggestion (#892) --- .editorconfig | 3 +- Directory.Build.props | 2 +- global.json | 8 +-- .../DocumentationGenerator.csproj | 2 +- src/DocumentationGenerator/Program.cs | 2 - src/ListDotNetTypes/ListDotNetTypes.csproj | 2 +- .../Internals/FixAllContextHelper.cs | 9 +--- .../Meziantou.Analyzer.CodeFixers.csproj | 2 +- ...ractTypesShouldNotHaveConstructorsFixer.cs | 5 +- ...ExceptionShouldSpecifyArgumentNameFixer.cs | 5 +- .../AvoidComparisonWithBoolConstantFixer.cs | 5 +- .../AvoidUsingRedundantElseFixAllProvider.cs | 3 +- .../Rules/AvoidUsingRedundantElseFixer.cs | 5 +- .../Rules/ClassMustBeSealedFixer.cs | 4 +- .../Rules/CommaFixer.cs | 4 +- ...riginalExceptionFromThrowStatementFixer.cs | 4 +- ...DoNotUseBlockingCallInAsyncContextFixer.cs | 3 -- ...UseEqualityComparerDefaultOfStringFixer.cs | 3 -- ...tUseEqualityOperatorsForSpanOfCharFixer.cs | 4 +- .../Rules/DoNotUseStringGetHashCodeFixer.cs | 3 -- ...ualityShouldBeCorrectlyImplementedFixer.cs | 5 +- ...tsShouldHaveProperArgumentsFixer.MA0091.cs | 4 +- .../Rules/MakeClassStaticFixer.cs | 4 +- .../Rules/MakeInterpolatedStringFixer.cs | 4 -- .../Rules/MakeMemberReadOnlyFixer.cs | 5 +- .../Rules/MakeMethodStaticFixer.cs | 6 +-- ...ributesWithAttributeUsageAttributeFixer.cs | 3 -- ...esShouldNotChangeParameterDefaultsFixer.cs | 4 +- .../Rules/NamedParameterFixer.cs | 4 +- ...otPatternShouldBeParenthesizedCodeFixer.cs | 5 +- .../Rules/OptimizeGuidCreationFixer.cs | 6 +-- .../Rules/OptimizeLinqUsageFixer.cs | 5 -- .../Rules/OptimizeStartsWithFixer.cs | 5 +- .../Rules/OptimizeStringBuilderUsageFixer.cs | 10 +--- ...zorComponentFixer.AddParameterAttribute.cs | 4 +- .../Rules/PreserveParamsOnOverrideFixer.cs | 4 +- .../Rules/RemoveEmptyStatementFixer.cs | 4 +- .../Rules/RemoveUselessToStringFixer.cs | 4 +- .../ReplaceEnumToStringWithNameofFixer.cs | 4 +- ...skFromResultInsteadOfReturningNullFixer.cs | 4 +- .../SimplifyCallerArgumentExpressionFixer.cs | 6 +-- ...tContainsNonDeterministicEndOfLineFixer.cs | 3 -- ...dThatHasCancellationTokenFixer_Argument.cs | 5 +- ...tHasCancellationTokenFixer_AwaitForEach.cs | 4 +- .../UseAnOverloadThatHasTimeProviderFixer.cs | 4 -- .../Rules/UseArrayEmptyFixer.cs | 4 +- .../Rules/UseConfigureAwaitFixer.cs | 3 -- .../Rules/UseDateTimeUnixEpochFixer.cs | 4 +- .../Rules/UseEventArgsEmptyFixer.cs | 3 -- .../Rules/UseGuidEmptyFixer.cs | 3 -- ...UseIsPatternInsteadOfSequenceEqualFixer.cs | 4 +- ...eVoidAsyncWhenReturnValueIsNotUsedFixer.cs | 4 +- .../Rules/UseLangwordInXmlCommentFixer.cs | 4 +- ...ternMatchingForEqualityComparisonsFixer.cs | 4 +- ...sePatternMatchingInsteadOfHasvalueFixer.cs | 4 +- .../Rules/UseRegexSourceGeneratorFixer.cs | 9 +--- .../Rules/UseStringComparerFixer.cs | 3 -- .../Rules/UseStringComparisonFixer.cs | 3 -- ...ngCreateInsteadOfFormattableStringFixer.cs | 4 +- .../Rules/UseStringEqualsFixer.cs | 3 -- .../Rules/UseStructLayoutAttributeFixer.cs | 4 +- .../Rules/ValidateArgumentsCorrectlyFixer.cs | 5 -- .../AnalyzerOptionsExtensions.cs | 3 -- .../Internals/AwaitableTypes.cs | 2 - .../Internals/ConcurrentHashSet.cs | 3 +- .../Internals/ContextExtensions.cs | 3 -- .../CultureSensitiveFormattingContext.cs | 3 -- .../Internals/CultureSensitiveOptions.cs | 2 - .../Internals/DiagnosticFieldReportOptions.cs | 2 - .../DiagnosticInvocationReportOptions.cs | 2 - .../DiagnosticMethodReportOptions.cs | 2 - .../DiagnosticParameterReportOptions.cs | 2 - .../DiagnosticPropertyReportOptions.cs | 2 - .../Internals/DiagnosticReporter.cs | 2 - .../Internals/EnumerableExtensions.cs | 4 -- .../Internals/HashSetExtensions.cs | 2 - .../Internals/ListExtensions.cs | 2 - .../Internals/LocationExtensions.cs | 1 - .../Internals/MethodSymbolExtensions.cs | 1 - .../Internals/ObjectPool.cs | 5 +- .../Internals/OperationExtensions.cs | 4 -- .../Internals/OverloadFinder.cs | 3 +- .../Internals/StringExtensions.cs | 1 - .../Internals/SuppressorHelpers.cs | 1 - .../Internals/SymbolExtensions.cs | 7 +-- .../Internals/SyntaxNodeExtensions.cs | 2 - .../Internals/SyntaxTokenListExtensions.cs | 1 - .../Internals/TimeSpanOperation.cs | 1 - .../Internals/TypeSymbolExtensions.cs | 3 -- .../Meziantou.Analyzer.csproj | 2 +- src/Meziantou.Analyzer/RuleIdentifiers.cs | 2 - .../AddOverloadWithSpanOrMemoryAnalyzer.cs | 3 +- ...eptionShouldSpecifyArgumentNameAnalyzer.cs | 4 -- ...reWhenUsingConcurrentDictionaryAnalyzer.cs | 3 -- ...AvoidComparisonWithBoolConstantAnalyzer.cs | 3 +- .../Rules/AvoidUsingRedundantElseAnalyzer.cs | 2 - .../AvoidUsingRedundantElseAnalyzerCommon.cs | 1 - ...aitTaskBeforeDisposingResourcesAnalyzer.cs | 4 +- .../Rules/ClassMustBeSealedAnalyzer.cs | 5 +- ...metersShouldExistInConstructorsAnalyzer.cs | 1 - ...teShouldContainValidExpressionsAnalyzer.cs | 8 +-- ...tCallVirtualMethodInConstructorAnalyzer.cs | 3 +- ...DoNotDeclareStaticMembersOnGenericTypes.cs | 4 +- .../Rules/DoNotLogClassifiedDataAnalyzer.cs | 2 - .../Rules/DoNotNaNInComparisonsAnalyzer.cs | 5 +- ...DoNotRaiseReservedExceptionTypeAnalyzer.cs | 2 - ...inalExceptionFromThrowStatementAnalyzer.cs | 3 +- .../Rules/DoNotThrowFromFinalizerAnalyzer.cs | 3 +- .../DoNotThrowFromFinallyBlockAnalyzer.cs | 3 +- ...otUseBlockingCallInAsyncContextAnalyzer.cs | 8 +-- ...EqualityComparerDefaultOfStringAnalyzer.cs | 4 +- ...eEqualityOperatorsForSpanOfCharAnalyzer.cs | 5 +- ...erCertificateValidationCallbackAnalyzer.cs | 2 - .../Rules/DoNotUseToStringIfObjectAnalyzer.cs | 3 +- ...knownParameterForRazorComponentAnalyzer.cs | 3 -- ...ontUseDangerousThreadingMethodsAnalyzer.cs | 1 - .../Rules/DotNotUseNameFromBCLAnalyzer.cs | 4 -- ...CaughtExceptionAsInnerExceptionAnalyzer.cs | 3 +- ...ityShouldBeCorrectlyImplementedAnalyzer.cs | 3 -- ...EventsShouldHaveProperArgumentsAnalyzer.cs | 2 - .../FileNameMustMatchTypeNameAnalyzer.cs | 5 +- .../Rules/FixToDoAnalyzer.cs | 2 - ...alVariablesShouldNotHideSymbolsAnalyzer.cs | 2 - .../Rules/LoggerParameterTypeAnalyzer.cs | 16 ++---- .../Rules/MakeClassStaticAnalyzer.cs | 3 -- .../Rules/MakeInterpolatedStringAnalyzer.cs | 2 - .../Rules/MakeMemberReadOnlyAnalyzer.cs | 3 +- .../Rules/MakeMethodStaticAnalyzer.cs | 3 -- ...houldNotChangeParameterDefaultsAnalyzer.cs | 4 +- .../Rules/MethodShouldNotBeTooLongAnalyzer.cs | 6 +-- ...tableTypeMustHaveTheAsyncSuffixAnalyzer.cs | 1 - .../Rules/NamedParameterAnalyzer.cs | 52 +++++++++---------- ...ldNotBeMarkedWithFlagsAttributeAnalyzer.cs | 3 -- .../Rules/NullableAttributeUsageAnalyzer.cs | 3 +- .../Rules/OptimizeLinqUsageAnalyzer.cs | 17 +++--- .../Rules/OptimizeStartsWithAnalyzer.cs | 2 - .../OptimizeStringBuilderUsageAnalyzer.cs | 3 -- ...ptimizeStringBuilderUsageAnalyzerCommon.cs | 1 - ...meterAttributeForRazorComponentAnalyzer.cs | 1 - ...tractionInsteadOfImplementationAnalyzer.cs | 1 - .../Rules/PreserveParamsOnOverrideAnalyzer.cs | 4 +- ...ructorParameterShouldBeReadOnlyAnalyzer.cs | 5 +- .../Rules/ProcessStartAnalyzer.cs | 3 +- .../Rules/RegexUsageAnalyzerBase.cs | 2 - .../Rules/RemoveEmptyBlockAnalyzer.cs | 3 +- .../Rules/RemoveUselessToStringAnalyzer.cs | 1 - ...romResultInsteadOfReturningNullAnalyzer.cs | 3 +- ...ultInsteadOfReturningNullAnalyzerCommon.cs | 1 - .../SequenceNumberMustBeAConstantAnalyzer.cs | 2 - ...implifyCallerArgumentExpressionAnalyzer.cs | 3 +- ...owIfNullWithNonNullableInstanceAnalyzer.cs | 1 - .../TypeNameMustNotMatchNamespaceAnalyzer.cs | 1 - ...verloadThatHasCancellationTokenAnalyzer.cs | 14 ++--- ...seAnOverloadThatHasTimeProviderAnalyzer.cs | 17 ++---- .../Rules/UseArrayEmptyAnalyzer.cs | 3 +- .../Rules/UseConfigureAwaitAnalyzer.cs | 3 -- ...ContainsKeyInsteadOfTryGetValueAnalyzer.cs | 2 - .../Rules/UseDateTimeUnixEpochAnalyzer.cs | 1 - ...seEqualsMethodInsteadOfOperatorAnalyzer.cs | 1 - .../Rules/UseEventHandlerOfTAnalyzer.cs | 3 +- .../Rules/UseIFormatProviderAnalyzer.cs | 1 - ...IsPatternInsteadOfSequenceEqualAnalyzer.cs | 4 +- ...idAsyncWhenReturnValueIsNotUsedAnalyzer.cs | 4 +- .../Rules/UseLangwordInXmlCommentAnalyzer.cs | 2 - ...stemInsteadOfRuntimeInformationAnalyzer.cs | 6 +-- ...nMatchingForEqualityComparisonsAnalyzer.cs | 3 +- ...atternMatchingInsteadOfHasValueAnalyzer.cs | 3 +- .../Rules/UseRegexSourceGeneratorAnalyzer.cs | 2 - .../Rules/UseStringComparerAnalyzer.cs | 3 -- .../Rules/UseStringComparisonAnalyzer.cs | 1 - ...reateInsteadOfFormattableStringAnalyzer.cs | 3 +- .../Rules/UseStringEqualsAnalyzer.cs | 5 +- .../Rules/UseStructLayoutAttributeAnalyzer.cs | 3 +- .../ValidateArgumentsCorrectlyAnalyzer.cs | 5 +- ...eturnedByStreamReadShouldBeUsedAnalyzer.cs | 7 ++- .../Helpers/DiagnosticResult.cs | 7 +-- .../Helpers/DiagnosticResultLocation.cs | 1 - .../Helpers/ProjectBuilder.Validation.cs | 6 +-- .../Helpers/ProjectBuilder.cs | 30 +++-------- .../Helpers/SharedHttpClient.cs | 5 -- .../TestAnalyzerConfigOptionsProvider.cs | 1 - .../Meziantou.Analyzer.Test.csproj | 5 +- tests/Meziantou.Analyzer.Test/RuleHelpUri.cs | 2 - ...sShouldNotHaveConstructorsAnalyzerTests.cs | 2 - ...ddOverloadWithSpanOrMemoryAnalyzerTests.cs | 2 - ...sedToUnsubscribeFromEventsAnalyzerTests.cs | 2 - ...nShouldSpecifyArgumentNameAnalyzerTests.cs | 2 - ...cifyArgumentNameAnalyzer_UseNameofTests.cs | 2 - ...NameShouldEndWithAttributeAnalyzerTests.cs | 2 - ...ComparisonWithBoolConstantAnalyzerTests.cs | 2 - ...PubliclyAccessibleInstanceAnalyzerTests.cs | 2 - .../AvoidUsingRedundantElseAnalyzerTests.cs | 2 - ...waitableMethodInSyncMethodAnalyzerTests.cs | 2 - ...skBeforeDisposingResourcesAnalyzerTests.cs | 2 - ...OfTheConditionAreIdenticalAnalyzerTests.cs | 1 - .../Rules/ClassMustBeSealedAnalyzerTests.cs | 2 - .../Rules/CommaAnalyzerTests.cs | 2 - ...WhenAccessingTheKeyAnalyzerTests_MA0105.cs | 2 - ...WhenAccessingTheKeyAnalyzerTests_MA0106.cs | 2 - ...sShouldExistInConstructorsAnalyzerTests.cs | 2 - .../DeclareTypesInNamespacesAnalyzerTests.cs | 2 - ...VirtualMethodInConstructorAnalyzerTests.cs | 2 - ...eWithDateTimeOffsetAnalyzerTests_MA0132.cs | 2 - ...eWithDateTimeOffsetAnalyzerTests_MA0133.cs | 2 - ...DeclareStaticMembersOnGenericTypesTests.cs | 2 - .../DoNotLogClassifiedDataAnalyzerTests.cs | 2 - .../DoNotNaNInComparisonsAnalyzerTests.cs | 2 - ...rwriteRazorComponentParameterValueTests.cs | 2 - ...tRaiseApplicationExceptionAnalyzerTests.cs | 4 +- ...iseNotImplementedExceptionAnalyzerTests.cs | 4 +- ...RaiseReservedExceptionTypeAnalyzerTests.cs | 4 +- ...xceptionFromThrowStatementAnalyzerTests.cs | 2 - .../DoNotThrowFromFinalizerAnalyzerTests.cs | 2 - ...DoNotThrowFromFinallyBlockAnalyzerTests.cs | 2 - ...yncDelegateForSyncDelegateAnalyzerTests.cs | 2 - .../Rules/DoNotUseAsyncVoidAnalyzerTests.cs | 2 - ...nAsyncContextAnalyzer_AsyncContextTests.cs | 2 - ...yncContextAnalyzer_NonAsyncContextTests.cs | 2 - ...ltEqualsOnValueTypeAnalyzer_EqualsTests.cs | 2 - ...tEqualsOnValueTypeAnalyzer_HashSetTests.cs | 4 +- ...ityComparerDefaultOfStringAnalyzerTests.cs | 3 +- ...lityOperatorsForSpanOfCharAnalyzerTests.cs | 2 - .../DoNotUseFinalizerAnalyzerTests.cs.cs | 2 - ...itCultureSensitiveToStringAnalyzerTests.cs | 2 - ...tificateValidationCallbackAnalyzerTests.cs | 2 - .../DoNotUseStringGetHashCodeAnalyzerTests.cs | 2 - .../DoNotUseToStringIfObjectAnalyzerTests.cs | 2 - ...ParameterForRazorComponentAnalyzerTests.cs | 2 - ...oNotUseZeroToInitializeAnEnumValueTests.cs | 2 - ...sWithThreadStaticAttributeAnalyzerTests.cs | 2 - ...eDangerousThreadingMethodsAnalyzerTests.cs | 2 - .../DotNotUseNameFromBCLAnalyzerTests.cs | 2 - ...tExceptionAsInnerExceptionAnalyzerTests.cs | 2 - ...CorrectlyImplementedAnalyzerMA0077Tests.cs | 2 - ...CorrectlyImplementedAnalyzerMA0094Tests.cs | 2 - ...CorrectlyImplementedAnalyzerMA0097Tests.cs | 2 - ...NameShouldEndWithEventArgsAnalyzerTests.cs | 2 - ...sShouldHaveProperArgumentsAnalyzerTests.cs | 2 - ...NameShouldEndWithExceptionAnalyzerTests.cs | 2 - .../FileNameMustMatchTypeNameAnalyzerTests.cs | 2 - .../Rules/FixToDoAnalyzerTests.cs | 2 - ...IfElseBranchesAreIdenticalAnalyzerTests.cs | 2 - ...stNotBeUsedInOnInitializedAnalyzerTests.cs | 2 - ...vokableMethodsMustBePublicAnalyzerTests.cs | 2 - ...iablesShouldNotHideSymbolsAnalyzerTests.cs | 2 - .../Rules/LoggerParameterTypeAnalyzerTests.cs | 2 - ...oggerParameterTypeAnalyzer_SerilogTests.cs | 2 - .../Rules/MakeClassStaticAnalyzerTests.cs | 2 - .../MakeInterpolatedStringAnalyzerTests.cs | 2 - .../Rules/MakeMemberReadOnlyAnalyzerTests.cs | 2 - .../MakeMethodStaticAnalyzerTests_Methods.cs | 2 - ...akeMethodStaticAnalyzerTests_Properties.cs | 2 - ...ributesWithAttributeUsageAttributeTests.cs | 2 - ...NotChangeParameterDefaultsAnalyzerTests.cs | 2 - .../MethodShouldNotBeTooLongAnalyzerTests.cs | 3 -- ...TypeMustHaveTheAsyncSuffixAnalyzerTests.cs | 2 - .../Rules/NamedParameterAnalyzerTests.cs | 2 - ...icFieldsShouldNotBeVisibleAnalyzerTests.cs | 2 - ...BeMarkedWithFlagsAttributeAnalyzerTests.cs | 4 -- ...tternShouldBeParenthesizedAnalyzerTests.cs | 2 - .../NullableAttributeUsageAnalyzerTests.cs | 2 - ...bjectGetTypeOnTypeInstanceAnalyzerTests.cs | 2 - ...sShouldIncludeExplanationsAnalyzerTests.cs | 2 - .../Rules/OptimizeGuidParsingAnalyzerTests.cs | 3 +- ...inqUsageAnalyzerCombineLinqMethodsTests.cs | 2 - .../OptimizeLinqUsageAnalyzerCountTests.cs | 2 - ...eLinqUsageAnalyzerDuplicateOrderByTests.cs | 2 - .../OptimizeLinqUsageAnalyzerOrderTests.cs | 2 - ...sageAnalyzerUseCastInsteadOfSelectTests.cs | 2 - ...qUsageAnalyzerUseCountInsteadOfAnyTests.cs | 2 - ...eLinqUsageAnalyzerUseDirectMethodsTests.cs | 2 - ...ptimizeLinqUsageAnalyzerUseIndexerTests.cs | 2 - ...inqUsageAnalyzerWhereBeforeOrderByTests.cs | 2 - .../Rules/OptimizeStartsWithAnalyzerTests.cs | 2 - ...OptimizeStringBuilderUsageAnalyzerTests.cs | 3 -- ...lParametersAttributeAnalyzerMA0087Tests.cs | 2 - ...lParametersAttributeAnalyzerMA0088Tests.cs | 2 - ...AttributeForRazorComponentAnalyzerTests.cs | 2 - ...ionInsteadOfImplementationAnalyzerTests.cs | 2 - .../PreserveParamsOnOverrideAnalyzerTests.cs | 2 - ...rParameterShouldBeReadOnlyAnalyzerTests.cs | 4 +- .../Rules/ProcessStartAnalyzerTests.cs | 2 - .../Rules/RemoveEmptyBlockAnalyzerTests.cs | 2 - .../RemoveEmptyStatementAnalyzerTests.cs | 2 - .../RemoveUselessToStringAnalyzerTests.cs | 2 - ...laceEnumToStringWithNameofAnalyzerTests.cs | 2 - ...sultInsteadOfReturningNullAnalyzerTests.cs | 2 - ...uenceNumberMustBeAConstantAnalyzerTests.cs | 2 - ...fyCallerArgumentExpressionAnalyzerTests.cs | 2 - ...sNonDeterministicEndOfLineAnalyzerTests.cs | 2 - .../Rules/TaskInUsingAnalyzerTests.cs | 2 - ...ullWithNonNullableInstanceAnalyzerTests.cs | 2 - ...UsedInAnAttributeParameterAnalyzerTests.cs | 2 - ...eNameMustNotMatchNamespaceAnalyzerTests.cs | 2 - ...SystemApplicationExceptionAnalyzerTests.cs | 2 - ...adThatHasCancellationTokenAnalyzerTests.cs | 2 - ...verloadThatHasTimeProviderAnalyzerTests.cs | 2 - .../Rules/UseArrayEmptyAnalyzerTests.cs | 2 - .../Rules/UseConfigureAwaitAnalyzerTests.cs | 2 - ...insKeyInsteadOfTryGetValueAnalyzerTests.cs | 2 - .../UseDateTimeUnixEpochAnalyzerTests.cs | 2 - ...alsMethodInsteadOfOperatorAnalyzerTests.cs | 2 - .../Rules/UseEventArgsEmptyAnalyzerTests.cs | 2 - .../Rules/UseEventHandlerOfTAnalyzerTests.cs | 2 - .../Rules/UseGuidEmptyAnalyzerTests.cs | 2 - .../Rules/UseIFormatProviderAnalyzerTests.cs | 2 - ...ternInsteadOfSequenceEqualAnalyzerTests.cs | 4 +- ...eVoidAsyncWhenReturnValueIsNotUsedTests.cs | 2 - .../UseLangwordInXmlCommentAnalyzerTests.cs | 2 - ...nitializerEnsureInitializeAnalyzerTests.cs | 1 - ...nsteadOfRuntimeInformationAnalyzerTests.cs | 2 - ...qualityComparisonsAnalyzerHasValueTests.cs | 3 +- ...hingForEqualityComparisonsAnalyzerTests.cs | 2 - ...ctForRefReadOnlyParametersAnalyzerTests.cs | 2 - .../Rules/UseRegexOptionsAnalyzerTests.cs | 2 - .../UseRegexSourceGeneratorAnalyzerTests.cs | 2 - .../Rules/UseRegexTimeoutAnalyzerTests.cs | 2 - .../Rules/UseStringComparerAnalyzerTests.cs | 2 - ...parisonAnalyzerNonCultureSensitiveTests.cs | 2 - .../Rules/UseStringComparisonAnalyzerTests.cs | 2 - ...InsteadOfFormattableStringAnalyzerTests.cs | 2 - .../Rules/UseStringEqualsAnalyzerTests.cs | 2 - ...ngEqualsInsteadOfIsPatternAnalyzerTests.cs | 2 - .../UseStructLayoutAttributeAnalyzerTests.cs | 2 - ...readingLockInsteadOfObjectAnalyzerTests.cs | 2 - .../Rules/UseTaskUnwrapAnalyzerTests.cs | 2 - ...ValidateArgumentsCorrectlyAnalyzerTests.cs | 2 - ...safeAccessorAttributeUsageAnalyzerTests.cs | 2 - ...edByStreamReadShouldBeUsedAnalyzerTests.cs | 2 - ...erializationPropertyNameSuppressorTests.cs | 2 - .../CA1822DecoratedMethodSuppressorTests.cs | 4 +- .../Suppressors/IDE0058SuppressorTests.cs | 4 +- 332 files changed, 178 insertions(+), 882 deletions(-) diff --git a/.editorconfig b/.editorconfig index c2c0b3e71..378f83d75 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -# reference:https://raw.githubusercontent.com/meziantou/Meziantou.DotNet.CodingStandard/refs/heads/main/.editorconfig +# reference:https://raw.githubusercontent.com/meziantou/Meziantou.DotNet.CodingStandard/refs/heads/main/.editorconfig # Schema: http://EditorConfig.org # Docs: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference @@ -41,7 +41,6 @@ insert_final_newline = true # endreference [*.cs] -dotnet_diagnostic.IDE0058.severity = none dotnet_diagnostic.IDE0303.severity = none dotnet_diagnostic.CA1508.severity = none dotnet_diagnostic.CA2000.severity = none diff --git a/Directory.Build.props b/Directory.Build.props index 70297f456..fa20c6b33 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -11,7 +11,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/global.json b/global.json index e7a1b53ab..7d0d05b7d 100644 --- a/global.json +++ b/global.json @@ -1,11 +1,11 @@ { "sdk": { - "version": "9.0.304", + "version": "10.0.100-rc.2.25502.107", "rollForward": "latestPatch" }, "msbuild-sdks": { - "Meziantou.NET.Sdk": "1.0.9", - "Meziantou.NET.Sdk.Test": "1.0.9", - "Meziantou.NET.Sdk.Web": "1.0.9" + "Meziantou.NET.Sdk": "1.0.38", + "Meziantou.NET.Sdk.Test": "1.0.38", + "Meziantou.NET.Sdk.Web": "1.0.38" } } diff --git a/src/DocumentationGenerator/DocumentationGenerator.csproj b/src/DocumentationGenerator/DocumentationGenerator.csproj index 42fa597dd..759553d17 100644 --- a/src/DocumentationGenerator/DocumentationGenerator.csproj +++ b/src/DocumentationGenerator/DocumentationGenerator.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/DocumentationGenerator/Program.cs b/src/DocumentationGenerator/Program.cs index a17664828..d6c7aad06 100644 --- a/src/DocumentationGenerator/Program.cs +++ b/src/DocumentationGenerator/Program.cs @@ -2,8 +2,6 @@ #pragma warning disable CA1849 #pragma warning disable MA0004 #pragma warning disable MA0009 -using System.Globalization; -using System.Text; using System.Text.Encodings.Web; using System.Text.RegularExpressions; using Meziantou.Framework; diff --git a/src/ListDotNetTypes/ListDotNetTypes.csproj b/src/ListDotNetTypes/ListDotNetTypes.csproj index 2348a2ee0..e37b0a6b6 100644 --- a/src/ListDotNetTypes/ListDotNetTypes.csproj +++ b/src/ListDotNetTypes/ListDotNetTypes.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/Meziantou.Analyzer.CodeFixers/Internals/FixAllContextHelper.cs b/src/Meziantou.Analyzer.CodeFixers/Internals/FixAllContextHelper.cs index 05507550e..bc720e4b7 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Internals/FixAllContextHelper.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Internals/FixAllContextHelper.cs @@ -1,13 +1,10 @@ -// File initially copied from +// File initially copied from // https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/4d9b3e3bb785a55f73b3029a843f0c0b73cc9ea7/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/Helpers/FixAllContextHelper.cs // Original copyright statement: // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. using System.Collections.Concurrent; using System.Collections.Immutable; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; @@ -40,9 +37,7 @@ public static async Task p.Language == project.Language) - .ToImmutableArray(); + projectsToFix = ImmutableArray.Empty.AddRange(project.Solution.Projects.Where(p => p.Language == project.Language)); var diagnostics = new ConcurrentDictionary>(); var tasks = new Task[projectsToFix.Length]; diff --git a/src/Meziantou.Analyzer.CodeFixers/Meziantou.Analyzer.CodeFixers.csproj b/src/Meziantou.Analyzer.CodeFixers/Meziantou.Analyzer.CodeFixers.csproj index 6dc7ebe5a..9befcd77f 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Meziantou.Analyzer.CodeFixers.csproj +++ b/src/Meziantou.Analyzer.CodeFixers/Meziantou.Analyzer.CodeFixers.csproj @@ -1,6 +1,6 @@  - net9.0;netstandard2.0 + net10.0;netstandard2.0 false 1.0.1 bin\$(RoslynVersion)\ diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/AbstractTypesShouldNotHaveConstructorsFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/AbstractTypesShouldNotHaveConstructorsFixer.cs index ed0b65fc0..03c8175e8 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/AbstractTypesShouldNotHaveConstructorsFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/AbstractTypesShouldNotHaveConstructorsFixer.cs @@ -1,8 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/ArgumentExceptionShouldSpecifyArgumentNameFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/ArgumentExceptionShouldSpecifyArgumentNameFixer.cs index 0f93ecedf..296fdd478 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/ArgumentExceptionShouldSpecifyArgumentNameFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/ArgumentExceptionShouldSpecifyArgumentNameFixer.cs @@ -1,8 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/AvoidComparisonWithBoolConstantFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/AvoidComparisonWithBoolConstantFixer.cs index f95de44bf..509d89b7e 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/AvoidComparisonWithBoolConstantFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/AvoidComparisonWithBoolConstantFixer.cs @@ -1,8 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Globalization; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/AvoidUsingRedundantElseFixAllProvider.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/AvoidUsingRedundantElseFixAllProvider.cs index fbea59859..eff033980 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/AvoidUsingRedundantElseFixAllProvider.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/AvoidUsingRedundantElseFixAllProvider.cs @@ -1,5 +1,4 @@ -using System.Collections.Immutable; -using System.Threading.Tasks; +using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/AvoidUsingRedundantElseFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/AvoidUsingRedundantElseFixer.cs index 41ecc4cab..1d6af4791 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/AvoidUsingRedundantElseFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/AvoidUsingRedundantElseFixer.cs @@ -1,8 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/ClassMustBeSealedFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/ClassMustBeSealedFixer.cs index f6effc163..91e7e4f0f 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/ClassMustBeSealedFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/ClassMustBeSealedFixer.cs @@ -1,7 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/CommaFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/CommaFixer.cs index 8a8a9b8cb..ee1bcd259 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/CommaFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/CommaFixer.cs @@ -1,7 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotRemoveOriginalExceptionFromThrowStatementFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotRemoveOriginalExceptionFromThrowStatementFixer.cs index 6f49e947f..30a2d196e 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotRemoveOriginalExceptionFromThrowStatementFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotRemoveOriginalExceptionFromThrowStatementFixer.cs @@ -1,7 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseBlockingCallInAsyncContextFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseBlockingCallInAsyncContextFixer.cs index 1f2bb7431..03b5b6ee8 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseBlockingCallInAsyncContextFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseBlockingCallInAsyncContextFixer.cs @@ -1,8 +1,5 @@ -using System; using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseEqualityComparerDefaultOfStringFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseEqualityComparerDefaultOfStringFixer.cs index c2b7abbe4..c5dd8ed3b 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseEqualityComparerDefaultOfStringFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseEqualityComparerDefaultOfStringFixer.cs @@ -1,8 +1,5 @@ -using System; using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseEqualityOperatorsForSpanOfCharFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseEqualityOperatorsForSpanOfCharFixer.cs index d8971b883..b22f331fd 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseEqualityOperatorsForSpanOfCharFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseEqualityOperatorsForSpanOfCharFixer.cs @@ -1,7 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseStringGetHashCodeFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseStringGetHashCodeFixer.cs index b30907044..557b297c1 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseStringGetHashCodeFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseStringGetHashCodeFixer.cs @@ -1,8 +1,5 @@ -using System; using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/EqualityShouldBeCorrectlyImplementedFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/EqualityShouldBeCorrectlyImplementedFixer.cs index d5bf6ae52..35faf59d5 100755 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/EqualityShouldBeCorrectlyImplementedFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/EqualityShouldBeCorrectlyImplementedFixer.cs @@ -1,8 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/EventsShouldHaveProperArgumentsFixer.MA0091.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/EventsShouldHaveProperArgumentsFixer.MA0091.cs index 64403be22..41fea353c 100755 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/EventsShouldHaveProperArgumentsFixer.MA0091.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/EventsShouldHaveProperArgumentsFixer.MA0091.cs @@ -1,7 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/MakeClassStaticFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/MakeClassStaticFixer.cs index bb33b0e11..257af17de 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/MakeClassStaticFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/MakeClassStaticFixer.cs @@ -1,7 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/MakeInterpolatedStringFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/MakeInterpolatedStringFixer.cs index 686320aba..68f8b550d 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/MakeInterpolatedStringFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/MakeInterpolatedStringFixer.cs @@ -1,15 +1,11 @@ -using System; using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; -using Microsoft.CodeAnalysis.Formatting; namespace Meziantou.Analyzer.Rules; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/MakeMemberReadOnlyFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/MakeMemberReadOnlyFixer.cs index aadb5961c..d2641b635 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/MakeMemberReadOnlyFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/MakeMemberReadOnlyFixer.cs @@ -1,8 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/MakeMethodStaticFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/MakeMethodStaticFixer.cs index 61377f1c6..25b50cdb7 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/MakeMethodStaticFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/MakeMethodStaticFixer.cs @@ -1,7 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; @@ -27,7 +25,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); var nodeToFix = root?.FindNode(context.Span, getInnermostNodeForTie: true); - if (nodeToFix is MethodDeclarationSyntax || nodeToFix is PropertyDeclarationSyntax) + if (nodeToFix is MethodDeclarationSyntax or PropertyDeclarationSyntax) { var title = "Add static modifier"; var codeAction = CodeAction.Create( diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/MarkAttributesWithAttributeUsageAttributeFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/MarkAttributesWithAttributeUsageAttributeFixer.cs index 1b65d702f..0e5d8263f 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/MarkAttributesWithAttributeUsageAttributeFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/MarkAttributesWithAttributeUsageAttributeFixer.cs @@ -1,8 +1,5 @@ -using System; using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/MethodOverridesShouldNotChangeParameterDefaultsFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/MethodOverridesShouldNotChangeParameterDefaultsFixer.cs index 9f4cacafc..e25fc5d91 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/MethodOverridesShouldNotChangeParameterDefaultsFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/MethodOverridesShouldNotChangeParameterDefaultsFixer.cs @@ -1,7 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/NamedParameterFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/NamedParameterFixer.cs index 7f5f3d965..62cb60762 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/NamedParameterFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/NamedParameterFixer.cs @@ -1,7 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/NotPatternShouldBeParenthesizedCodeFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/NotPatternShouldBeParenthesizedCodeFixer.cs index 7723ec7d8..3e7befa46 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/NotPatternShouldBeParenthesizedCodeFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/NotPatternShouldBeParenthesizedCodeFixer.cs @@ -1,8 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeGuidCreationFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeGuidCreationFixer.cs index b415af910..c021b4926 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeGuidCreationFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeGuidCreationFixer.cs @@ -1,9 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Linq; -using System.Net.NetworkInformation; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeLinqUsageFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeLinqUsageFixer.cs index f120f2ad0..69f5ba2a8 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeLinqUsageFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeLinqUsageFixer.cs @@ -1,10 +1,5 @@ -using System; using System.Collections.Immutable; using System.Composition; -using System.Globalization; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeStartsWithFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeStartsWithFixer.cs index c45b71c81..519118127 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeStartsWithFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeStartsWithFixer.cs @@ -1,8 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeStringBuilderUsageFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeStringBuilderUsageFixer.cs index 2802ed8eb..82f170a6c 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeStringBuilderUsageFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeStringBuilderUsageFixer.cs @@ -1,11 +1,5 @@ -using System; using System.Collections.Immutable; using System.Composition; -using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; @@ -260,7 +254,7 @@ private static async Task ReplaceStringFormatWithAppendFormat(Document var stringFormatOperation = (IInvocationOperation)operation.Arguments[0].Value; var newExpression = generator.InvocationExpression(generator.MemberAccessExpression(operation.GetChildOperations().First().Syntax, "AppendFormat"), - stringFormatOperation.Arguments.Select(a => a.Syntax).ToArray()); + [.. stringFormatOperation.Arguments.Select(a => a.Syntax)]); if (isAppendLine) { @@ -285,7 +279,7 @@ private static async Task ReplaceStringJoinWithAppendJoin(Document doc var stringFormatOperation = (IInvocationOperation)operation.Arguments[0].Value; var newExpression = generator.InvocationExpression(generator.MemberAccessExpression(operation.GetChildOperations().First().Syntax, "AppendJoin"), - stringFormatOperation.Arguments.Select(a => a.Syntax).ToArray()); + [.. stringFormatOperation.Arguments.Select(a => a.Syntax)]); if (isAppendLine) { diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/ParameterAttributeForRazorComponentFixer.AddParameterAttribute.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/ParameterAttributeForRazorComponentFixer.AddParameterAttribute.cs index 8f9f2083f..cada87ffd 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/ParameterAttributeForRazorComponentFixer.AddParameterAttribute.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/ParameterAttributeForRazorComponentFixer.AddParameterAttribute.cs @@ -1,7 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/PreserveParamsOnOverrideFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/PreserveParamsOnOverrideFixer.cs index 38827a6cc..68215a8bd 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/PreserveParamsOnOverrideFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/PreserveParamsOnOverrideFixer.cs @@ -1,7 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/RemoveEmptyStatementFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/RemoveEmptyStatementFixer.cs index 210b68ec9..0ab4a6538 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/RemoveEmptyStatementFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/RemoveEmptyStatementFixer.cs @@ -1,7 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/RemoveUselessToStringFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/RemoveUselessToStringFixer.cs index e90ca3b40..b93d475ef 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/RemoveUselessToStringFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/RemoveUselessToStringFixer.cs @@ -1,7 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/ReplaceEnumToStringWithNameofFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/ReplaceEnumToStringWithNameofFixer.cs index e6a11beaa..8000eb42f 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/ReplaceEnumToStringWithNameofFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/ReplaceEnumToStringWithNameofFixer.cs @@ -1,7 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/ReturnTaskFromResultInsteadOfReturningNullFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/ReturnTaskFromResultInsteadOfReturningNullFixer.cs index c65422c63..6fd24d1ac 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/ReturnTaskFromResultInsteadOfReturningNullFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/ReturnTaskFromResultInsteadOfReturningNullFixer.cs @@ -1,7 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/SimplifyCallerArgumentExpressionFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/SimplifyCallerArgumentExpressionFixer.cs index ae4e15a0e..36b349316 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/SimplifyCallerArgumentExpressionFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/SimplifyCallerArgumentExpressionFixer.cs @@ -1,7 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; @@ -24,7 +22,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); var nodeToFix = root?.FindNode(context.Span, getInnermostNodeForTie: false); - if (nodeToFix is null || nodeToFix is not ArgumentSyntax) + if (nodeToFix is null or not ArgumentSyntax) return; var title = "Remove argument"; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/StringShouldNotContainsNonDeterministicEndOfLineFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/StringShouldNotContainsNonDeterministicEndOfLineFixer.cs index 1d4449751..94bb91ea1 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/StringShouldNotContainsNonDeterministicEndOfLineFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/StringShouldNotContainsNonDeterministicEndOfLineFixer.cs @@ -1,8 +1,5 @@ -using System; using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/UseAnOverloadThatHasCancellationTokenFixer_Argument.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/UseAnOverloadThatHasCancellationTokenFixer_Argument.cs index 1212d8caf..75ad0f949 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/UseAnOverloadThatHasCancellationTokenFixer_Argument.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/UseAnOverloadThatHasCancellationTokenFixer_Argument.cs @@ -1,8 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Globalization; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/UseAnOverloadThatHasCancellationTokenFixer_AwaitForEach.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/UseAnOverloadThatHasCancellationTokenFixer_AwaitForEach.cs index 47159c904..e086f4f72 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/UseAnOverloadThatHasCancellationTokenFixer_AwaitForEach.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/UseAnOverloadThatHasCancellationTokenFixer_AwaitForEach.cs @@ -1,7 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/UseAnOverloadThatHasTimeProviderFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/UseAnOverloadThatHasTimeProviderFixer.cs index 1ffc923d5..a3612b84a 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/UseAnOverloadThatHasTimeProviderFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/UseAnOverloadThatHasTimeProviderFixer.cs @@ -1,15 +1,11 @@ using System.Collections.Immutable; using System.Composition; -using System.Globalization; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; -using Microsoft.CodeAnalysis.Operations; namespace Meziantou.Analyzer.Rules; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/UseArrayEmptyFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/UseArrayEmptyFixer.cs index 68168ee3f..f2611affc 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/UseArrayEmptyFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/UseArrayEmptyFixer.cs @@ -1,7 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/UseConfigureAwaitFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/UseConfigureAwaitFixer.cs index 1008d7c2b..46ca11b9b 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/UseConfigureAwaitFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/UseConfigureAwaitFixer.cs @@ -1,8 +1,5 @@ using System.Collections.Immutable; using System.Composition; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/UseDateTimeUnixEpochFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/UseDateTimeUnixEpochFixer.cs index 416e70d70..99729b805 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/UseDateTimeUnixEpochFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/UseDateTimeUnixEpochFixer.cs @@ -1,7 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/UseEventArgsEmptyFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/UseEventArgsEmptyFixer.cs index b2b152ba6..a9d595756 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/UseEventArgsEmptyFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/UseEventArgsEmptyFixer.cs @@ -1,8 +1,5 @@ -using System; using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/UseGuidEmptyFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/UseGuidEmptyFixer.cs index 218771a22..e960c5780 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/UseGuidEmptyFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/UseGuidEmptyFixer.cs @@ -1,8 +1,5 @@ -using System; using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/UseIsPatternInsteadOfSequenceEqualFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/UseIsPatternInsteadOfSequenceEqualFixer.cs index d18e6bd0f..1dcf08310 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/UseIsPatternInsteadOfSequenceEqualFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/UseIsPatternInsteadOfSequenceEqualFixer.cs @@ -1,12 +1,10 @@ -using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis; using System.Composition; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Operations; using System.Collections.Immutable; -using System.Threading.Tasks; -using System.Threading; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/UseJSRuntimeInvokeVoidAsyncWhenReturnValueIsNotUsedFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/UseJSRuntimeInvokeVoidAsyncWhenReturnValueIsNotUsedFixer.cs index 650ae32fa..17a246222 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/UseJSRuntimeInvokeVoidAsyncWhenReturnValueIsNotUsedFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/UseJSRuntimeInvokeVoidAsyncWhenReturnValueIsNotUsedFixer.cs @@ -1,7 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/UseLangwordInXmlCommentFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/UseLangwordInXmlCommentFixer.cs index 496506a19..622d09a0e 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/UseLangwordInXmlCommentFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/UseLangwordInXmlCommentFixer.cs @@ -1,7 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/UsePatternMatchingForEqualityComparisonsFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/UsePatternMatchingForEqualityComparisonsFixer.cs index fdda62f39..f64fc7443 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/UsePatternMatchingForEqualityComparisonsFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/UsePatternMatchingForEqualityComparisonsFixer.cs @@ -1,7 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/UsePatternMatchingInsteadOfHasvalueFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/UsePatternMatchingInsteadOfHasvalueFixer.cs index c0b9a9cb7..086d58618 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/UsePatternMatchingInsteadOfHasvalueFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/UsePatternMatchingInsteadOfHasvalueFixer.cs @@ -1,4 +1,4 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; @@ -71,7 +71,7 @@ private static (SyntaxNode Node, bool Negate) GetNodeToReplace(IOperation operat } } - if (operation.Parent is IIsPatternOperation { Pattern: IConstantPatternOperation { Value: ILiteralOperation { ConstantValue: { Value: bool value } } } }) + if (operation.Parent is IIsPatternOperation { Pattern: IConstantPatternOperation { Value: ILiteralOperation { ConstantValue.Value: bool value } } }) { if (value) { diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/UseRegexSourceGeneratorFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/UseRegexSourceGeneratorFixer.cs index d6e93ba25..8a239a48a 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/UseRegexSourceGeneratorFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/UseRegexSourceGeneratorFixer.cs @@ -1,10 +1,5 @@ -using System; using System.Collections.Immutable; using System.Composition; -using System.Globalization; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; @@ -117,13 +112,13 @@ private static async Task ConvertToSourceGenerator(Document document, TryParseInt32(properties, UseRegexSourceGeneratorAnalyzerCommon.RegexOptionsIndexName), TryParseInt32(properties, UseRegexSourceGeneratorAnalyzerCommon.RegexTimeoutIndexName), }; - foreach (var index in indices.Where(value => value is not null).OrderByDescending(value => value)) + foreach (var index in indices.Where(value => value is not null).OrderDescending()) { arguments = arguments.RemoveAt(index.GetValueOrDefault()); } var createRegexMethod = generator.InvocationExpression(generator.IdentifierName(methodName)); - var method = generator.InvocationExpression(generator.MemberAccessExpression(createRegexMethod, invocationOperation.TargetMethod.Name), arguments.Select(arg => arg.Syntax).ToArray()); + var method = generator.InvocationExpression(generator.MemberAccessExpression(createRegexMethod, invocationOperation.TargetMethod.Name), [.. arguments.Select(arg => arg.Syntax)]); newTypeDeclaration = newTypeDeclaration.ReplaceNode(nodeToFix, method); } diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/UseStringComparerFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/UseStringComparerFixer.cs index e101c81e0..692bcc988 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/UseStringComparerFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/UseStringComparerFixer.cs @@ -1,8 +1,5 @@ -using System; using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/UseStringComparisonFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/UseStringComparisonFixer.cs index d16eb0c12..1e8a3ef22 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/UseStringComparisonFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/UseStringComparisonFixer.cs @@ -1,8 +1,5 @@ -using System; using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/UseStringCreateInsteadOfFormattableStringFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/UseStringCreateInsteadOfFormattableStringFixer.cs index 1d1803793..6a3415a88 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/UseStringCreateInsteadOfFormattableStringFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/UseStringCreateInsteadOfFormattableStringFixer.cs @@ -1,7 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/UseStringEqualsFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/UseStringEqualsFixer.cs index ea8e163af..0e6baa656 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/UseStringEqualsFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/UseStringEqualsFixer.cs @@ -1,8 +1,5 @@ -using System; using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/UseStructLayoutAttributeFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/UseStructLayoutAttributeFixer.cs index 9cc33dcd1..4cf70f633 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/UseStructLayoutAttributeFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/UseStructLayoutAttributeFixer.cs @@ -1,8 +1,6 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Composition; using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/ValidateArgumentsCorrectlyFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/ValidateArgumentsCorrectlyFixer.cs index 58f24c40f..356cc333f 100755 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/ValidateArgumentsCorrectlyFixer.cs +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/ValidateArgumentsCorrectlyFixer.cs @@ -1,10 +1,5 @@ -using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; -using System.Globalization; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Meziantou.Analyzer/Configurations/AnalyzerOptionsExtensions.cs b/src/Meziantou.Analyzer/Configurations/AnalyzerOptionsExtensions.cs index 587cf09b9..62a783b61 100644 --- a/src/Meziantou.Analyzer/Configurations/AnalyzerOptionsExtensions.cs +++ b/src/Meziantou.Analyzer/Configurations/AnalyzerOptionsExtensions.cs @@ -1,6 +1,3 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Meziantou.Analyzer/Internals/AwaitableTypes.cs b/src/Meziantou.Analyzer/Internals/AwaitableTypes.cs index 4367fc391..e84d58931 100644 --- a/src/Meziantou.Analyzer/Internals/AwaitableTypes.cs +++ b/src/Meziantou.Analyzer/Internals/AwaitableTypes.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; -using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Operations; diff --git a/src/Meziantou.Analyzer/Internals/ConcurrentHashSet.cs b/src/Meziantou.Analyzer/Internals/ConcurrentHashSet.cs index 2fec8bc50..04ffa7ae3 100644 --- a/src/Meziantou.Analyzer/Internals/ConcurrentHashSet.cs +++ b/src/Meziantou.Analyzer/Internals/ConcurrentHashSet.cs @@ -1,6 +1,5 @@ -using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.Collections; -using System.Collections.Generic; namespace Meziantou.Analyzer.Internals; internal sealed class ConcurrentHashSet : ICollection, IReadOnlyCollection diff --git a/src/Meziantou.Analyzer/Internals/ContextExtensions.cs b/src/Meziantou.Analyzer/Internals/ContextExtensions.cs index 63a6353fc..008d45bbf 100755 --- a/src/Meziantou.Analyzer/Internals/ContextExtensions.cs +++ b/src/Meziantou.Analyzer/Internals/ContextExtensions.cs @@ -1,8 +1,5 @@ -using System; using System.Collections.Immutable; using System.Data; -using System.Linq; -using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Operations; diff --git a/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs b/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs index c0c04845b..8385ee765 100755 --- a/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs +++ b/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs @@ -1,7 +1,4 @@ -using System; -using Meziantou.Analyzer.Rules; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Meziantou.Analyzer.Internals; diff --git a/src/Meziantou.Analyzer/Internals/CultureSensitiveOptions.cs b/src/Meziantou.Analyzer/Internals/CultureSensitiveOptions.cs index 299820934..23775661e 100644 --- a/src/Meziantou.Analyzer/Internals/CultureSensitiveOptions.cs +++ b/src/Meziantou.Analyzer/Internals/CultureSensitiveOptions.cs @@ -1,5 +1,3 @@ -using System; - namespace Meziantou.Analyzer.Internals; [Flags] diff --git a/src/Meziantou.Analyzer/Internals/DiagnosticFieldReportOptions.cs b/src/Meziantou.Analyzer/Internals/DiagnosticFieldReportOptions.cs index 5d63e67b3..137b10914 100644 --- a/src/Meziantou.Analyzer/Internals/DiagnosticFieldReportOptions.cs +++ b/src/Meziantou.Analyzer/Internals/DiagnosticFieldReportOptions.cs @@ -1,5 +1,3 @@ -using System; - namespace Meziantou.Analyzer.Internals; [Flags] diff --git a/src/Meziantou.Analyzer/Internals/DiagnosticInvocationReportOptions.cs b/src/Meziantou.Analyzer/Internals/DiagnosticInvocationReportOptions.cs index 6aa0e8d2e..c0305c169 100644 --- a/src/Meziantou.Analyzer/Internals/DiagnosticInvocationReportOptions.cs +++ b/src/Meziantou.Analyzer/Internals/DiagnosticInvocationReportOptions.cs @@ -1,5 +1,3 @@ -using System; - namespace Meziantou.Analyzer.Internals; [Flags] diff --git a/src/Meziantou.Analyzer/Internals/DiagnosticMethodReportOptions.cs b/src/Meziantou.Analyzer/Internals/DiagnosticMethodReportOptions.cs index 71c283848..617c53a81 100644 --- a/src/Meziantou.Analyzer/Internals/DiagnosticMethodReportOptions.cs +++ b/src/Meziantou.Analyzer/Internals/DiagnosticMethodReportOptions.cs @@ -1,5 +1,3 @@ -using System; - namespace Meziantou.Analyzer.Internals; [Flags] diff --git a/src/Meziantou.Analyzer/Internals/DiagnosticParameterReportOptions.cs b/src/Meziantou.Analyzer/Internals/DiagnosticParameterReportOptions.cs index 8d05bac6b..d4303c0b0 100644 --- a/src/Meziantou.Analyzer/Internals/DiagnosticParameterReportOptions.cs +++ b/src/Meziantou.Analyzer/Internals/DiagnosticParameterReportOptions.cs @@ -1,5 +1,3 @@ -using System; - namespace Meziantou.Analyzer.Internals; [Flags] diff --git a/src/Meziantou.Analyzer/Internals/DiagnosticPropertyReportOptions.cs b/src/Meziantou.Analyzer/Internals/DiagnosticPropertyReportOptions.cs index 135b05bf8..5769c25aa 100644 --- a/src/Meziantou.Analyzer/Internals/DiagnosticPropertyReportOptions.cs +++ b/src/Meziantou.Analyzer/Internals/DiagnosticPropertyReportOptions.cs @@ -1,5 +1,3 @@ -using System; - namespace Meziantou.Analyzer.Internals; [Flags] diff --git a/src/Meziantou.Analyzer/Internals/DiagnosticReporter.cs b/src/Meziantou.Analyzer/Internals/DiagnosticReporter.cs index c9f275f9a..ab2fb63fe 100644 --- a/src/Meziantou.Analyzer/Internals/DiagnosticReporter.cs +++ b/src/Meziantou.Analyzer/Internals/DiagnosticReporter.cs @@ -1,5 +1,3 @@ -using System; -using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Meziantou.Analyzer/Internals/EnumerableExtensions.cs b/src/Meziantou.Analyzer/Internals/EnumerableExtensions.cs index c9675124b..90c9d4689 100644 --- a/src/Meziantou.Analyzer/Internals/EnumerableExtensions.cs +++ b/src/Meziantou.Analyzer/Internals/EnumerableExtensions.cs @@ -1,7 +1,3 @@ -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; - namespace Meziantou.Analyzer.Internals; internal static class EnumerableExtensions diff --git a/src/Meziantou.Analyzer/Internals/HashSetExtensions.cs b/src/Meziantou.Analyzer/Internals/HashSetExtensions.cs index 9c0bfb38d..abe543055 100644 --- a/src/Meziantou.Analyzer/Internals/HashSetExtensions.cs +++ b/src/Meziantou.Analyzer/Internals/HashSetExtensions.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace Meziantou.Analyzer.Internals; internal static class HashSetExtensions { diff --git a/src/Meziantou.Analyzer/Internals/ListExtensions.cs b/src/Meziantou.Analyzer/Internals/ListExtensions.cs index bf5425f5a..3672a9087 100644 --- a/src/Meziantou.Analyzer/Internals/ListExtensions.cs +++ b/src/Meziantou.Analyzer/Internals/ListExtensions.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace Meziantou.Analyzer.Internals; internal static class ListExtensions diff --git a/src/Meziantou.Analyzer/Internals/LocationExtensions.cs b/src/Meziantou.Analyzer/Internals/LocationExtensions.cs index 34a01931b..5daea14f8 100644 --- a/src/Meziantou.Analyzer/Internals/LocationExtensions.cs +++ b/src/Meziantou.Analyzer/Internals/LocationExtensions.cs @@ -1,4 +1,3 @@ -using System.Threading; using Microsoft.CodeAnalysis; namespace Meziantou.Analyzer.Internals; diff --git a/src/Meziantou.Analyzer/Internals/MethodSymbolExtensions.cs b/src/Meziantou.Analyzer/Internals/MethodSymbolExtensions.cs index 824476c75..e39e60bdc 100755 --- a/src/Meziantou.Analyzer/Internals/MethodSymbolExtensions.cs +++ b/src/Meziantou.Analyzer/Internals/MethodSymbolExtensions.cs @@ -1,4 +1,3 @@ -using System.Linq; using Microsoft.CodeAnalysis; namespace Meziantou.Analyzer.Internals; diff --git a/src/Meziantou.Analyzer/Internals/ObjectPool.cs b/src/Meziantou.Analyzer/Internals/ObjectPool.cs index e00cc1170..f9670c314 100644 --- a/src/Meziantou.Analyzer/Internals/ObjectPool.cs +++ b/src/Meziantou.Analyzer/Internals/ObjectPool.cs @@ -1,9 +1,6 @@ #pragma warning disable MA0048 // File name must match type name #pragma warning disable RS1035 // Do not use APIs banned for analyzers using System.Collections.Concurrent; -using System.Threading; -using System; -using System.Text; namespace Meziantou.Analyzer.Internals; @@ -128,7 +125,7 @@ public DefaultObjectPool(IPooledObjectPolicy policy, int maximumRetained) public override T Get() { var item = FastItem; - if (item == null || Interlocked.CompareExchange(ref FastItem, null, item) != item) + if (item == null || Interlocked.CompareExchange(ref FastItem, value: null, item) != item) { if (Items.TryDequeue(out item)) { diff --git a/src/Meziantou.Analyzer/Internals/OperationExtensions.cs b/src/Meziantou.Analyzer/Internals/OperationExtensions.cs index b877367c8..4e0174886 100644 --- a/src/Meziantou.Analyzer/Internals/OperationExtensions.cs +++ b/src/Meziantou.Analyzer/Internals/OperationExtensions.cs @@ -1,7 +1,3 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/src/Meziantou.Analyzer/Internals/OverloadFinder.cs b/src/Meziantou.Analyzer/Internals/OverloadFinder.cs index a89847cbd..a1ca6eeb8 100644 --- a/src/Meziantou.Analyzer/Internals/OverloadFinder.cs +++ b/src/Meziantou.Analyzer/Internals/OverloadFinder.cs @@ -1,5 +1,4 @@ using System.Collections.Immutable; -using System.Linq; using Microsoft.CodeAnalysis; namespace Meziantou.Analyzer.Internals; @@ -64,7 +63,7 @@ public bool HasOverloadWithAdditionalParameterOfType( if (additionalParameterTypes is null) return null; - additionalParameterTypes = additionalParameterTypes.Where(type => type is not null).ToArray(); + additionalParameterTypes = [.. additionalParameterTypes.Where(type => type is not null)]; if (additionalParameterTypes.Length == 0) return null; diff --git a/src/Meziantou.Analyzer/Internals/StringExtensions.cs b/src/Meziantou.Analyzer/Internals/StringExtensions.cs index 1b3a2b5fa..6ad4921a6 100644 --- a/src/Meziantou.Analyzer/Internals/StringExtensions.cs +++ b/src/Meziantou.Analyzer/Internals/StringExtensions.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; namespace Meziantou.Analyzer.Internals; diff --git a/src/Meziantou.Analyzer/Internals/SuppressorHelpers.cs b/src/Meziantou.Analyzer/Internals/SuppressorHelpers.cs index e8c94012f..c84d26a98 100644 --- a/src/Meziantou.Analyzer/Internals/SuppressorHelpers.cs +++ b/src/Meziantou.Analyzer/Internals/SuppressorHelpers.cs @@ -1,4 +1,3 @@ -using System.Threading; using Microsoft.CodeAnalysis; namespace Meziantou.Analyzer.Internals; diff --git a/src/Meziantou.Analyzer/Internals/SymbolExtensions.cs b/src/Meziantou.Analyzer/Internals/SymbolExtensions.cs index 4452256d4..acaf0eb44 100644 --- a/src/Meziantou.Analyzer/Internals/SymbolExtensions.cs +++ b/src/Meziantou.Analyzer/Internals/SymbolExtensions.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Threading; using Microsoft.CodeAnalysis; namespace Meziantou.Analyzer.Internals; @@ -20,9 +17,7 @@ public static bool IsVisibleOutsideOfAssembly([NotNullWhen(true)] this ISymbol? if (symbol is null) return false; - if (symbol.DeclaredAccessibility != Accessibility.Public && - symbol.DeclaredAccessibility != Accessibility.Protected && - symbol.DeclaredAccessibility != Accessibility.ProtectedOrInternal) + if (symbol.DeclaredAccessibility is not Accessibility.Public and not Accessibility.Protected and not Accessibility.ProtectedOrInternal) { return false; } diff --git a/src/Meziantou.Analyzer/Internals/SyntaxNodeExtensions.cs b/src/Meziantou.Analyzer/Internals/SyntaxNodeExtensions.cs index f4c37cc7c..5018b7c03 100644 --- a/src/Meziantou.Analyzer/Internals/SyntaxNodeExtensions.cs +++ b/src/Meziantou.Analyzer/Internals/SyntaxNodeExtensions.cs @@ -1,5 +1,3 @@ -using System; -using System.Linq; using Microsoft.CodeAnalysis; namespace Meziantou.Analyzer.Internals; diff --git a/src/Meziantou.Analyzer/Internals/SyntaxTokenListExtensions.cs b/src/Meziantou.Analyzer/Internals/SyntaxTokenListExtensions.cs index 486dcbf66..e3b366030 100644 --- a/src/Meziantou.Analyzer/Internals/SyntaxTokenListExtensions.cs +++ b/src/Meziantou.Analyzer/Internals/SyntaxTokenListExtensions.cs @@ -1,4 +1,3 @@ -using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; diff --git a/src/Meziantou.Analyzer/Internals/TimeSpanOperation.cs b/src/Meziantou.Analyzer/Internals/TimeSpanOperation.cs index 2c0400917..8b424cd89 100644 --- a/src/Meziantou.Analyzer/Internals/TimeSpanOperation.cs +++ b/src/Meziantou.Analyzer/Internals/TimeSpanOperation.cs @@ -1,4 +1,3 @@ -using System; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Operations; diff --git a/src/Meziantou.Analyzer/Internals/TypeSymbolExtensions.cs b/src/Meziantou.Analyzer/Internals/TypeSymbolExtensions.cs index 7e26287d8..98ca2e8d1 100755 --- a/src/Meziantou.Analyzer/Internals/TypeSymbolExtensions.cs +++ b/src/Meziantou.Analyzer/Internals/TypeSymbolExtensions.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; using Microsoft.CodeAnalysis; namespace Meziantou.Analyzer.Internals; diff --git a/src/Meziantou.Analyzer/Meziantou.Analyzer.csproj b/src/Meziantou.Analyzer/Meziantou.Analyzer.csproj index 57b7855b8..6df946162 100644 --- a/src/Meziantou.Analyzer/Meziantou.Analyzer.csproj +++ b/src/Meziantou.Analyzer/Meziantou.Analyzer.csproj @@ -1,6 +1,6 @@  - net9.0;netstandard2.0 + net10.0;netstandard2.0 false 1.0.1 diff --git a/src/Meziantou.Analyzer/RuleIdentifiers.cs b/src/Meziantou.Analyzer/RuleIdentifiers.cs index 0afe37099..64a7420b3 100755 --- a/src/Meziantou.Analyzer/RuleIdentifiers.cs +++ b/src/Meziantou.Analyzer/RuleIdentifiers.cs @@ -1,5 +1,3 @@ -using System.Globalization; - namespace Meziantou.Analyzer; internal static class RuleIdentifiers diff --git a/src/Meziantou.Analyzer/Rules/AddOverloadWithSpanOrMemoryAnalyzer.cs b/src/Meziantou.Analyzer/Rules/AddOverloadWithSpanOrMemoryAnalyzer.cs index cfe6041fb..b99a1ded4 100644 --- a/src/Meziantou.Analyzer/Rules/AddOverloadWithSpanOrMemoryAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/AddOverloadWithSpanOrMemoryAnalyzer.cs @@ -1,5 +1,4 @@ -using System.Collections.Immutable; -using System.Linq; +using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Meziantou.Analyzer/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzer.cs b/src/Meziantou.Analyzer/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzer.cs index c7e3e5978..ce48df78c 100644 --- a/src/Meziantou.Analyzer/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzer.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; -using System.Threading; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/src/Meziantou.Analyzer/Rules/AvoidClosureWhenUsingConcurrentDictionaryAnalyzer.cs b/src/Meziantou.Analyzer/Rules/AvoidClosureWhenUsingConcurrentDictionaryAnalyzer.cs index a4e7e39e2..76c458135 100644 --- a/src/Meziantou.Analyzer/Rules/AvoidClosureWhenUsingConcurrentDictionaryAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/AvoidClosureWhenUsingConcurrentDictionaryAnalyzer.cs @@ -1,7 +1,4 @@ -using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; -using System.Linq; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/src/Meziantou.Analyzer/Rules/AvoidComparisonWithBoolConstantAnalyzer.cs b/src/Meziantou.Analyzer/Rules/AvoidComparisonWithBoolConstantAnalyzer.cs index e796aff50..1a63feb50 100644 --- a/src/Meziantou.Analyzer/Rules/AvoidComparisonWithBoolConstantAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/AvoidComparisonWithBoolConstantAnalyzer.cs @@ -1,5 +1,4 @@ -using System.Collections.Immutable; -using System.Globalization; +using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/src/Meziantou.Analyzer/Rules/AvoidUsingRedundantElseAnalyzer.cs b/src/Meziantou.Analyzer/Rules/AvoidUsingRedundantElseAnalyzer.cs index aa8978e5a..12a631959 100644 --- a/src/Meziantou.Analyzer/Rules/AvoidUsingRedundantElseAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/AvoidUsingRedundantElseAnalyzer.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; diff --git a/src/Meziantou.Analyzer/Rules/AvoidUsingRedundantElseAnalyzerCommon.cs b/src/Meziantou.Analyzer/Rules/AvoidUsingRedundantElseAnalyzerCommon.cs index 90a6f036d..62cd72d74 100644 --- a/src/Meziantou.Analyzer/Rules/AvoidUsingRedundantElseAnalyzerCommon.cs +++ b/src/Meziantou.Analyzer/Rules/AvoidUsingRedundantElseAnalyzerCommon.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/src/Meziantou.Analyzer/Rules/AwaitTaskBeforeDisposingResourcesAnalyzer.cs b/src/Meziantou.Analyzer/Rules/AwaitTaskBeforeDisposingResourcesAnalyzer.cs index cf336ae25..35b612378 100644 --- a/src/Meziantou.Analyzer/Rules/AwaitTaskBeforeDisposingResourcesAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/AwaitTaskBeforeDisposingResourcesAnalyzer.cs @@ -1,6 +1,4 @@ -using System.Collections.Immutable; -using System.Linq; -using System.Threading.Tasks; +using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Meziantou.Analyzer/Rules/ClassMustBeSealedAnalyzer.cs b/src/Meziantou.Analyzer/Rules/ClassMustBeSealedAnalyzer.cs index b225dadad..db3c9aded 100644 --- a/src/Meziantou.Analyzer/Rules/ClassMustBeSealedAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/ClassMustBeSealedAnalyzer.cs @@ -1,7 +1,4 @@ -using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; -using System.Threading; using Meziantou.Analyzer.Configurations; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; @@ -113,7 +110,7 @@ private bool IsPotentialSealed(AnalyzerOptions options, INamedTypeSymbol symbol, if (symbol.GetMembers().Any(member => member.IsVirtual) && !SealedClassWithVirtualMember(options, symbol)) return false; - var canBeInheritedOutsideOfAssembly = symbol.IsVisibleOutsideOfAssembly() && symbol.GetMembers().OfType().Where(member => member.MethodKind is MethodKind.Constructor).Any(member => member.IsVisibleOutsideOfAssembly()); + var canBeInheritedOutsideOfAssembly = symbol.IsVisibleOutsideOfAssembly() && symbol.GetMembers().OfType().Any(member => member.MethodKind is MethodKind.Constructor && member.IsVisibleOutsideOfAssembly()); if (canBeInheritedOutsideOfAssembly && !PublicClassShouldBeSealed(options, symbol)) return false; diff --git a/src/Meziantou.Analyzer/Rules/ConstructorArgumentParametersShouldExistInConstructorsAnalyzer.cs b/src/Meziantou.Analyzer/Rules/ConstructorArgumentParametersShouldExistInConstructorsAnalyzer.cs index c2bd3d39d..7dfcff417 100644 --- a/src/Meziantou.Analyzer/Rules/ConstructorArgumentParametersShouldExistInConstructorsAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/ConstructorArgumentParametersShouldExistInConstructorsAnalyzer.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; diff --git a/src/Meziantou.Analyzer/Rules/DebuggerDisplayAttributeShouldContainValidExpressionsAnalyzer.cs b/src/Meziantou.Analyzer/Rules/DebuggerDisplayAttributeShouldContainValidExpressionsAnalyzer.cs index 5437e8209..d4611f279 100644 --- a/src/Meziantou.Analyzer/Rules/DebuggerDisplayAttributeShouldContainValidExpressionsAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/DebuggerDisplayAttributeShouldContainValidExpressionsAnalyzer.cs @@ -1,11 +1,5 @@ -using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Diagnostics.SymbolStore; -using System.Globalization; -using System.Linq; -using System.Linq.Expressions; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -209,7 +203,7 @@ private static bool IsValid(Compilation compilation, ISymbol rootSymbol, List.Result), StringComparison.Ordinal)) + if (string.Equals(operation.Property.Name, nameof(Task<>.Result), StringComparison.Ordinal)) { if (operation.Member.ContainingType.OriginalDefinition.IsEqualToAny(TaskOfTSymbol, ValueTaskOfTSymbol)) { diff --git a/src/Meziantou.Analyzer/Rules/DoNotUseEqualityComparerDefaultOfStringAnalyzer.cs b/src/Meziantou.Analyzer/Rules/DoNotUseEqualityComparerDefaultOfStringAnalyzer.cs index f6b659bae..c990a6ca5 100644 --- a/src/Meziantou.Analyzer/Rules/DoNotUseEqualityComparerDefaultOfStringAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/DoNotUseEqualityComparerDefaultOfStringAnalyzer.cs @@ -1,5 +1,3 @@ -using System; -using System.Collections.Generic; using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; @@ -34,7 +32,7 @@ public override void Initialize(AnalysisContext context) private static void Analyze(OperationAnalysisContext context) { var operation = (IPropertyReferenceOperation)context.Operation; - if (!string.Equals(operation.Member.Name, nameof(EqualityComparer.Default), StringComparison.Ordinal)) + if (!string.Equals(operation.Member.Name, nameof(EqualityComparer<>.Default), StringComparison.Ordinal)) return; var equalityComparerSymbol = context.Compilation.GetBestTypeByMetadataName("System.Collections.Generic.EqualityComparer`1"); diff --git a/src/Meziantou.Analyzer/Rules/DoNotUseEqualityOperatorsForSpanOfCharAnalyzer.cs b/src/Meziantou.Analyzer/Rules/DoNotUseEqualityOperatorsForSpanOfCharAnalyzer.cs index 14182096a..3918b8a5b 100644 --- a/src/Meziantou.Analyzer/Rules/DoNotUseEqualityOperatorsForSpanOfCharAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/DoNotUseEqualityOperatorsForSpanOfCharAnalyzer.cs @@ -1,4 +1,4 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; @@ -45,8 +45,7 @@ private sealed class AnalyzerContext(Compilation compilation, params INamedTypeS public void AnalyzeBinaryOperator(OperationAnalysisContext context) { var operation = (IBinaryOperation)context.Operation; - if (operation.OperatorKind == BinaryOperatorKind.Equals || - operation.OperatorKind == BinaryOperatorKind.NotEquals) + if (operation.OperatorKind is BinaryOperatorKind.Equals or BinaryOperatorKind.NotEquals) { if (!IsSpanOfString(operation.LeftOperand.Type) || !IsSpanOfString(operation.RightOperand.Type)) return; diff --git a/src/Meziantou.Analyzer/Rules/DoNotUseServerCertificateValidationCallbackAnalyzer.cs b/src/Meziantou.Analyzer/Rules/DoNotUseServerCertificateValidationCallbackAnalyzer.cs index 8872aff55..21e59824b 100644 --- a/src/Meziantou.Analyzer/Rules/DoNotUseServerCertificateValidationCallbackAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/DoNotUseServerCertificateValidationCallbackAnalyzer.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Meziantou.Analyzer/Rules/DoNotUseToStringIfObjectAnalyzer.cs b/src/Meziantou.Analyzer/Rules/DoNotUseToStringIfObjectAnalyzer.cs index 6cf2a2abe..08851be2e 100644 --- a/src/Meziantou.Analyzer/Rules/DoNotUseToStringIfObjectAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/DoNotUseToStringIfObjectAnalyzer.cs @@ -1,6 +1,5 @@ -using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.Collections.Immutable; -using System.Linq; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Meziantou.Analyzer/Rules/DoNotUseUnknownParameterForRazorComponentAnalyzer.cs b/src/Meziantou.Analyzer/Rules/DoNotUseUnknownParameterForRazorComponentAnalyzer.cs index 2627802d9..c176ed5f4 100644 --- a/src/Meziantou.Analyzer/Rules/DoNotUseUnknownParameterForRazorComponentAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/DoNotUseUnknownParameterForRazorComponentAnalyzer.cs @@ -1,8 +1,5 @@ -using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using Meziantou.Analyzer.Configurations; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; diff --git a/src/Meziantou.Analyzer/Rules/DontUseDangerousThreadingMethodsAnalyzer.cs b/src/Meziantou.Analyzer/Rules/DontUseDangerousThreadingMethodsAnalyzer.cs index 692f0ba53..94f591293 100644 --- a/src/Meziantou.Analyzer/Rules/DontUseDangerousThreadingMethodsAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/DontUseDangerousThreadingMethodsAnalyzer.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; diff --git a/src/Meziantou.Analyzer/Rules/DotNotUseNameFromBCLAnalyzer.cs b/src/Meziantou.Analyzer/Rules/DotNotUseNameFromBCLAnalyzer.cs index f68e1ca8f..2b447aece 100644 --- a/src/Meziantou.Analyzer/Rules/DotNotUseNameFromBCLAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/DotNotUseNameFromBCLAnalyzer.cs @@ -1,9 +1,5 @@ -using System; -using System.Collections.Generic; using System.Collections.Immutable; -using System.IO; using System.Text.RegularExpressions; -using System.Threading; using Meziantou.Analyzer.Configurations; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; diff --git a/src/Meziantou.Analyzer/Rules/EmbedCaughtExceptionAsInnerExceptionAnalyzer.cs b/src/Meziantou.Analyzer/Rules/EmbedCaughtExceptionAsInnerExceptionAnalyzer.cs index e4e5f71a3..dbd71a21f 100644 --- a/src/Meziantou.Analyzer/Rules/EmbedCaughtExceptionAsInnerExceptionAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/EmbedCaughtExceptionAsInnerExceptionAnalyzer.cs @@ -1,5 +1,4 @@ -using System.Collections.Immutable; -using System.Linq; +using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Meziantou.Analyzer/Rules/EqualityShouldBeCorrectlyImplementedAnalyzer.cs b/src/Meziantou.Analyzer/Rules/EqualityShouldBeCorrectlyImplementedAnalyzer.cs index 4ae180f8e..bc7cc5b37 100644 --- a/src/Meziantou.Analyzer/Rules/EqualityShouldBeCorrectlyImplementedAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/EqualityShouldBeCorrectlyImplementedAnalyzer.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Meziantou.Analyzer/Rules/EventsShouldHaveProperArgumentsAnalyzer.cs b/src/Meziantou.Analyzer/Rules/EventsShouldHaveProperArgumentsAnalyzer.cs index 9ad51062c..56b08eb5c 100644 --- a/src/Meziantou.Analyzer/Rules/EventsShouldHaveProperArgumentsAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/EventsShouldHaveProperArgumentsAnalyzer.cs @@ -1,6 +1,4 @@ -using System; using System.Collections.Immutable; -using System.Threading; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Meziantou.Analyzer/Rules/FileNameMustMatchTypeNameAnalyzer.cs b/src/Meziantou.Analyzer/Rules/FileNameMustMatchTypeNameAnalyzer.cs index a94bb667a..d4aff6895 100644 --- a/src/Meziantou.Analyzer/Rules/FileNameMustMatchTypeNameAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/FileNameMustMatchTypeNameAnalyzer.cs @@ -1,7 +1,4 @@ -using System; using System.Collections.Immutable; -using System.Globalization; -using System.Linq; using System.Text.RegularExpressions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; @@ -113,7 +110,7 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context) continue; // Type{T} - if (fileName.Equals((symbolName + '{' + string.Join(",", symbol.TypeParameters.Select(t => t.Name)) + '}').AsSpan(), StringComparison.OrdinalIgnoreCase)) + if (fileName.Equals((symbolName + '{' + string.Join(',', symbol.TypeParameters.Select(t => t.Name)) + '}').AsSpan(), StringComparison.OrdinalIgnoreCase)) continue; } diff --git a/src/Meziantou.Analyzer/Rules/FixToDoAnalyzer.cs b/src/Meziantou.Analyzer/Rules/FixToDoAnalyzer.cs index 8756ae248..623725c76 100644 --- a/src/Meziantou.Analyzer/Rules/FixToDoAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/FixToDoAnalyzer.cs @@ -1,6 +1,4 @@ -using System; using System.Collections.Immutable; -using System.Linq; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; diff --git a/src/Meziantou.Analyzer/Rules/LocalVariablesShouldNotHideSymbolsAnalyzer.cs b/src/Meziantou.Analyzer/Rules/LocalVariablesShouldNotHideSymbolsAnalyzer.cs index bf71d28d7..d35e926e3 100644 --- a/src/Meziantou.Analyzer/Rules/LocalVariablesShouldNotHideSymbolsAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/LocalVariablesShouldNotHideSymbolsAnalyzer.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; using System.Collections.Immutable; -using System.Threading; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; diff --git a/src/Meziantou.Analyzer/Rules/LoggerParameterTypeAnalyzer.cs b/src/Meziantou.Analyzer/Rules/LoggerParameterTypeAnalyzer.cs index 78194787b..887636734 100644 --- a/src/Meziantou.Analyzer/Rules/LoggerParameterTypeAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/LoggerParameterTypeAnalyzer.cs @@ -1,14 +1,8 @@ -using System; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Globalization; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; -using System.Diagnostics.CodeAnalysis; -using System.Linq; using Microsoft.CodeAnalysis.CSharp.Syntax; -using System.IO; using Microsoft.CodeAnalysis.Text; using Meziantou.Analyzer.Configurations; using Microsoft.CodeAnalysis.CSharp; @@ -213,7 +207,7 @@ LoggerConfigurationFile LoadConfiguration() if (attribute.ConstructorArguments is [{ Type.SpecialType: SpecialType.System_String, IsNull: false, Value: string name }, TypedConstant { Kind: TypedConstantKind.Array } types]) { - configuration[name] = types.Values.Select(v => v.Value as ITypeSymbol).WhereNotNull().ToArray(); + configuration[name] = [.. types.Values.Select(v => v.Value as ITypeSymbol).WhereNotNull()]; } } } @@ -335,7 +329,7 @@ public void AnalyzeInvocationDeclaration(OperationAnalysisContext context) return; formatExpression = arg.Value; - argumentTypes = operation.TargetMethod.TypeArguments.Select((arg, index) => ((ITypeSymbol?)arg, GetSyntaxNode(operation, index))).ToArray(); + argumentTypes = [.. operation.TargetMethod.TypeArguments.Select((arg, index) => ((ITypeSymbol?)arg, GetSyntaxNode(operation, index)))]; static SyntaxNode GetSyntaxNode(IOperation operation, int index) { @@ -364,7 +358,7 @@ static SyntaxNode GetSyntaxNode(IOperation operation, int index) { if (argument.ArgumentKind == ArgumentKind.ParamArray && argument.Value is IArrayCreationOperation arrayCreation && arrayCreation.Initializer is not null) { - argumentTypes = arrayCreation.Initializer.ElementValues.Select(v => (v.UnwrapImplicitConversionOperations().Type, v.Syntax)).ToArray(); + argumentTypes = [.. arrayCreation.Initializer.ElementValues.Select(v => (v.UnwrapImplicitConversionOperations().Type, v.Syntax))]; } } } @@ -384,13 +378,13 @@ static SyntaxNode GetSyntaxNode(IOperation operation, int index) if (argument.ArgumentKind == ArgumentKind.ParamArray && argument.Value is IArrayCreationOperation arrayCreation && arrayCreation.Initializer is not null) { - argumentTypes = arrayCreation.Initializer.ElementValues.Select(v => (v.UnwrapImplicitConversionOperations().Type, v.Syntax)).ToArray(); + argumentTypes = [.. arrayCreation.Initializer.ElementValues.Select(v => (v.UnwrapImplicitConversionOperations().Type, v.Syntax))]; } } if (operation.Arguments.Length >= templateIndex && argumentTypes is null) { - argumentTypes = operation.Arguments.Skip(templateIndex + 1).Select(v => (v.Value.UnwrapImplicitConversionOperations().Type, v.Syntax)).ToArray(); + argumentTypes = [.. operation.Arguments.Skip(templateIndex + 1).Select(v => (v.Value.UnwrapImplicitConversionOperations().Type, v.Syntax))]; } } diff --git a/src/Meziantou.Analyzer/Rules/MakeClassStaticAnalyzer.cs b/src/Meziantou.Analyzer/Rules/MakeClassStaticAnalyzer.cs index f9ac25851..e7245ed14 100644 --- a/src/Meziantou.Analyzer/Rules/MakeClassStaticAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/MakeClassStaticAnalyzer.cs @@ -1,7 +1,4 @@ -using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; -using System.Threading; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Meziantou.Analyzer/Rules/MakeInterpolatedStringAnalyzer.cs b/src/Meziantou.Analyzer/Rules/MakeInterpolatedStringAnalyzer.cs index c0b0970d2..964722007 100644 --- a/src/Meziantou.Analyzer/Rules/MakeInterpolatedStringAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/MakeInterpolatedStringAnalyzer.cs @@ -1,11 +1,9 @@ -using System; using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Operations; namespace Meziantou.Analyzer.Rules; diff --git a/src/Meziantou.Analyzer/Rules/MakeMemberReadOnlyAnalyzer.cs b/src/Meziantou.Analyzer/Rules/MakeMemberReadOnlyAnalyzer.cs index e8428ad9a..0ab470e45 100644 --- a/src/Meziantou.Analyzer/Rules/MakeMemberReadOnlyAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/MakeMemberReadOnlyAnalyzer.cs @@ -1,5 +1,4 @@ -using System.Collections.Immutable; -using System.Linq; +using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; diff --git a/src/Meziantou.Analyzer/Rules/MakeMethodStaticAnalyzer.cs b/src/Meziantou.Analyzer/Rules/MakeMethodStaticAnalyzer.cs index 012da045b..604807716 100644 --- a/src/Meziantou.Analyzer/Rules/MakeMethodStaticAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/MakeMethodStaticAnalyzer.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; diff --git a/src/Meziantou.Analyzer/Rules/MethodOverridesShouldNotChangeParameterDefaultsAnalyzer.cs b/src/Meziantou.Analyzer/Rules/MethodOverridesShouldNotChangeParameterDefaultsAnalyzer.cs index fc6b663a6..d5601a4ab 100644 --- a/src/Meziantou.Analyzer/Rules/MethodOverridesShouldNotChangeParameterDefaultsAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/MethodOverridesShouldNotChangeParameterDefaultsAnalyzer.cs @@ -1,6 +1,4 @@ -using System; using System.Collections.Immutable; -using System.Threading; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -99,6 +97,6 @@ private static string GetParameterDisplayValue(IParameterSymbol parameter) return "null"; } - return FormattableString.Invariant($"'{parameter.ExplicitDefaultValue}'"); + return string.Create(CultureInfo.InvariantCulture, $"'{parameter.ExplicitDefaultValue}'"); } } diff --git a/src/Meziantou.Analyzer/Rules/MethodShouldNotBeTooLongAnalyzer.cs b/src/Meziantou.Analyzer/Rules/MethodShouldNotBeTooLongAnalyzer.cs index d24fbe85a..9b30e3bd2 100644 --- a/src/Meziantou.Analyzer/Rules/MethodShouldNotBeTooLongAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/MethodShouldNotBeTooLongAnalyzer.cs @@ -1,6 +1,4 @@ -using System.Collections.Immutable; -using System.Globalization; -using System.Linq; +using System.Collections.Immutable; using Meziantou.Analyzer.Configurations; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; @@ -161,7 +159,7 @@ bool ShouldDescendIntoChildren(SyntaxNode node) static bool IsCountableStatement(StatementSyntax statement) { - if (statement is BlockSyntax || statement is LocalFunctionStatementSyntax) + if (statement is BlockSyntax or LocalFunctionStatementSyntax) return false; return true; diff --git a/src/Meziantou.Analyzer/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzer.cs b/src/Meziantou.Analyzer/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzer.cs index e574dcb1c..58b34cc81 100644 --- a/src/Meziantou.Analyzer/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzer.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; diff --git a/src/Meziantou.Analyzer/Rules/NamedParameterAnalyzer.cs b/src/Meziantou.Analyzer/Rules/NamedParameterAnalyzer.cs index 55096749b..9fcbbde2d 100644 --- a/src/Meziantou.Analyzer/Rules/NamedParameterAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/NamedParameterAnalyzer.cs @@ -1,11 +1,7 @@ -using System; using System.Collections.Immutable; -using System.Globalization; using System.Linq.Expressions; using System.Reflection; using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; using Meziantou.Analyzer.Configurations; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; @@ -36,29 +32,29 @@ public override void Initialize(AnalysisContext context) context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - context.RegisterCompilationStartAction(compilationContext => + context.RegisterCompilationStartAction(context => { - var callerMustUseNamedArgumentType = compilationContext.Compilation.GetBestTypeByMetadataName("Meziantou.Analyzer.Annotations.RequireNamedArgumentAttribute"); - - var objectType = compilationContext.Compilation.GetSpecialType(SpecialType.System_Object); - var taskTokenType = compilationContext.Compilation.GetBestTypeByMetadataName("System.Threading.Tasks.Task"); - var taskGenericTokenType = compilationContext.Compilation.GetBestTypeByMetadataName("System.Threading.Tasks.Task`1"); - var valueTaskTokenType = compilationContext.Compilation.GetBestTypeByMetadataName("System.Threading.Tasks.ValueTask"); - var valueTaskGenericTokenType = compilationContext.Compilation.GetBestTypeByMetadataName("System.Threading.Tasks.ValueTask`1"); - var taskCompletionSourceType = compilationContext.Compilation.GetBestTypeByMetadataName("System.Threading.Tasks.TaskCompletionSource`1"); - var methodBaseTokenType = compilationContext.Compilation.GetBestTypeByMetadataName("System.Reflection.MethodBase"); - var fieldInfoTokenType = compilationContext.Compilation.GetBestTypeByMetadataName("System.Reflection.FieldInfo"); - var propertyInfoTokenType = compilationContext.Compilation.GetBestTypeByMetadataName("System.Reflection.PropertyInfo"); - var msTestAssertTokenType = compilationContext.Compilation.GetBestTypeByMetadataName("Microsoft.VisualStudio.TestTools.UnitTesting.Assert"); - var nunitAssertTokenType = compilationContext.Compilation.GetBestTypeByMetadataName("NUnit.Framework.Assert"); - var xunitAssertTokenType = compilationContext.Compilation.GetBestTypeByMetadataName("Xunit.Assert"); - var keyValuePairTokenType = compilationContext.Compilation.GetBestTypeByMetadataName("System.Collection.Generic.KeyValuePair`2"); - var propertyBuilderType = compilationContext.Compilation.GetBestTypeByMetadataName("Microsoft.EntityFrameworkCore.Metadata.Builders.PropertyBuilder`1"); - var syntaxNodeType = compilationContext.Compilation.GetBestTypeByMetadataName("Microsoft.CodeAnalysis.SyntaxNode"); - var expressionType = compilationContext.Compilation.GetBestTypeByMetadataName("System.Linq.Expressions.Expression"); - var operationUtilities = new OperationUtilities(compilationContext.Compilation); - - compilationContext.RegisterSyntaxNodeAction(syntaxContext => + var callerMustUseNamedArgumentType = context.Compilation.GetBestTypeByMetadataName("Meziantou.Analyzer.Annotations.RequireNamedArgumentAttribute"); + + var objectType = context.Compilation.GetSpecialType(SpecialType.System_Object); + var taskTokenType = context.Compilation.GetBestTypeByMetadataName("System.Threading.Tasks.Task"); + var taskGenericTokenType = context.Compilation.GetBestTypeByMetadataName("System.Threading.Tasks.Task`1"); + var valueTaskTokenType = context.Compilation.GetBestTypeByMetadataName("System.Threading.Tasks.ValueTask"); + var valueTaskGenericTokenType = context.Compilation.GetBestTypeByMetadataName("System.Threading.Tasks.ValueTask`1"); + var taskCompletionSourceType = context.Compilation.GetBestTypeByMetadataName("System.Threading.Tasks.TaskCompletionSource`1"); + var methodBaseTokenType = context.Compilation.GetBestTypeByMetadataName("System.Reflection.MethodBase"); + var fieldInfoTokenType = context.Compilation.GetBestTypeByMetadataName("System.Reflection.FieldInfo"); + var propertyInfoTokenType = context.Compilation.GetBestTypeByMetadataName("System.Reflection.PropertyInfo"); + var msTestAssertTokenType = context.Compilation.GetBestTypeByMetadataName("Microsoft.VisualStudio.TestTools.UnitTesting.Assert"); + var nunitAssertTokenType = context.Compilation.GetBestTypeByMetadataName("NUnit.Framework.Assert"); + var xunitAssertTokenType = context.Compilation.GetBestTypeByMetadataName("Xunit.Assert"); + var keyValuePairTokenType = context.Compilation.GetBestTypeByMetadataName("System.Collection.Generic.KeyValuePair`2"); + var propertyBuilderType = context.Compilation.GetBestTypeByMetadataName("Microsoft.EntityFrameworkCore.Metadata.Builders.PropertyBuilder`1"); + var syntaxNodeType = context.Compilation.GetBestTypeByMetadataName("Microsoft.CodeAnalysis.SyntaxNode"); + var expressionType = context.Compilation.GetBestTypeByMetadataName("System.Linq.Expressions.Expression"); + var operationUtilities = new OperationUtilities(context.Compilation); + + context.RegisterSyntaxNodeAction(syntaxContext => { var argument = (ArgumentSyntax)syntaxContext.Node; if (argument.NameColon is not null) @@ -220,10 +216,10 @@ bool IsParams(SyntaxNode node) if (IsMethod(invokedMethodSymbol, valueTaskTokenType, nameof(Task.FromResult))) return; - if (IsMethod(invokedMethodSymbol, taskCompletionSourceType, nameof(TaskCompletionSource.SetResult))) + if (IsMethod(invokedMethodSymbol, taskCompletionSourceType, nameof(TaskCompletionSource<>.SetResult))) return; - if (IsMethod(invokedMethodSymbol, taskCompletionSourceType, nameof(TaskCompletionSource.TrySetResult))) + if (IsMethod(invokedMethodSymbol, taskCompletionSourceType, nameof(TaskCompletionSource<>.TrySetResult))) return; if (IsMethod(invokedMethodSymbol, methodBaseTokenType, nameof(MethodBase.Invoke)) && argumentIndex == 0) diff --git a/src/Meziantou.Analyzer/Rules/NonFlagsEnumsShouldNotBeMarkedWithFlagsAttributeAnalyzer.cs b/src/Meziantou.Analyzer/Rules/NonFlagsEnumsShouldNotBeMarkedWithFlagsAttributeAnalyzer.cs index 337ba18b5..3f6131d35 100644 --- a/src/Meziantou.Analyzer/Rules/NonFlagsEnumsShouldNotBeMarkedWithFlagsAttributeAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/NonFlagsEnumsShouldNotBeMarkedWithFlagsAttributeAnalyzer.cs @@ -1,7 +1,4 @@ -using System; using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; -using System.Linq; using Meziantou.Analyzer.Configurations; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; diff --git a/src/Meziantou.Analyzer/Rules/NullableAttributeUsageAnalyzer.cs b/src/Meziantou.Analyzer/Rules/NullableAttributeUsageAnalyzer.cs index c65cfef48..b301b9aa5 100644 --- a/src/Meziantou.Analyzer/Rules/NullableAttributeUsageAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/NullableAttributeUsageAnalyzer.cs @@ -1,5 +1,4 @@ -using System.Collections.Immutable; -using System.Linq; +using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Meziantou.Analyzer/Rules/OptimizeLinqUsageAnalyzer.cs b/src/Meziantou.Analyzer/Rules/OptimizeLinqUsageAnalyzer.cs index b6c59ee93..3e9df6661 100755 --- a/src/Meziantou.Analyzer/Rules/OptimizeLinqUsageAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/OptimizeLinqUsageAnalyzer.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Globalization; -using System.Linq; using Meziantou.Analyzer.Configurations; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; @@ -10,7 +6,6 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; -using static System.FormattableString; namespace Meziantou.Analyzer.Rules; @@ -566,7 +561,7 @@ private void OptimizeCountUsage(OperationAnalysisContext context, IInvocationOpe // expr.Count() == 1 if (!HasTake(operation, ExtensionMethodOwnerTypes)) { - message = Invariant($"Replace 'Count() == {value}' with 'Take({value + 1}).Count() == {value}'"); + message = string.Create(CultureInfo.InvariantCulture, $"Replace 'Count() == {value}' with 'Take({value + 1}).Count() == {value}'"); properties = CreateProperties(OptimizeLinqUsageData.UseTakeAndCount); } } @@ -591,7 +586,7 @@ private void OptimizeCountUsage(OperationAnalysisContext context, IInvocationOpe // expr.Count() != 1 if (!HasTake(operation, ExtensionMethodOwnerTypes)) { - message = Invariant($"Replace 'Count() != {value}' with 'Take({value + 1}).Count() != {value}'"); + message = string.Create(CultureInfo.InvariantCulture, $"Replace 'Count() != {value}' with 'Take({value + 1}).Count() != {value}'"); properties = CreateProperties(OptimizeLinqUsageData.UseTakeAndCount); } } @@ -614,7 +609,7 @@ private void OptimizeCountUsage(OperationAnalysisContext context, IInvocationOpe else { // expr.Count() < 10 - message = Invariant($"Replace 'Count() < {value}' with 'Skip({value - 1}).Any() == false'"); + message = string.Create(CultureInfo.InvariantCulture, $"Replace 'Count() < {value}' with 'Skip({value - 1}).Any() == false'"); properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndNotAny) .Add("SkipMinusOne", value: ""); } @@ -637,7 +632,7 @@ private void OptimizeCountUsage(OperationAnalysisContext context, IInvocationOpe else { // expr.Count() < 10 - message = Invariant($"Replace 'Count() <= {value}' with 'Skip({value}).Any() == false'"); + message = string.Create(CultureInfo.InvariantCulture, $"Replace 'Count() <= {value}' with 'Skip({value}).Any() == false'"); properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndNotAny); } @@ -659,7 +654,7 @@ private void OptimizeCountUsage(OperationAnalysisContext context, IInvocationOpe else { // expr.Count() > 1 - message = Invariant($"Replace 'Count() > {value}' with 'Skip({value}).Any()'"); + message = string.Create(CultureInfo.InvariantCulture, $"Replace 'Count() > {value}' with 'Skip({value}).Any()'"); properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndAny); } @@ -681,7 +676,7 @@ private void OptimizeCountUsage(OperationAnalysisContext context, IInvocationOpe else { // expr.Count() >= 2 - message = Invariant($"Replace 'Count() >= {value}' with 'Skip({value - 1}).Any()'"); + message = string.Create(CultureInfo.InvariantCulture, $"Replace 'Count() >= {value}' with 'Skip({value - 1}).Any()'"); properties = CreateProperties(OptimizeLinqUsageData.UseSkipAndAny) .Add("SkipMinusOne", value: ""); } diff --git a/src/Meziantou.Analyzer/Rules/OptimizeStartsWithAnalyzer.cs b/src/Meziantou.Analyzer/Rules/OptimizeStartsWithAnalyzer.cs index fbe6b2a3c..e58bd3328 100644 --- a/src/Meziantou.Analyzer/Rules/OptimizeStartsWithAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/OptimizeStartsWithAnalyzer.cs @@ -1,6 +1,4 @@ -using System; using System.Collections.Immutable; -using System.Linq; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Meziantou.Analyzer/Rules/OptimizeStringBuilderUsageAnalyzer.cs b/src/Meziantou.Analyzer/Rules/OptimizeStringBuilderUsageAnalyzer.cs index 704cdaf39..87e352dff 100644 --- a/src/Meziantou.Analyzer/Rules/OptimizeStringBuilderUsageAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/OptimizeStringBuilderUsageAnalyzer.cs @@ -1,7 +1,4 @@ -using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; -using System.Text; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Meziantou.Analyzer/Rules/OptimizeStringBuilderUsageAnalyzerCommon.cs b/src/Meziantou.Analyzer/Rules/OptimizeStringBuilderUsageAnalyzerCommon.cs index 4e0cb48db..aede1967e 100644 --- a/src/Meziantou.Analyzer/Rules/OptimizeStringBuilderUsageAnalyzerCommon.cs +++ b/src/Meziantou.Analyzer/Rules/OptimizeStringBuilderUsageAnalyzerCommon.cs @@ -1,4 +1,3 @@ -using System.Text; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Operations; diff --git a/src/Meziantou.Analyzer/Rules/ParameterAttributeForRazorComponentAnalyzer.cs b/src/Meziantou.Analyzer/Rules/ParameterAttributeForRazorComponentAnalyzer.cs index cbb6d2d9f..92c7bbe16 100644 --- a/src/Meziantou.Analyzer/Rules/ParameterAttributeForRazorComponentAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/ParameterAttributeForRazorComponentAnalyzer.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; diff --git a/src/Meziantou.Analyzer/Rules/PreferReturningCollectionAbstractionInsteadOfImplementationAnalyzer.cs b/src/Meziantou.Analyzer/Rules/PreferReturningCollectionAbstractionInsteadOfImplementationAnalyzer.cs index c825703c4..e7529139b 100644 --- a/src/Meziantou.Analyzer/Rules/PreferReturningCollectionAbstractionInsteadOfImplementationAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/PreferReturningCollectionAbstractionInsteadOfImplementationAnalyzer.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; diff --git a/src/Meziantou.Analyzer/Rules/PreserveParamsOnOverrideAnalyzer.cs b/src/Meziantou.Analyzer/Rules/PreserveParamsOnOverrideAnalyzer.cs index 97a8626b0..13a683730 100644 --- a/src/Meziantou.Analyzer/Rules/PreserveParamsOnOverrideAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/PreserveParamsOnOverrideAnalyzer.cs @@ -1,6 +1,4 @@ -using System.Collections.Immutable; -using System.Linq; -using System.Threading; +using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/src/Meziantou.Analyzer/Rules/PrimaryConstructorParameterShouldBeReadOnlyAnalyzer.cs b/src/Meziantou.Analyzer/Rules/PrimaryConstructorParameterShouldBeReadOnlyAnalyzer.cs index 2f9e203c0..d1fe35838 100644 --- a/src/Meziantou.Analyzer/Rules/PrimaryConstructorParameterShouldBeReadOnlyAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/PrimaryConstructorParameterShouldBeReadOnlyAnalyzer.cs @@ -1,8 +1,5 @@ -#if CSHARP12_OR_GREATER -using System.Collections.Generic; +#if CSHARP12_OR_GREATER using System.Collections.Immutable; -using System.Linq; -using System.Threading; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/src/Meziantou.Analyzer/Rules/ProcessStartAnalyzer.cs b/src/Meziantou.Analyzer/Rules/ProcessStartAnalyzer.cs index 7e7004f5d..a4d04bcbb 100644 --- a/src/Meziantou.Analyzer/Rules/ProcessStartAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/ProcessStartAnalyzer.cs @@ -1,5 +1,4 @@ -using System.Collections.Immutable; -using System.Linq; +using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/src/Meziantou.Analyzer/Rules/RegexUsageAnalyzerBase.cs b/src/Meziantou.Analyzer/Rules/RegexUsageAnalyzerBase.cs index 82d116daf..efc755700 100644 --- a/src/Meziantou.Analyzer/Rules/RegexUsageAnalyzerBase.cs +++ b/src/Meziantou.Analyzer/Rules/RegexUsageAnalyzerBase.cs @@ -1,6 +1,4 @@ -using System; using System.Collections.Immutable; -using System.Linq; using System.Text.RegularExpressions; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; diff --git a/src/Meziantou.Analyzer/Rules/RemoveEmptyBlockAnalyzer.cs b/src/Meziantou.Analyzer/Rules/RemoveEmptyBlockAnalyzer.cs index 4ce5f33ec..6a796856a 100644 --- a/src/Meziantou.Analyzer/Rules/RemoveEmptyBlockAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/RemoveEmptyBlockAnalyzer.cs @@ -1,5 +1,4 @@ -using System.Collections.Immutable; -using System.Linq; +using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; diff --git a/src/Meziantou.Analyzer/Rules/RemoveUselessToStringAnalyzer.cs b/src/Meziantou.Analyzer/Rules/RemoveUselessToStringAnalyzer.cs index 1158626e5..393c6c0fd 100644 --- a/src/Meziantou.Analyzer/Rules/RemoveUselessToStringAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/RemoveUselessToStringAnalyzer.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; diff --git a/src/Meziantou.Analyzer/Rules/ReturnTaskFromResultInsteadOfReturningNullAnalyzer.cs b/src/Meziantou.Analyzer/Rules/ReturnTaskFromResultInsteadOfReturningNullAnalyzer.cs index da33d4c78..37e8e0fea 100644 --- a/src/Meziantou.Analyzer/Rules/ReturnTaskFromResultInsteadOfReturningNullAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/ReturnTaskFromResultInsteadOfReturningNullAnalyzer.cs @@ -1,5 +1,4 @@ -using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; +using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Meziantou.Analyzer/Rules/ReturnTaskFromResultInsteadOfReturningNullAnalyzerCommon.cs b/src/Meziantou.Analyzer/Rules/ReturnTaskFromResultInsteadOfReturningNullAnalyzerCommon.cs index 651ad3d69..573123d98 100644 --- a/src/Meziantou.Analyzer/Rules/ReturnTaskFromResultInsteadOfReturningNullAnalyzerCommon.cs +++ b/src/Meziantou.Analyzer/Rules/ReturnTaskFromResultInsteadOfReturningNullAnalyzerCommon.cs @@ -1,4 +1,3 @@ -using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis; diff --git a/src/Meziantou.Analyzer/Rules/SequenceNumberMustBeAConstantAnalyzer.cs b/src/Meziantou.Analyzer/Rules/SequenceNumberMustBeAConstantAnalyzer.cs index 156615735..c5ce14105 100644 --- a/src/Meziantou.Analyzer/Rules/SequenceNumberMustBeAConstantAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/SequenceNumberMustBeAConstantAnalyzer.cs @@ -1,5 +1,3 @@ -using System; -using System.Collections.Generic; using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; diff --git a/src/Meziantou.Analyzer/Rules/SimplifyCallerArgumentExpressionAnalyzer.cs b/src/Meziantou.Analyzer/Rules/SimplifyCallerArgumentExpressionAnalyzer.cs index 0a0900fd2..c9ca2a036 100644 --- a/src/Meziantou.Analyzer/Rules/SimplifyCallerArgumentExpressionAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/SimplifyCallerArgumentExpressionAnalyzer.cs @@ -1,5 +1,4 @@ -using System.Collections.Immutable; -using System.Linq; +using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Meziantou.Analyzer/Rules/ThrowIfNullWithNonNullableInstanceAnalyzer.cs b/src/Meziantou.Analyzer/Rules/ThrowIfNullWithNonNullableInstanceAnalyzer.cs index c5c46c6ed..234730919 100644 --- a/src/Meziantou.Analyzer/Rules/ThrowIfNullWithNonNullableInstanceAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/ThrowIfNullWithNonNullableInstanceAnalyzer.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; diff --git a/src/Meziantou.Analyzer/Rules/TypeNameMustNotMatchNamespaceAnalyzer.cs b/src/Meziantou.Analyzer/Rules/TypeNameMustNotMatchNamespaceAnalyzer.cs index 65d665803..da2e21caa 100644 --- a/src/Meziantou.Analyzer/Rules/TypeNameMustNotMatchNamespaceAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/TypeNameMustNotMatchNamespaceAnalyzer.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; diff --git a/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasCancellationTokenAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasCancellationTokenAnalyzer.cs index f6604a136..a34712da4 100644 --- a/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasCancellationTokenAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasCancellationTokenAnalyzer.cs @@ -1,13 +1,7 @@ -using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Linq; using System.Runtime.InteropServices; -using System.Threading; using Meziantou.Analyzer.Configurations; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; @@ -242,7 +236,7 @@ public void AnalyzeLoop(OperationAnalysisContext context) .Add("ParameterIndex", parameterInfo.ParameterIndex.ToString(CultureInfo.InvariantCulture)) .Add("ParameterName", parameterInfo.Name) .Add("ParameterIsEnumeratorCancellation", parameterInfo.HasEnumeratorCancellationAttribute.ToString()) - .Add("CancellationTokens", string.Join(",", cancellationTokens)); + .Add("CancellationTokens", string.Join(',', cancellationTokens)); } private List? GetMembers(ITypeSymbol symbol, int maxDepth) @@ -297,7 +291,7 @@ public void AnalyzeLoop(OperationAnalysisContext context) { foreach (var objectMember in typeMembers) { - result.Add(Prepend(member, objectMember).ToArray()); + result.Add([.. Prepend(member, objectMember)]); } } } @@ -376,9 +370,9 @@ static bool AreAllSymbolsAccessibleFromOperation(IEnumerable symbols, I static string ComputeFullPath(string? prefix, IEnumerable symbols) { if (prefix is null) - return string.Join(".", symbols.Select(symbol => symbol.Name)); + return string.Join('.', symbols.Select(symbol => symbol.Name)); - var suffix = string.Join(".", symbols.Select(symbol => symbol.Name)); + var suffix = string.Join('.', symbols.Select(symbol => symbol.Name)); if (string.IsNullOrEmpty(suffix)) return prefix; diff --git a/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasTimeProviderAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasTimeProviderAnalyzer.cs index b79caa083..db7c11eeb 100644 --- a/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasTimeProviderAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasTimeProviderAnalyzer.cs @@ -1,18 +1,9 @@ -using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Linq; using System.Runtime.InteropServices; -using System.Threading; -using Meziantou.Analyzer.Configurations; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; @@ -147,7 +138,7 @@ public void AnalyzeInvocation(OperationAnalysisContext context) return ImmutableDictionary.Create() .Add("ParameterIndex", parameterInfo.ParameterIndex.ToString(CultureInfo.InvariantCulture)) .Add("ParameterName", parameterInfo.Name) - .Add("Paths", string.Join(",", cancellationTokens)); + .Add("Paths", string.Join(',', cancellationTokens)); } private List? GetMembers(ITypeSymbol symbol, int maxDepth) @@ -199,7 +190,7 @@ public void AnalyzeInvocation(OperationAnalysisContext context) { foreach (var objectMember in typeMembers) { - result.Add(Prepend(member, objectMember).ToArray()); + result.Add([.. Prepend(member, objectMember)]); } } } @@ -270,9 +261,9 @@ static bool AreAllSymbolsAccessibleFromOperation(IEnumerable symbols, I static string ComputeFullPath(string? prefix, IEnumerable symbols) { if (prefix is null) - return string.Join(".", symbols.Select(symbol => symbol.Name)); + return string.Join('.', symbols.Select(symbol => symbol.Name)); - var suffix = string.Join(".", symbols.Select(symbol => symbol.Name)); + var suffix = string.Join('.', symbols.Select(symbol => symbol.Name)); if (string.IsNullOrEmpty(suffix)) return prefix; diff --git a/src/Meziantou.Analyzer/Rules/UseArrayEmptyAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseArrayEmptyAnalyzer.cs index 3f4390f13..54275f555 100644 --- a/src/Meziantou.Analyzer/Rules/UseArrayEmptyAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseArrayEmptyAnalyzer.cs @@ -1,5 +1,4 @@ -using System.Collections.Immutable; -using System.Linq; +using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/src/Meziantou.Analyzer/Rules/UseConfigureAwaitAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseConfigureAwaitAnalyzer.cs index e78f5709d..95a204bc6 100644 --- a/src/Meziantou.Analyzer/Rules/UseConfigureAwaitAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseConfigureAwaitAnalyzer.cs @@ -1,7 +1,4 @@ -using System; using System.Collections.Immutable; -using System.Linq; -using System.Threading; using Meziantou.Analyzer.Configurations; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; diff --git a/src/Meziantou.Analyzer/Rules/UseContainsKeyInsteadOfTryGetValueAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseContainsKeyInsteadOfTryGetValueAnalyzer.cs index 7ba4d8acf..6b8819c01 100644 --- a/src/Meziantou.Analyzer/Rules/UseContainsKeyInsteadOfTryGetValueAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseContainsKeyInsteadOfTryGetValueAnalyzer.cs @@ -1,6 +1,4 @@ -using System; using System.Collections.Immutable; -using System.Linq; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Meziantou.Analyzer/Rules/UseDateTimeUnixEpochAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseDateTimeUnixEpochAnalyzer.cs index 16cf8550c..59ddad120 100644 --- a/src/Meziantou.Analyzer/Rules/UseDateTimeUnixEpochAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseDateTimeUnixEpochAnalyzer.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; diff --git a/src/Meziantou.Analyzer/Rules/UseEqualsMethodInsteadOfOperatorAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseEqualsMethodInsteadOfOperatorAnalyzer.cs index d275ca796..8d808d8e9 100644 --- a/src/Meziantou.Analyzer/Rules/UseEqualsMethodInsteadOfOperatorAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseEqualsMethodInsteadOfOperatorAnalyzer.cs @@ -1,5 +1,4 @@ using System.Collections.Immutable; -using System.Linq; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Meziantou.Analyzer/Rules/UseEventHandlerOfTAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseEventHandlerOfTAnalyzer.cs index 01c9c59c4..1749b4d9c 100644 --- a/src/Meziantou.Analyzer/Rules/UseEventHandlerOfTAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseEventHandlerOfTAnalyzer.cs @@ -1,5 +1,4 @@ -using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; +using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs index 5136e300b..c0ca66197 100644 --- a/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Collections.Immutable; using Meziantou.Analyzer.Configurations; using Meziantou.Analyzer.Internals; diff --git a/src/Meziantou.Analyzer/Rules/UseIsPatternInsteadOfSequenceEqualAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseIsPatternInsteadOfSequenceEqualAnalyzer.cs index 98869c111..05358e2cd 100644 --- a/src/Meziantou.Analyzer/Rules/UseIsPatternInsteadOfSequenceEqualAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseIsPatternInsteadOfSequenceEqualAnalyzer.cs @@ -1,9 +1,7 @@ -using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis; using System.Collections.Immutable; using Microsoft.CodeAnalysis.Operations; -using System.Linq; -using System; using Meziantou.Analyzer.Internals; namespace Meziantou.Analyzer.Rules; diff --git a/src/Meziantou.Analyzer/Rules/UseJSRuntimeInvokeVoidAsyncWhenReturnValueIsNotUsedAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseJSRuntimeInvokeVoidAsyncWhenReturnValueIsNotUsedAnalyzer.cs index 4429aad5e..6b1d20c6b 100644 --- a/src/Meziantou.Analyzer/Rules/UseJSRuntimeInvokeVoidAsyncWhenReturnValueIsNotUsedAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseJSRuntimeInvokeVoidAsyncWhenReturnValueIsNotUsedAnalyzer.cs @@ -1,4 +1,4 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; @@ -73,7 +73,7 @@ static bool IsValueUsed(IInvocationOperation operation) parent = parent.Parent; } - if (parent is null || parent is IBlockOperation || parent is IExpressionStatementOperation) + if (parent is null or IBlockOperation or IExpressionStatementOperation) return false; return true; diff --git a/src/Meziantou.Analyzer/Rules/UseLangwordInXmlCommentAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseLangwordInXmlCommentAnalyzer.cs index 1dc30feac..75bd486ce 100644 --- a/src/Meziantou.Analyzer/Rules/UseLangwordInXmlCommentAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseLangwordInXmlCommentAnalyzer.cs @@ -1,9 +1,7 @@ -using System; using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Syntax; -using System.Collections.Generic; using Meziantou.Analyzer.Internals; using System.Linq.Expressions; diff --git a/src/Meziantou.Analyzer/Rules/UseOperatingSystemInsteadOfRuntimeInformationAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseOperatingSystemInsteadOfRuntimeInformationAnalyzer.cs index ea9aad65f..2055b50a4 100644 --- a/src/Meziantou.Analyzer/Rules/UseOperatingSystemInsteadOfRuntimeInformationAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseOperatingSystemInsteadOfRuntimeInformationAnalyzer.cs @@ -1,4 +1,4 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; @@ -27,7 +27,7 @@ public override void Initialize(AnalysisContext context) context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); context.RegisterCompilationStartAction(context => { - var isOSPlatformSymbol = DocumentationCommentId.GetFirstSymbolForDeclarationId("M:System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform)", context.Compilation) as IMethodSymbol; + var isOSPlatformSymbol = DocumentationCommentId.GetFirstSymbolForDeclarationId("M:System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform)", context.Compilation); var osPlatformSymbol = context.Compilation.GetBestTypeByMetadataName("System.Runtime.InteropServices.OSPlatform"); var operatingSystemSymbol = DocumentationCommentId.GetFirstSymbolForDeclarationId("M:System.OperatingSystem.IsWindows", context.Compilation); if (isOSPlatformSymbol is null || operatingSystemSymbol is null || !context.Compilation.IsSymbolAccessibleWithin(operatingSystemSymbol, context.Compilation.Assembly) || osPlatformSymbol is null) @@ -37,7 +37,7 @@ public override void Initialize(AnalysisContext context) }); } - private static void AnalyzeInvocation(OperationAnalysisContext context, IMethodSymbol runtimeInformationSymbol, INamedTypeSymbol osPlatformSymbol) + private static void AnalyzeInvocation(OperationAnalysisContext context, ISymbol runtimeInformationSymbol, INamedTypeSymbol osPlatformSymbol) { var operation = (IInvocationOperation)context.Operation; if (operation.Arguments.Length == 1 && SymbolEqualityComparer.Default.Equals(runtimeInformationSymbol, operation.TargetMethod)) diff --git a/src/Meziantou.Analyzer/Rules/UsePatternMatchingForEqualityComparisonsAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UsePatternMatchingForEqualityComparisonsAnalyzer.cs index a79e4f4ee..99457e55f 100644 --- a/src/Meziantou.Analyzer/Rules/UsePatternMatchingForEqualityComparisonsAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UsePatternMatchingForEqualityComparisonsAnalyzer.cs @@ -1,5 +1,4 @@ -using System.Collections.Immutable; -using System.Linq; +using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Meziantou.Analyzer/Rules/UsePatternMatchingInsteadOfHasValueAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UsePatternMatchingInsteadOfHasValueAnalyzer.cs index 72bc9720a..5939523d9 100644 --- a/src/Meziantou.Analyzer/Rules/UsePatternMatchingInsteadOfHasValueAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UsePatternMatchingInsteadOfHasValueAnalyzer.cs @@ -1,5 +1,4 @@ -using System.Collections.Immutable; -using System.Linq; +using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Meziantou.Analyzer/Rules/UseRegexSourceGeneratorAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseRegexSourceGeneratorAnalyzer.cs index bee638a32..2e4abd8c7 100644 --- a/src/Meziantou.Analyzer/Rules/UseRegexSourceGeneratorAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseRegexSourceGeneratorAnalyzer.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; using System.Collections.Immutable; -using System.Globalization; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Meziantou.Analyzer/Rules/UseStringComparerAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseStringComparerAnalyzer.cs index 9cb952b86..c04e42f34 100644 --- a/src/Meziantou.Analyzer/Rules/UseStringComparerAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseStringComparerAnalyzer.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using Meziantou.Analyzer.Configurations; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; diff --git a/src/Meziantou.Analyzer/Rules/UseStringComparisonAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseStringComparisonAnalyzer.cs index 795f6e7a1..d1ccde79f 100644 --- a/src/Meziantou.Analyzer/Rules/UseStringComparisonAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseStringComparisonAnalyzer.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; diff --git a/src/Meziantou.Analyzer/Rules/UseStringCreateInsteadOfFormattableStringAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseStringCreateInsteadOfFormattableStringAnalyzer.cs index 7d4110303..a13020c5c 100644 --- a/src/Meziantou.Analyzer/Rules/UseStringCreateInsteadOfFormattableStringAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseStringCreateInsteadOfFormattableStringAnalyzer.cs @@ -1,5 +1,4 @@ -using System.Collections.Immutable; -using System.Linq; +using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Meziantou.Analyzer/Rules/UseStringEqualsAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseStringEqualsAnalyzer.cs index f6ee18eec..5f37a6d29 100644 --- a/src/Meziantou.Analyzer/Rules/UseStringEqualsAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseStringEqualsAnalyzer.cs @@ -1,4 +1,4 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; @@ -36,8 +36,7 @@ public override void Initialize(AnalysisContext context) private static void AnalyzeInvocation(OperationAnalysisContext context, OperationUtilities operationUtilities) { var operation = (IBinaryOperation)context.Operation; - if (operation.OperatorKind == BinaryOperatorKind.Equals || - operation.OperatorKind == BinaryOperatorKind.NotEquals) + if (operation.OperatorKind is BinaryOperatorKind.Equals or BinaryOperatorKind.NotEquals) { if (operation.LeftOperand.Type.IsString() && operation.RightOperand.Type.IsString()) { diff --git a/src/Meziantou.Analyzer/Rules/UseStructLayoutAttributeAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseStructLayoutAttributeAnalyzer.cs index 67a128f1e..b12de10c8 100644 --- a/src/Meziantou.Analyzer/Rules/UseStructLayoutAttributeAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseStructLayoutAttributeAnalyzer.cs @@ -1,5 +1,4 @@ -using System.Collections.Immutable; -using System.Linq; +using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Meziantou.Analyzer/Rules/ValidateArgumentsCorrectlyAnalyzer.cs b/src/Meziantou.Analyzer/Rules/ValidateArgumentsCorrectlyAnalyzer.cs index 21b5187b4..6b68e77a1 100755 --- a/src/Meziantou.Analyzer/Rules/ValidateArgumentsCorrectlyAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/ValidateArgumentsCorrectlyAnalyzer.cs @@ -1,7 +1,4 @@ -using System.Collections.Generic; using System.Collections.Immutable; -using System.Globalization; -using System.Linq; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -153,7 +150,7 @@ private static bool FilterDescendants(SyntaxNode node) if (operation is IMethodBodyOperation) break; - if (operation.Parent is not null && operation.Parent is IBlockOperation) + if (operation.Parent is IBlockOperation) { if (operation.Parent.Parent is IMethodBodyOperation) break; diff --git a/src/Meziantou.Analyzer/Rules/ValueReturnedByStreamReadShouldBeUsedAnalyzer.cs b/src/Meziantou.Analyzer/Rules/ValueReturnedByStreamReadShouldBeUsedAnalyzer.cs index 1c2a1ac11..359d9a27e 100644 --- a/src/Meziantou.Analyzer/Rules/ValueReturnedByStreamReadShouldBeUsedAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/ValueReturnedByStreamReadShouldBeUsedAnalyzer.cs @@ -1,5 +1,4 @@ -using System.Collections.Immutable; -using System.IO; +using System.Collections.Immutable; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; @@ -41,7 +40,7 @@ private static void AnalyzeOperation(OperationAnalysisContext context, INamedTyp { var invocation = (IInvocationOperation)context.Operation; var targetMethod = invocation.TargetMethod; - if (targetMethod.Name != nameof(Stream.Read) && targetMethod.Name != nameof(Stream.ReadAsync)) + if (targetMethod.Name is not nameof(Stream.Read) and not nameof(Stream.ReadAsync)) return; if (!targetMethod.ContainingType.IsOrInheritFrom(streamSymbol)) @@ -53,7 +52,7 @@ private static void AnalyzeOperation(OperationAnalysisContext context, INamedTyp parent = parent.Parent; } - if (parent is null || parent is IBlockOperation || parent is IExpressionStatementOperation) + if (parent is null or IBlockOperation or IExpressionStatementOperation) { context.ReportDiagnostic(Rule, invocation, targetMethod.Name); } diff --git a/tests/Meziantou.Analyzer.Test/Helpers/DiagnosticResult.cs b/tests/Meziantou.Analyzer.Test/Helpers/DiagnosticResult.cs index 263fc6b9d..7dd208779 100755 --- a/tests/Meziantou.Analyzer.Test/Helpers/DiagnosticResult.cs +++ b/tests/Meziantou.Analyzer.Test/Helpers/DiagnosticResult.cs @@ -1,16 +1,13 @@ -using System.Collections.Generic; using Microsoft.CodeAnalysis; namespace Meziantou.Analyzer.Test.Helpers; public sealed class DiagnosticResult { - private IReadOnlyList? _locations; - public IReadOnlyList Locations { - get => _locations ??= []; - set => _locations = value; + get => field ??= []; + set; } public DiagnosticSeverity? Severity { get; set; } diff --git a/tests/Meziantou.Analyzer.Test/Helpers/DiagnosticResultLocation.cs b/tests/Meziantou.Analyzer.Test/Helpers/DiagnosticResultLocation.cs index e1b6bd662..31e30c237 100644 --- a/tests/Meziantou.Analyzer.Test/Helpers/DiagnosticResultLocation.cs +++ b/tests/Meziantou.Analyzer.Test/Helpers/DiagnosticResultLocation.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.InteropServices; namespace Meziantou.Analyzer.Test.Helpers; diff --git a/tests/Meziantou.Analyzer.Test/Helpers/ProjectBuilder.Validation.cs b/tests/Meziantou.Analyzer.Test/Helpers/ProjectBuilder.Validation.cs index 6210a9d82..ec978770e 100755 --- a/tests/Meziantou.Analyzer.Test/Helpers/ProjectBuilder.Validation.cs +++ b/tests/Meziantou.Analyzer.Test/Helpers/ProjectBuilder.Validation.cs @@ -58,14 +58,14 @@ private void VerifyDiagnosticResults(IEnumerable actualResults, ILis var expectedCount = expectedResults.Count; if (DefaultAnalyzerId is not null) { - actualResults = actualResults.Where(diagnostic => diagnostic.Id == DefaultAnalyzerId).ToArray(); + actualResults = [.. actualResults.Where(diagnostic => diagnostic.Id == DefaultAnalyzerId)]; } var actualCount = actualResults.Count(); if (expectedCount != actualCount) { - var diagnosticsOutput = actualResults.Any() ? FormatDiagnostics(analyzers, actualResults.ToArray()) : " NONE."; + var diagnosticsOutput = actualResults.Any() ? FormatDiagnostics(analyzers, [.. actualResults]) : " NONE."; Assert.Fail($"Mismatch between number of diagnostics returned, expected \"{expectedCount.ToString(CultureInfo.InvariantCulture)}\" actual \"{actualCount.ToString(CultureInfo.InvariantCulture)}\"\r\n\r\nDiagnostics:\r\n{diagnosticsOutput}\r\n"); } @@ -474,7 +474,7 @@ private async Task VerifyFix(IList analyzers, CodeFixProvide { if (!codeFixProvider.FixableDiagnosticIds.Any(id => string.Equals(diagnostic.Id, id, StringComparison.Ordinal))) { - Assert.Fail($"The CodeFixProvider is not valid for the DiagnosticAnalyzer. DiagnosticId: {diagnostic.Id}, Supported diagnostics: {string.Join(",", codeFixProvider.FixableDiagnosticIds)}"); + Assert.Fail($"The CodeFixProvider is not valid for the DiagnosticAnalyzer. DiagnosticId: {diagnostic.Id}, Supported diagnostics: {string.Join(',', codeFixProvider.FixableDiagnosticIds)}"); } } diff --git a/tests/Meziantou.Analyzer.Test/Helpers/ProjectBuilder.cs b/tests/Meziantou.Analyzer.Test/Helpers/ProjectBuilder.cs index 9f9814b2f..aafb16eb8 100755 --- a/tests/Meziantou.Analyzer.Test/Helpers/ProjectBuilder.cs +++ b/tests/Meziantou.Analyzer.Test/Helpers/ProjectBuilder.cs @@ -1,24 +1,15 @@ -using System; using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.IO; using System.IO.Compression; -using System.Linq; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Security.Cryptography; -using System.Text; -using System.Threading.Tasks; using Meziantou.Analyzer.Annotations; -using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; -using Xunit; namespace TestHelper; @@ -37,11 +28,11 @@ public sealed partial class ProjectBuilder public bool IsValidFixCode { get; private set; } = true; public LanguageVersion LanguageVersion { get; private set; } = LanguageVersion.Latest; public TargetFramework TargetFramework { get; private set; } = TargetFramework.NetStandard2_0; - public IList References { get; } = new List(); - public IList ApiReferences { get; } = new List(); - public IList DiagnosticAnalyzer { get; } = new List(); + public IList References { get; } = []; + public IList ApiReferences { get; } = []; + public IList DiagnosticAnalyzer { get; } = []; public CodeFixProvider? CodeFixProvider { get; private set; } - public IList ExpectedDiagnosticResults { get; } = new List(); + public IList ExpectedDiagnosticResults { get; } = []; public string? ExpectedFixedCode { get; private set; } public int? CodeFixIndex { get; private set; } public bool UseBatchFixer { get; private set; } @@ -50,13 +41,8 @@ public sealed partial class ProjectBuilder private static async Task GetNuGetReferences(string packageName, string version, params string[] paths) { - var bytes = Encoding.UTF8.GetBytes(packageName + '@' + version + ':' + string.Join(",", paths)); -#if NET8_0_OR_GREATER + var bytes = Encoding.UTF8.GetBytes(packageName + '@' + version + ':' + string.Join(',', paths)); var hash = SHA256.HashData(bytes); -#else - using var sha256 = SHA256.Create(); - var hash = sha256.ComputeHash(bytes); -#endif var key = Convert.ToBase64String(hash).Replace('/', '_'); var task = NuGetPackagesCache.GetOrAdd(key, _ => new Lazy>(Download)); return await task.Value.ConfigureAwait(false); @@ -71,13 +57,13 @@ async Task Download() var tempFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N")); Directory.CreateDirectory(tempFolder); - using var stream = await SharedHttpClient.Instance.GetStreamAsync(new Uri($"https://www.nuget.org/api/v2/package/{packageName}/{version}")).ConfigureAwait(false); - using var zip = new ZipArchive(stream, ZipArchiveMode.Read); + await using var stream = await SharedHttpClient.Instance.GetStreamAsync(new Uri($"https://www.nuget.org/api/v2/package/{packageName}/{version}")).ConfigureAwait(false); + await using var zip = new ZipArchive(stream, ZipArchiveMode.Read); var hasEntry = false; foreach (var entry in zip.Entries.Where(file => paths.Any(path => file.FullName.StartsWith(path, StringComparison.Ordinal)))) { - entry.ExtractToFile(Path.Combine(tempFolder, entry.Name), overwrite: true); + await entry.ExtractToFileAsync(Path.Combine(tempFolder, entry.Name), overwrite: true); hasEntry = true; } diff --git a/tests/Meziantou.Analyzer.Test/Helpers/SharedHttpClient.cs b/tests/Meziantou.Analyzer.Test/Helpers/SharedHttpClient.cs index 2205fdd93..e52799156 100644 --- a/tests/Meziantou.Analyzer.Test/Helpers/SharedHttpClient.cs +++ b/tests/Meziantou.Analyzer.Test/Helpers/SharedHttpClient.cs @@ -1,8 +1,3 @@ -using System; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; - namespace Meziantou.Analyzer.Test.Helpers; internal static class SharedHttpClient { diff --git a/tests/Meziantou.Analyzer.Test/Helpers/TestAnalyzerConfigOptionsProvider.cs b/tests/Meziantou.Analyzer.Test/Helpers/TestAnalyzerConfigOptionsProvider.cs index ed39d17e3..13598b4b5 100644 --- a/tests/Meziantou.Analyzer.Test/Helpers/TestAnalyzerConfigOptionsProvider.cs +++ b/tests/Meziantou.Analyzer.Test/Helpers/TestAnalyzerConfigOptionsProvider.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/tests/Meziantou.Analyzer.Test/Meziantou.Analyzer.Test.csproj b/tests/Meziantou.Analyzer.Test/Meziantou.Analyzer.Test.csproj index 5499be778..61aa0ceec 100644 --- a/tests/Meziantou.Analyzer.Test/Meziantou.Analyzer.Test.csproj +++ b/tests/Meziantou.Analyzer.Test/Meziantou.Analyzer.Test.csproj @@ -1,11 +1,10 @@  - + - - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/Meziantou.Analyzer.Test/RuleHelpUri.cs b/tests/Meziantou.Analyzer.Test/RuleHelpUri.cs index 798fd5565..58d2cbf57 100755 --- a/tests/Meziantou.Analyzer.Test/RuleHelpUri.cs +++ b/tests/Meziantou.Analyzer.Test/RuleHelpUri.cs @@ -1,7 +1,5 @@ -using System; using Meziantou.Analyzer.Rules; using Microsoft.CodeAnalysis.Diagnostics; -using Xunit; namespace Meziantou.Analyzer.Test; public sealed class RuleHelpUri diff --git a/tests/Meziantou.Analyzer.Test/Rules/AbstractTypesShouldNotHaveConstructorsAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/AbstractTypesShouldNotHaveConstructorsAnalyzerTests.cs index 7ae5ae76c..13c912acb 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/AbstractTypesShouldNotHaveConstructorsAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/AbstractTypesShouldNotHaveConstructorsAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/AddOverloadWithSpanOrMemoryAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/AddOverloadWithSpanOrMemoryAnalyzerTests.cs index da5de3b87..3f5c99881 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/AddOverloadWithSpanOrMemoryAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/AddOverloadWithSpanOrMemoryAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/AnonymousDelegatesShouldNotBeUsedToUnsubscribeFromEventsAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/AnonymousDelegatesShouldNotBeUsedToUnsubscribeFromEventsAnalyzerTests.cs index deb81ffab..34df77417 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/AnonymousDelegatesShouldNotBeUsedToUnsubscribeFromEventsAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/AnonymousDelegatesShouldNotBeUsedToUnsubscribeFromEventsAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzerTests.cs index 2c70c8da2..a8be3f61f 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzer_UseNameofTests.cs b/tests/Meziantou.Analyzer.Test/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzer_UseNameofTests.cs index ed19c7004..67c56cf0b 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzer_UseNameofTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzer_UseNameofTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/AttributeNameShouldEndWithAttributeAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/AttributeNameShouldEndWithAttributeAnalyzerTests.cs index df2ba3bfa..d8c24b70e 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/AttributeNameShouldEndWithAttributeAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/AttributeNameShouldEndWithAttributeAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/AvoidComparisonWithBoolConstantAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/AvoidComparisonWithBoolConstantAnalyzerTests.cs index b34629e2b..1d9ef66eb 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/AvoidComparisonWithBoolConstantAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/AvoidComparisonWithBoolConstantAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/AvoidLockingOnPubliclyAccessibleInstanceAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/AvoidLockingOnPubliclyAccessibleInstanceAnalyzerTests.cs index 295e60692..3e73ce4e8 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/AvoidLockingOnPubliclyAccessibleInstanceAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/AvoidLockingOnPubliclyAccessibleInstanceAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/AvoidUsingRedundantElseAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/AvoidUsingRedundantElseAnalyzerTests.cs index 5d9d4f889..057e95790 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/AvoidUsingRedundantElseAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/AvoidUsingRedundantElseAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/AwaitAwaitableMethodInSyncMethodAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/AwaitAwaitableMethodInSyncMethodAnalyzerTests.cs index 6b2ba0464..2bd34ebc0 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/AwaitAwaitableMethodInSyncMethodAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/AwaitAwaitableMethodInSyncMethodAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Microsoft.CodeAnalysis; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public sealed class AwaitAwaitableMethodInSyncMethodAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/AwaitTaskBeforeDisposingResourcesAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/AwaitTaskBeforeDisposingResourcesAnalyzerTests.cs index 3450721ac..1f257d022 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/AwaitTaskBeforeDisposingResourcesAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/AwaitTaskBeforeDisposingResourcesAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/BothSideOfTheConditionAreIdenticalAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/BothSideOfTheConditionAreIdenticalAnalyzerTests.cs index 1e1f8c05e..ceaca8870 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/BothSideOfTheConditionAreIdenticalAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/BothSideOfTheConditionAreIdenticalAnalyzerTests.cs @@ -1,6 +1,5 @@ using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/ClassMustBeSealedAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/ClassMustBeSealedAnalyzerTests.cs index f5e9e1549..bbb0f1bfc 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/ClassMustBeSealedAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/ClassMustBeSealedAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/CommaAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/CommaAnalyzerTests.cs index 046e34c53..a818f4ecb 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/CommaAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/CommaAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/ConcurrentDictionaryMustPreventClosureWhenAccessingTheKeyAnalyzerTests_MA0105.cs b/tests/Meziantou.Analyzer.Test/Rules/ConcurrentDictionaryMustPreventClosureWhenAccessingTheKeyAnalyzerTests_MA0105.cs index c1d9c09c2..98f9cf475 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/ConcurrentDictionaryMustPreventClosureWhenAccessingTheKeyAnalyzerTests_MA0105.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/ConcurrentDictionaryMustPreventClosureWhenAccessingTheKeyAnalyzerTests_MA0105.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/ConcurrentDictionaryMustPreventClosureWhenAccessingTheKeyAnalyzerTests_MA0106.cs b/tests/Meziantou.Analyzer.Test/Rules/ConcurrentDictionaryMustPreventClosureWhenAccessingTheKeyAnalyzerTests_MA0106.cs index b895b8133..95dd3a44e 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/ConcurrentDictionaryMustPreventClosureWhenAccessingTheKeyAnalyzerTests_MA0106.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/ConcurrentDictionaryMustPreventClosureWhenAccessingTheKeyAnalyzerTests_MA0106.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/ConstructorArgumentParametersShouldExistInConstructorsAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/ConstructorArgumentParametersShouldExistInConstructorsAnalyzerTests.cs index 578f916b3..ae8d09585 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/ConstructorArgumentParametersShouldExistInConstructorsAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/ConstructorArgumentParametersShouldExistInConstructorsAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DeclareTypesInNamespacesAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DeclareTypesInNamespacesAnalyzerTests.cs index f24253e80..ee41c2927 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DeclareTypesInNamespacesAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DeclareTypesInNamespacesAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Microsoft.CodeAnalysis; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotCallVirtualMethodInConstructorAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotCallVirtualMethodInConstructorAnalyzerTests.cs index a95a5aaea..99300b12a 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotCallVirtualMethodInConstructorAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotCallVirtualMethodInConstructorAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotCompareDateTimeWithDateTimeOffsetAnalyzerTests_MA0132.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotCompareDateTimeWithDateTimeOffsetAnalyzerTests_MA0132.cs index deb348729..ee6089adc 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotCompareDateTimeWithDateTimeOffsetAnalyzerTests_MA0132.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotCompareDateTimeWithDateTimeOffsetAnalyzerTests_MA0132.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Microsoft.CodeAnalysis; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotCompareDateTimeWithDateTimeOffsetAnalyzerTests_MA0133.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotCompareDateTimeWithDateTimeOffsetAnalyzerTests_MA0133.cs index 7581f47c7..a99ba464c 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotCompareDateTimeWithDateTimeOffsetAnalyzerTests_MA0133.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotCompareDateTimeWithDateTimeOffsetAnalyzerTests_MA0133.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Microsoft.CodeAnalysis; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotDeclareStaticMembersOnGenericTypesTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotDeclareStaticMembersOnGenericTypesTests.cs index fdc649b64..c4913ec82 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotDeclareStaticMembersOnGenericTypesTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotDeclareStaticMembersOnGenericTypesTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotLogClassifiedDataAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotLogClassifiedDataAnalyzerTests.cs index 0a84535a1..0f085143e 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotLogClassifiedDataAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotLogClassifiedDataAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public sealed class DoNotLogClassifiedDataAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotNaNInComparisonsAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotNaNInComparisonsAnalyzerTests.cs index 7677dfd44..4700f13a0 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotNaNInComparisonsAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotNaNInComparisonsAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotOverwriteRazorComponentParameterValueTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotOverwriteRazorComponentParameterValueTests.cs index 92cce5079..5a69dab7f 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotOverwriteRazorComponentParameterValueTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotOverwriteRazorComponentParameterValueTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public class DoNotOverwriteRazorComponentParameterValueTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotRaiseApplicationExceptionAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotRaiseApplicationExceptionAnalyzerTests.cs index 7e7812429..12f7e7b1d 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotRaiseApplicationExceptionAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotRaiseApplicationExceptionAnalyzerTests.cs @@ -1,8 +1,6 @@ -#pragma warning disable CA1030 // Use events where appropriate -using System.Threading.Tasks; +#pragma warning disable CA1030 // Use events where appropriate using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotRaiseNotImplementedExceptionAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotRaiseNotImplementedExceptionAnalyzerTests.cs index 5d2568ef3..53c4426c7 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotRaiseNotImplementedExceptionAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotRaiseNotImplementedExceptionAnalyzerTests.cs @@ -1,8 +1,6 @@ -#pragma warning disable CA1030 // Use events where appropriate -using System.Threading.Tasks; +#pragma warning disable CA1030 // Use events where appropriate using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotRaiseReservedExceptionTypeAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotRaiseReservedExceptionTypeAnalyzerTests.cs index dfd5d8c4e..a29ad5dbb 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotRaiseReservedExceptionTypeAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotRaiseReservedExceptionTypeAnalyzerTests.cs @@ -1,8 +1,6 @@ -#pragma warning disable CA1030 // Use events where appropriate -using System.Threading.Tasks; +#pragma warning disable CA1030 // Use events where appropriate using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotRemoveOriginalExceptionFromThrowStatementAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotRemoveOriginalExceptionFromThrowStatementAnalyzerTests.cs index 56cdf7018..354b95e37 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotRemoveOriginalExceptionFromThrowStatementAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotRemoveOriginalExceptionFromThrowStatementAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotThrowFromFinalizerAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotThrowFromFinalizerAnalyzerTests.cs index e146bbe7c..e048d14da 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotThrowFromFinalizerAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotThrowFromFinalizerAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotThrowFromFinallyBlockAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotThrowFromFinallyBlockAnalyzerTests.cs index 9aa4ec830..0d3903dd1 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotThrowFromFinallyBlockAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotThrowFromFinallyBlockAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseAsyncDelegateForSyncDelegateAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseAsyncDelegateForSyncDelegateAnalyzerTests.cs index 8d79ec491..8994cafb7 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseAsyncDelegateForSyncDelegateAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseAsyncDelegateForSyncDelegateAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseAsyncVoidAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseAsyncVoidAnalyzerTests.cs index 6e569baa9..3108adb5e 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseAsyncVoidAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseAsyncVoidAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public sealed class DoNotUseAsyncVoidAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer_AsyncContextTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer_AsyncContextTests.cs index b22d7995b..0bfd6fa22 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer_AsyncContextTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer_AsyncContextTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer_NonAsyncContextTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer_NonAsyncContextTests.cs index 6ded213e2..b6616fd6a 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer_NonAsyncContextTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer_NonAsyncContextTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseDefaultEqualsOnValueTypeAnalyzer_EqualsTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseDefaultEqualsOnValueTypeAnalyzer_EqualsTests.cs index b3e7c067c..3c3712254 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseDefaultEqualsOnValueTypeAnalyzer_EqualsTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseDefaultEqualsOnValueTypeAnalyzer_EqualsTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseDefaultEqualsOnValueTypeAnalyzer_HashSetTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseDefaultEqualsOnValueTypeAnalyzer_HashSetTests.cs index 2434024a1..9c5b533de 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseDefaultEqualsOnValueTypeAnalyzer_HashSetTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseDefaultEqualsOnValueTypeAnalyzer_HashSetTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; @@ -137,7 +135,7 @@ await CreateProjectBuilder() [InlineData("System.Collections.Immutable.ImmutableSortedDictionary.Create()")] public async Task GetHashCode_Enum(string text) { - string sourceCode = @" + var sourceCode = @" enum Test { A, diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseEqualityComparerDefaultOfStringAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseEqualityComparerDefaultOfStringAnalyzerTests.cs index b99b6c7ad..cb84d5469 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseEqualityComparerDefaultOfStringAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseEqualityComparerDefaultOfStringAnalyzerTests.cs @@ -1,6 +1,5 @@ -using Meziantou.Analyzer.Rules; +using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseEqualityOperatorsForSpanOfCharAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseEqualityOperatorsForSpanOfCharAnalyzerTests.cs index 50a14f8b1..d949f2b1a 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseEqualityOperatorsForSpanOfCharAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseEqualityOperatorsForSpanOfCharAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseFinalizerAnalyzerTests.cs.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseFinalizerAnalyzerTests.cs.cs index ae836e46f..9c7da5f19 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseFinalizerAnalyzerTests.cs.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseFinalizerAnalyzerTests.cs.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseImplicitCultureSensitiveToStringAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseImplicitCultureSensitiveToStringAnalyzerTests.cs index ebd461e1d..ddb800385 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseImplicitCultureSensitiveToStringAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseImplicitCultureSensitiveToStringAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseServerCertificateValidationCallbackAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseServerCertificateValidationCallbackAnalyzerTests.cs index 34d6db3c1..de73622be 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseServerCertificateValidationCallbackAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseServerCertificateValidationCallbackAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseStringGetHashCodeAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseStringGetHashCodeAnalyzerTests.cs index 340de04e7..94246d64b 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseStringGetHashCodeAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseStringGetHashCodeAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseToStringIfObjectAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseToStringIfObjectAnalyzerTests.cs index 44896853e..2e31120fe 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseToStringIfObjectAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseToStringIfObjectAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public sealed class DoNotUseToStringIfObjectAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseUnknownParameterForRazorComponentAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseUnknownParameterForRazorComponentAnalyzerTests.cs index 9497fbcfb..c7f95187a 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseUnknownParameterForRazorComponentAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseUnknownParameterForRazorComponentAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public class DoNotUseUnknownParameterForRazorComponentAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseZeroToInitializeAnEnumValueTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseZeroToInitializeAnEnumValueTests.cs index 153cad8a1..f82d690a5 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseZeroToInitializeAnEnumValueTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseZeroToInitializeAnEnumValueTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DontTagInstanceFieldsWithThreadStaticAttributeAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DontTagInstanceFieldsWithThreadStaticAttributeAnalyzerTests.cs index 64b54cb3e..9efe002a9 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DontTagInstanceFieldsWithThreadStaticAttributeAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DontTagInstanceFieldsWithThreadStaticAttributeAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DontUseDangerousThreadingMethodsAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DontUseDangerousThreadingMethodsAnalyzerTests.cs index 120cb9f18..623fbb62c 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DontUseDangerousThreadingMethodsAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DontUseDangerousThreadingMethodsAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/DotNotUseNameFromBCLAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DotNotUseNameFromBCLAnalyzerTests.cs index 85f439fc1..c21481d03 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/DotNotUseNameFromBCLAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DotNotUseNameFromBCLAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/EmbedCaughtExceptionAsInnerExceptionAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/EmbedCaughtExceptionAsInnerExceptionAnalyzerTests.cs index 6a132298e..117112ef3 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/EmbedCaughtExceptionAsInnerExceptionAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/EmbedCaughtExceptionAsInnerExceptionAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/EqualityShouldBeCorrectlyImplementedAnalyzerMA0077Tests.cs b/tests/Meziantou.Analyzer.Test/Rules/EqualityShouldBeCorrectlyImplementedAnalyzerMA0077Tests.cs index eec477130..9ce592e15 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/EqualityShouldBeCorrectlyImplementedAnalyzerMA0077Tests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/EqualityShouldBeCorrectlyImplementedAnalyzerMA0077Tests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/EqualityShouldBeCorrectlyImplementedAnalyzerMA0094Tests.cs b/tests/Meziantou.Analyzer.Test/Rules/EqualityShouldBeCorrectlyImplementedAnalyzerMA0094Tests.cs index 7fa46ba4a..6f4ae45e6 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/EqualityShouldBeCorrectlyImplementedAnalyzerMA0094Tests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/EqualityShouldBeCorrectlyImplementedAnalyzerMA0094Tests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/EqualityShouldBeCorrectlyImplementedAnalyzerMA0097Tests.cs b/tests/Meziantou.Analyzer.Test/Rules/EqualityShouldBeCorrectlyImplementedAnalyzerMA0097Tests.cs index f7a26cea5..7b26c4251 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/EqualityShouldBeCorrectlyImplementedAnalyzerMA0097Tests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/EqualityShouldBeCorrectlyImplementedAnalyzerMA0097Tests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/EventArgsNameShouldEndWithEventArgsAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/EventArgsNameShouldEndWithEventArgsAnalyzerTests.cs index 182bdf3e8..22468579a 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/EventArgsNameShouldEndWithEventArgsAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/EventArgsNameShouldEndWithEventArgsAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/EventsShouldHaveProperArgumentsAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/EventsShouldHaveProperArgumentsAnalyzerTests.cs index ccff4fcbf..0857bd5fb 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/EventsShouldHaveProperArgumentsAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/EventsShouldHaveProperArgumentsAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/ExceptionNameShouldEndWithExceptionAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/ExceptionNameShouldEndWithExceptionAnalyzerTests.cs index 6955ea2fc..b762b10fa 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/ExceptionNameShouldEndWithExceptionAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/ExceptionNameShouldEndWithExceptionAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/FileNameMustMatchTypeNameAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/FileNameMustMatchTypeNameAnalyzerTests.cs index 2a007dbfb..132ac8fb2 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/FileNameMustMatchTypeNameAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/FileNameMustMatchTypeNameAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/FixToDoAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/FixToDoAnalyzerTests.cs index 9c1eae619..1b7571379 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/FixToDoAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/FixToDoAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/IfElseBranchesAreIdenticalAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/IfElseBranchesAreIdenticalAnalyzerTests.cs index be68da77f..73f52b768 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/IfElseBranchesAreIdenticalAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/IfElseBranchesAreIdenticalAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public sealed class IfElseBranchesAreIdenticalAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/JSInteropMustNotBeUsedInOnInitializedAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/JSInteropMustNotBeUsedInOnInitializedAnalyzerTests.cs index b8d3bea5a..4945a241c 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/JSInteropMustNotBeUsedInOnInitializedAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/JSInteropMustNotBeUsedInOnInitializedAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public sealed class JSInteropMustNotBeUsedInOnInitializedAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/JSInvokableMethodsMustBePublicAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/JSInvokableMethodsMustBePublicAnalyzerTests.cs index af85b4998..cf4573e92 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/JSInvokableMethodsMustBePublicAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/JSInvokableMethodsMustBePublicAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public class JSInvokableMethodsMustBePublicAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/LocalVariablesShouldNotHideSymbolsAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/LocalVariablesShouldNotHideSymbolsAnalyzerTests.cs index 5272f92fe..f01082df5 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/LocalVariablesShouldNotHideSymbolsAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/LocalVariablesShouldNotHideSymbolsAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzerTests.cs index c03775935..a30bdedcb 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public sealed class LoggerParameterTypeAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzer_SerilogTests.cs b/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzer_SerilogTests.cs index 6b7d51e2f..80dce8f5a 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzer_SerilogTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/LoggerParameterTypeAnalyzer_SerilogTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public sealed class LoggerParameterTypeAnalyzer_SerilogTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/MakeClassStaticAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/MakeClassStaticAnalyzerTests.cs index 134755c18..45e5f9904 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/MakeClassStaticAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/MakeClassStaticAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/MakeInterpolatedStringAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/MakeInterpolatedStringAnalyzerTests.cs index cf5cbd5e3..6ed027b3d 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/MakeInterpolatedStringAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/MakeInterpolatedStringAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public sealed class MakeInterpolatedStringAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/MakeMemberReadOnlyAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/MakeMemberReadOnlyAnalyzerTests.cs index 2470110e2..c095d31fe 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/MakeMemberReadOnlyAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/MakeMemberReadOnlyAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/MakeMethodStaticAnalyzerTests_Methods.cs b/tests/Meziantou.Analyzer.Test/Rules/MakeMethodStaticAnalyzerTests_Methods.cs index 5725b0d37..d3eef532f 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/MakeMethodStaticAnalyzerTests_Methods.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/MakeMethodStaticAnalyzerTests_Methods.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/MakeMethodStaticAnalyzerTests_Properties.cs b/tests/Meziantou.Analyzer.Test/Rules/MakeMethodStaticAnalyzerTests_Properties.cs index f5538726f..3cddc5e68 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/MakeMethodStaticAnalyzerTests_Properties.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/MakeMethodStaticAnalyzerTests_Properties.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/MarkAttributesWithAttributeUsageAttributeTests.cs b/tests/Meziantou.Analyzer.Test/Rules/MarkAttributesWithAttributeUsageAttributeTests.cs index 4c8d577d9..e1f58925d 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/MarkAttributesWithAttributeUsageAttributeTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/MarkAttributesWithAttributeUsageAttributeTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/MethodOverridesShouldNotChangeParameterDefaultsAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/MethodOverridesShouldNotChangeParameterDefaultsAnalyzerTests.cs index 527e64061..d006e5439 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/MethodOverridesShouldNotChangeParameterDefaultsAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/MethodOverridesShouldNotChangeParameterDefaultsAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/MethodShouldNotBeTooLongAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/MethodShouldNotBeTooLongAnalyzerTests.cs index c07b3559e..f7dc9a6e3 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/MethodShouldNotBeTooLongAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/MethodShouldNotBeTooLongAnalyzerTests.cs @@ -1,10 +1,7 @@ -using System.Linq; -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzerTests.cs index 8082d72ca..6dc62d9dd 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public sealed class MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/NamedParameterAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/NamedParameterAnalyzerTests.cs index 5a2e07d4a..c3b8ea651 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/NamedParameterAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/NamedParameterAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/NonConstantStaticFieldsShouldNotBeVisibleAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/NonConstantStaticFieldsShouldNotBeVisibleAnalyzerTests.cs index b2a74114b..75e0f3a81 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/NonConstantStaticFieldsShouldNotBeVisibleAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/NonConstantStaticFieldsShouldNotBeVisibleAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/NonFlagsEnumsShouldNotBeMarkedWithFlagsAttributeAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/NonFlagsEnumsShouldNotBeMarkedWithFlagsAttributeAnalyzerTests.cs index e53791d3b..f460a4998 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/NonFlagsEnumsShouldNotBeMarkedWithFlagsAttributeAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/NonFlagsEnumsShouldNotBeMarkedWithFlagsAttributeAnalyzerTests.cs @@ -1,9 +1,5 @@ -using System; -using System.Globalization; -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/NotPatternShouldBeParenthesizedAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/NotPatternShouldBeParenthesizedAnalyzerTests.cs index bfda10593..2c95d3bff 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/NotPatternShouldBeParenthesizedAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/NotPatternShouldBeParenthesizedAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public sealed class NotPatternShouldBeParenthesizedAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/NullableAttributeUsageAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/NullableAttributeUsageAnalyzerTests.cs index e95098f9d..eb7aa1cbe 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/NullableAttributeUsageAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/NullableAttributeUsageAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/ObjectGetTypeOnTypeInstanceAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/ObjectGetTypeOnTypeInstanceAnalyzerTests.cs index 0a49c5fe7..bca6eb9d8 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/ObjectGetTypeOnTypeInstanceAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/ObjectGetTypeOnTypeInstanceAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Microsoft.CodeAnalysis; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public sealed class ObjectGetTypeOnTypeInstanceAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/ObsoleteAttributesShouldIncludeExplanationsAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/ObsoleteAttributesShouldIncludeExplanationsAnalyzerTests.cs index 680c70532..aa6d44b39 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/ObsoleteAttributesShouldIncludeExplanationsAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/ObsoleteAttributesShouldIncludeExplanationsAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/OptimizeGuidParsingAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/OptimizeGuidParsingAnalyzerTests.cs index 276825e1d..f1efc2ca6 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/OptimizeGuidParsingAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/OptimizeGuidParsingAnalyzerTests.cs @@ -1,6 +1,5 @@ -using Meziantou.Analyzer.Rules; +using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerCombineLinqMethodsTests.cs b/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerCombineLinqMethodsTests.cs index cbcea8940..3f8cde808 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerCombineLinqMethodsTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerCombineLinqMethodsTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerCountTests.cs b/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerCountTests.cs index 640f4121e..8d33f2480 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerCountTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerCountTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerDuplicateOrderByTests.cs b/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerDuplicateOrderByTests.cs index da273a768..29df870d7 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerDuplicateOrderByTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerDuplicateOrderByTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerOrderTests.cs b/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerOrderTests.cs index 27c88ac7c..5bfdd169c 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerOrderTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerOrderTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerUseCastInsteadOfSelectTests.cs b/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerUseCastInsteadOfSelectTests.cs index 364d753ba..d5d08253a 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerUseCastInsteadOfSelectTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerUseCastInsteadOfSelectTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerUseCountInsteadOfAnyTests.cs b/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerUseCountInsteadOfAnyTests.cs index 31a4d2a4d..794f4ffde 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerUseCountInsteadOfAnyTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerUseCountInsteadOfAnyTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerUseDirectMethodsTests.cs b/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerUseDirectMethodsTests.cs index 947f01801..a786ba1b0 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerUseDirectMethodsTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerUseDirectMethodsTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerUseIndexerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerUseIndexerTests.cs index cc0bafdcc..32351a65d 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerUseIndexerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerUseIndexerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerWhereBeforeOrderByTests.cs b/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerWhereBeforeOrderByTests.cs index 7f1a4d9f3..e902c0d42 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerWhereBeforeOrderByTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/OptimizeLinqUsageAnalyzerWhereBeforeOrderByTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/OptimizeStartsWithAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/OptimizeStartsWithAnalyzerTests.cs index 483d79a2e..8197538d7 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/OptimizeStartsWithAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/OptimizeStartsWithAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/OptimizeStringBuilderUsageAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/OptimizeStringBuilderUsageAnalyzerTests.cs index 3144ba2d3..125172cd3 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/OptimizeStringBuilderUsageAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/OptimizeStringBuilderUsageAnalyzerTests.cs @@ -1,9 +1,6 @@ -using System.Collections.Generic; -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/OptionalParametersAttributeAnalyzerMA0087Tests.cs b/tests/Meziantou.Analyzer.Test/Rules/OptionalParametersAttributeAnalyzerMA0087Tests.cs index ef8d81d4b..50fa4ebbc 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/OptionalParametersAttributeAnalyzerMA0087Tests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/OptionalParametersAttributeAnalyzerMA0087Tests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/OptionalParametersAttributeAnalyzerMA0088Tests.cs b/tests/Meziantou.Analyzer.Test/Rules/OptionalParametersAttributeAnalyzerMA0088Tests.cs index 79317ad8d..6283c8214 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/OptionalParametersAttributeAnalyzerMA0088Tests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/OptionalParametersAttributeAnalyzerMA0088Tests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/ParameterAttributeForRazorComponentAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/ParameterAttributeForRazorComponentAnalyzerTests.cs index 98be9fc9b..d0f0fe163 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/ParameterAttributeForRazorComponentAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/ParameterAttributeForRazorComponentAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public sealed class ParameterAttributeForRazorComponentAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/PreferReturningCollectionAbstractionInsteadOfImplementationAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/PreferReturningCollectionAbstractionInsteadOfImplementationAnalyzerTests.cs index 2f9eff6ab..18e78bca6 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/PreferReturningCollectionAbstractionInsteadOfImplementationAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/PreferReturningCollectionAbstractionInsteadOfImplementationAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/PreserveParamsOnOverrideAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/PreserveParamsOnOverrideAnalyzerTests.cs index 85df9fa25..7dbc35f32 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/PreserveParamsOnOverrideAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/PreserveParamsOnOverrideAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/PrimaryConstructorParameterShouldBeReadOnlyAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/PrimaryConstructorParameterShouldBeReadOnlyAnalyzerTests.cs index 109597abe..0bbb2895a 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/PrimaryConstructorParameterShouldBeReadOnlyAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/PrimaryConstructorParameterShouldBeReadOnlyAnalyzerTests.cs @@ -1,8 +1,6 @@ -#if CSHARP12_OR_GREATER -using System.Threading.Tasks; +#if CSHARP12_OR_GREATER using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/ProcessStartAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/ProcessStartAnalyzerTests.cs index 86af3dda2..2bd8279b0 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/ProcessStartAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/ProcessStartAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/RemoveEmptyBlockAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/RemoveEmptyBlockAnalyzerTests.cs index d3ac9b81e..dee74ff53 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/RemoveEmptyBlockAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/RemoveEmptyBlockAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/RemoveEmptyStatementAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/RemoveEmptyStatementAnalyzerTests.cs index e07f182c9..5225dd400 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/RemoveEmptyStatementAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/RemoveEmptyStatementAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/RemoveUselessToStringAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/RemoveUselessToStringAnalyzerTests.cs index f9e5e5844..9f94c0647 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/RemoveUselessToStringAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/RemoveUselessToStringAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/ReplaceEnumToStringWithNameofAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/ReplaceEnumToStringWithNameofAnalyzerTests.cs index 89521f5f5..e2a1650c4 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/ReplaceEnumToStringWithNameofAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/ReplaceEnumToStringWithNameofAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/ReturnTaskFromResultInsteadOfReturningNullAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/ReturnTaskFromResultInsteadOfReturningNullAnalyzerTests.cs index beb309f03..65829c319 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/ReturnTaskFromResultInsteadOfReturningNullAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/ReturnTaskFromResultInsteadOfReturningNullAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/SequenceNumberMustBeAConstantAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/SequenceNumberMustBeAConstantAnalyzerTests.cs index 2c81091a7..a42e7b721 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/SequenceNumberMustBeAConstantAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/SequenceNumberMustBeAConstantAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public sealed class SequenceNumberMustBeAConstantAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/SimplifyCallerArgumentExpressionAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/SimplifyCallerArgumentExpressionAnalyzerTests.cs index 41892daff..db6108871 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/SimplifyCallerArgumentExpressionAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/SimplifyCallerArgumentExpressionAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/StringShouldNotContainsNonDeterministicEndOfLineAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/StringShouldNotContainsNonDeterministicEndOfLineAnalyzerTests.cs index 9f2ae2d3f..2a7edb2a9 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/StringShouldNotContainsNonDeterministicEndOfLineAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/StringShouldNotContainsNonDeterministicEndOfLineAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/TaskInUsingAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/TaskInUsingAnalyzerTests.cs index 8d8abd054..ecef195ec 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/TaskInUsingAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/TaskInUsingAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Microsoft.CodeAnalysis; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public sealed class TaskInUsingAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/ThrowIfNullWithNonNullableInstanceAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/ThrowIfNullWithNonNullableInstanceAnalyzerTests.cs index 9c95bb25e..d67c79273 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/ThrowIfNullWithNonNullableInstanceAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/ThrowIfNullWithNonNullableInstanceAnalyzerTests.cs @@ -1,9 +1,7 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using Microsoft.CodeAnalysis; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public sealed class ThrowIfNullWithNonNullableInstanceAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/TypeCannotBeUsedInAnAttributeParameterAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/TypeCannotBeUsedInAnAttributeParameterAnalyzerTests.cs index d54f03ee2..dd257e4c9 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/TypeCannotBeUsedInAnAttributeParameterAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/TypeCannotBeUsedInAnAttributeParameterAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public sealed class TypeCannotBeUsedInAnAttributeParameterAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/TypeNameMustNotMatchNamespaceAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/TypeNameMustNotMatchNamespaceAnalyzerTests.cs index 30f67d339..e3c0c11c2 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/TypeNameMustNotMatchNamespaceAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/TypeNameMustNotMatchNamespaceAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/TypesShouldNotExtendSystemApplicationExceptionAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/TypesShouldNotExtendSystemApplicationExceptionAnalyzerTests.cs index cfb3ac995..8c5e35c3d 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/TypesShouldNotExtendSystemApplicationExceptionAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/TypesShouldNotExtendSystemApplicationExceptionAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseAnOverloadThatHasCancellationTokenAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseAnOverloadThatHasCancellationTokenAnalyzerTests.cs index 17a1d0232..2045cd25c 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/UseAnOverloadThatHasCancellationTokenAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseAnOverloadThatHasCancellationTokenAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseAnOverloadThatHasTimeProviderAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseAnOverloadThatHasTimeProviderAnalyzerTests.cs index 230853b92..5d2a1ea32 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/UseAnOverloadThatHasTimeProviderAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseAnOverloadThatHasTimeProviderAnalyzerTests.cs @@ -1,9 +1,7 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using Microsoft.CodeAnalysis; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseArrayEmptyAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseArrayEmptyAnalyzerTests.cs index c913d71df..b20752aec 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/UseArrayEmptyAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseArrayEmptyAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseConfigureAwaitAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseConfigureAwaitAnalyzerTests.cs index f4037ca9e..aa8a7196c 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/UseConfigureAwaitAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseConfigureAwaitAnalyzerTests.cs @@ -1,9 +1,7 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using Microsoft.CodeAnalysis; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseContainsKeyInsteadOfTryGetValueAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseContainsKeyInsteadOfTryGetValueAnalyzerTests.cs index e243ea97b..6a54e6116 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/UseContainsKeyInsteadOfTryGetValueAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseContainsKeyInsteadOfTryGetValueAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public sealed class UseContainsKeyInsteadOfTryGetValueAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseDateTimeUnixEpochAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseDateTimeUnixEpochAnalyzerTests.cs index 0df6798a0..9d0472e08 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/UseDateTimeUnixEpochAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseDateTimeUnixEpochAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public class UseDateTimeUnixEpochAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseEqualsMethodInsteadOfOperatorAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseEqualsMethodInsteadOfOperatorAnalyzerTests.cs index 59c3356ad..1e89f92e6 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/UseEqualsMethodInsteadOfOperatorAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseEqualsMethodInsteadOfOperatorAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public class UseEqualsMethodInsteadOfOperatorAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseEventArgsEmptyAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseEventArgsEmptyAnalyzerTests.cs index c31168c8b..fe2eda71c 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/UseEventArgsEmptyAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseEventArgsEmptyAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseEventHandlerOfTAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseEventHandlerOfTAnalyzerTests.cs index b28f81309..db5f742da 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/UseEventHandlerOfTAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseEventHandlerOfTAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseGuidEmptyAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseGuidEmptyAnalyzerTests.cs index e7c22ade2..982e305f5 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/UseGuidEmptyAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseGuidEmptyAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseIFormatProviderAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseIFormatProviderAnalyzerTests.cs index 5e335e8b7..9b24b4ce6 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/UseIFormatProviderAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseIFormatProviderAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseIsPatternInsteadOfSequenceEqualAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseIsPatternInsteadOfSequenceEqualAnalyzerTests.cs index e700596f3..78b391d0b 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/UseIsPatternInsteadOfSequenceEqualAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseIsPatternInsteadOfSequenceEqualAnalyzerTests.cs @@ -1,9 +1,7 @@ -#if CSHARP11_OR_GREATER -using System.Threading.Tasks; +#if CSHARP11_OR_GREATER using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public sealed class UseIsPatternInsteadOfSequenceEqualAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseJSRuntimeInvokeVoidAsyncWhenReturnValueIsNotUsedTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseJSRuntimeInvokeVoidAsyncWhenReturnValueIsNotUsedTests.cs index 99c36b040..b12d43a69 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/UseJSRuntimeInvokeVoidAsyncWhenReturnValueIsNotUsedTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseJSRuntimeInvokeVoidAsyncWhenReturnValueIsNotUsedTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public class UseJSRuntimeInvokeVoidAsyncWhenReturnValueIsNotUsedTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseLangwordInXmlCommentAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseLangwordInXmlCommentAnalyzerTests.cs index c5ce3b1fc..e9de2f750 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/UseLangwordInXmlCommentAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseLangwordInXmlCommentAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public sealed class UseLangwordInXmlCommentAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseLazyInitializerEnsureInitializeAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseLazyInitializerEnsureInitializeAnalyzerTests.cs index 96aa4a0a5..a1ea3aa74 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/UseLazyInitializerEnsureInitializeAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseLazyInitializerEnsureInitializeAnalyzerTests.cs @@ -1,7 +1,6 @@ using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseOperatingSystemInsteadOfRuntimeInformationAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseOperatingSystemInsteadOfRuntimeInformationAnalyzerTests.cs index aab1e1a09..9eceda2a2 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/UseOperatingSystemInsteadOfRuntimeInformationAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseOperatingSystemInsteadOfRuntimeInformationAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public class UseOperatingSystemInsteadOfRuntimeInformationAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/UsePatternMatchingForEqualityComparisonsAnalyzerHasValueTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UsePatternMatchingForEqualityComparisonsAnalyzerHasValueTests.cs index 9d7be8c96..32529d20e 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/UsePatternMatchingForEqualityComparisonsAnalyzerHasValueTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UsePatternMatchingForEqualityComparisonsAnalyzerHasValueTests.cs @@ -1,6 +1,5 @@ -using Meziantou.Analyzer.Rules; +using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/UsePatternMatchingForEqualityComparisonsAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UsePatternMatchingForEqualityComparisonsAnalyzerTests.cs index 5a8752f94..fee0fbe56 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/UsePatternMatchingForEqualityComparisonsAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UsePatternMatchingForEqualityComparisonsAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseReadOnlyStructForRefReadOnlyParametersAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseReadOnlyStructForRefReadOnlyParametersAnalyzerTests.cs index 15a213338..82d6b7159 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/UseReadOnlyStructForRefReadOnlyParametersAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseReadOnlyStructForRefReadOnlyParametersAnalyzerTests.cs @@ -1,8 +1,6 @@ #if ROSLYN_4_8_OR_GREATER -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseRegexOptionsAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseRegexOptionsAnalyzerTests.cs index 250b6a5cc..252e476bf 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/UseRegexOptionsAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseRegexOptionsAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseRegexSourceGeneratorAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseRegexSourceGeneratorAnalyzerTests.cs index c1cb4d3a1..64eba68f7 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/UseRegexSourceGeneratorAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseRegexSourceGeneratorAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseRegexTimeoutAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseRegexTimeoutAnalyzerTests.cs index eca65067a..496f769f1 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/UseRegexTimeoutAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseRegexTimeoutAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseStringComparerAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseStringComparerAnalyzerTests.cs index 45d68a367..31973b83a 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/UseStringComparerAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseStringComparerAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseStringComparisonAnalyzerNonCultureSensitiveTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseStringComparisonAnalyzerNonCultureSensitiveTests.cs index c6d257eda..953cfb962 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/UseStringComparisonAnalyzerNonCultureSensitiveTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseStringComparisonAnalyzerNonCultureSensitiveTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseStringComparisonAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseStringComparisonAnalyzerTests.cs index 8880928bf..77f7dd7aa 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/UseStringComparisonAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseStringComparisonAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseStringCreateInsteadOfFormattableStringAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseStringCreateInsteadOfFormattableStringAnalyzerTests.cs index bf44cf3ec..a6faf1a45 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/UseStringCreateInsteadOfFormattableStringAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseStringCreateInsteadOfFormattableStringAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseStringEqualsAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseStringEqualsAnalyzerTests.cs index fef97b746..f6826cedc 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/UseStringEqualsAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseStringEqualsAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseStringEqualsInsteadOfIsPatternAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseStringEqualsInsteadOfIsPatternAnalyzerTests.cs index 5e41a9aa0..b5ff14046 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/UseStringEqualsInsteadOfIsPatternAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseStringEqualsInsteadOfIsPatternAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseStructLayoutAttributeAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseStructLayoutAttributeAnalyzerTests.cs index 4b0176384..4583becd0 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/UseStructLayoutAttributeAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseStructLayoutAttributeAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseSystemThreadingLockInsteadOfObjectAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseSystemThreadingLockInsteadOfObjectAnalyzerTests.cs index 94ed5e3cd..d5836c02d 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/UseSystemThreadingLockInsteadOfObjectAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseSystemThreadingLockInsteadOfObjectAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public sealed class UseSystemThreadingLockInsteadOfObjectAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseTaskUnwrapAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseTaskUnwrapAnalyzerTests.cs index 9b0115259..69c48aeab 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/UseTaskUnwrapAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseTaskUnwrapAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/ValidateArgumentsCorrectlyAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/ValidateArgumentsCorrectlyAnalyzerTests.cs index 5c5be2c4f..8c4b5cdae 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/ValidateArgumentsCorrectlyAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/ValidateArgumentsCorrectlyAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Rules/ValidateUnsafeAccessorAttributeUsageAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/ValidateUnsafeAccessorAttributeUsageAnalyzerTests.cs index ed2dff7e2..ba441772e 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/ValidateUnsafeAccessorAttributeUsageAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/ValidateUnsafeAccessorAttributeUsageAnalyzerTests.cs @@ -1,8 +1,6 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; public class ValidateUnsafeAccessorAttributeUsageAnalyzerTests diff --git a/tests/Meziantou.Analyzer.Test/Rules/ValueReturnedByStreamReadShouldBeUsedAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/ValueReturnedByStreamReadShouldBeUsedAnalyzerTests.cs index 18ebd51e2..5fc049223 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/ValueReturnedByStreamReadShouldBeUsedAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/ValueReturnedByStreamReadShouldBeUsedAnalyzerTests.cs @@ -1,7 +1,5 @@ -using System.Threading.Tasks; using Meziantou.Analyzer.Rules; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Rules; diff --git a/tests/Meziantou.Analyzer.Test/Suppressors/CA1507SerializationPropertyNameSuppressorTests.cs b/tests/Meziantou.Analyzer.Test/Suppressors/CA1507SerializationPropertyNameSuppressorTests.cs index 8107c271d..48015548c 100644 --- a/tests/Meziantou.Analyzer.Test/Suppressors/CA1507SerializationPropertyNameSuppressorTests.cs +++ b/tests/Meziantou.Analyzer.Test/Suppressors/CA1507SerializationPropertyNameSuppressorTests.cs @@ -1,9 +1,7 @@ #if ROSLYN_4_10_OR_GREATER -using System.Threading.Tasks; using Meziantou.Analyzer.Suppressors; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Suppressors; public sealed class CA1507SerializationPropertyNameSuppressorTests diff --git a/tests/Meziantou.Analyzer.Test/Suppressors/CA1822DecoratedMethodSuppressorTests.cs b/tests/Meziantou.Analyzer.Test/Suppressors/CA1822DecoratedMethodSuppressorTests.cs index 366e6a7a6..4b8db2552 100644 --- a/tests/Meziantou.Analyzer.Test/Suppressors/CA1822DecoratedMethodSuppressorTests.cs +++ b/tests/Meziantou.Analyzer.Test/Suppressors/CA1822DecoratedMethodSuppressorTests.cs @@ -1,9 +1,7 @@ -#if ROSLYN_4_10_OR_GREATER -using System.Threading.Tasks; +#if ROSLYN_4_10_OR_GREATER using Meziantou.Analyzer.Suppressors; using Meziantou.Analyzer.Test.Helpers; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Suppressors; public sealed class CA1822DecoratedMethodSuppressorTests diff --git a/tests/Meziantou.Analyzer.Test/Suppressors/IDE0058SuppressorTests.cs b/tests/Meziantou.Analyzer.Test/Suppressors/IDE0058SuppressorTests.cs index 9764d8d2c..e8d422d4d 100644 --- a/tests/Meziantou.Analyzer.Test/Suppressors/IDE0058SuppressorTests.cs +++ b/tests/Meziantou.Analyzer.Test/Suppressors/IDE0058SuppressorTests.cs @@ -1,9 +1,7 @@ -#if ROSLYN_4_10_OR_GREATER -using System.Threading.Tasks; +#if ROSLYN_4_10_OR_GREATER using Meziantou.Analyzer.Suppressors; using Microsoft.CodeAnalysis; using TestHelper; -using Xunit; namespace Meziantou.Analyzer.Test.Suppressors; public sealed class IDE0058SuppressorTests From 50d4d1c0b613304116d8fbe2ce1078cec2331786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Wed, 22 Oct 2025 23:10:28 -0400 Subject: [PATCH 12/38] Improve support for culture-sensitive ToString and support null format (#893) --- .../CultureSensitiveFormattingContext.cs | 51 +- .../Rules/UseIFormatProviderAnalyzer.cs | 38 +- .../Rules/UseIFormatProviderAnalyzerTests.cs | 664 +++--------------- 3 files changed, 167 insertions(+), 586 deletions(-) diff --git a/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs b/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs index 8385ee765..1b80e42da 100755 --- a/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs +++ b/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs @@ -5,6 +5,8 @@ namespace Meziantou.Analyzer.Internals; internal sealed class CultureSensitiveFormattingContext(Compilation compilation) { + private readonly HashSet _excludedMethods = CreateExcludedMethods(compilation); + public INamedTypeSymbol? CultureInsensitiveTypeAttributeSymbol { get; } = compilation.GetBestTypeByMetadataName("Meziantou.Analyzer.Annotations.CultureInsensitiveTypeAttribute"); public INamedTypeSymbol? FormatProviderSymbol { get; } = compilation.GetBestTypeByMetadataName("System.IFormatProvider"); public INamedTypeSymbol? CultureInfoSymbol { get; } = compilation.GetBestTypeByMetadataName("System.Globalization.CultureInfo"); @@ -24,6 +26,26 @@ internal sealed class CultureSensitiveFormattingContext(Compilation compilation) public INamedTypeSymbol? SystemIFormattableSymbol { get; } = compilation.GetBestTypeByMetadataName("System.IFormattable"); public INamedTypeSymbol? SystemWindowsFontStretchSymbol { get; } = compilation.GetBestTypeByMetadataName("System.Windows.FontStretch"); public INamedTypeSymbol? SystemWindowsMediaBrushSymbol { get; } = compilation.GetBestTypeByMetadataName("System.Windows.Media.Brush"); + public INamedTypeSymbol? NuGetVersioningSemanticVersionSymbol { get; } = compilation.GetBestTypeByMetadataName("NuGet.Versioning.SemanticVersion"); + + private static HashSet CreateExcludedMethods(Compilation compilation) + { + var result = new HashSet(SymbolEqualityComparer.Default); + AddDocumentationId(result, compilation, "M:System.Convert.ToChar(System.String)"); + AddDocumentationId(result, compilation, "M:System.Convert.ToChar(System.Object)"); + AddDocumentationId(result, compilation, "M:System.Convert.ToBoolean(System.String)"); + AddDocumentationId(result, compilation, "M:System.Convert.ToBoolean(System.Object)"); + return result; + + static void AddDocumentationId(HashSet result, Compilation compilation, string id) + { + foreach (var item in DocumentationCommentId.GetSymbolsForDeclarationId(id, compilation)) + { + result.Add(item); + } + } + } + private static bool MustUnwrapNullableOfT(CultureSensitiveOptions options) { @@ -40,6 +62,9 @@ public bool IsCultureSensitiveOperation(IOperation operation, CultureSensitiveOp if (operation is IInvocationOperation invocation) { + if (_excludedMethods.Contains(invocation.TargetMethod)) + return false; + var methodName = invocation.TargetMethod.Name; if (methodName is "ToString") { @@ -49,7 +74,7 @@ public bool IsCultureSensitiveOperation(IOperation operation, CultureSensitiveOp { foreach (var arg in invocation.Arguments) { - if (arg.Value is { ConstantValue: { HasValue: true, Value: string } }) + if (arg.Value is { ConstantValue: { HasValue: true, Value: string } } or IConversionOperation { Type.SpecialType: SpecialType.System_String, ConstantValue: { HasValue: true, Value: null } }) { if (format is not null) { @@ -252,16 +277,19 @@ private bool IsCultureSensitiveType(ITypeSymbol? typeSymbol, CultureSensitiveOpt if (typeSymbol.IsOrInheritFrom(SystemWindowsMediaBrushSymbol)) return false; + if (typeSymbol.IsOrInheritFrom(NuGetVersioningSemanticVersionSymbol)) + return false; + if (!typeSymbol.Implements(SystemIFormattableSymbol)) return false; - if (!IsCultureSensitiveTypeUsingAttribute(typeSymbol, format: null)) + if (!IsCultureSensitiveTypeUsingAttribute(typeSymbol, hasFormat: false, format: null)) return false; return true; } - private bool IsCultureSensitiveTypeUsingAttribute(ITypeSymbol typeSymbol, string? format) + private bool IsCultureSensitiveTypeUsingAttribute(ITypeSymbol typeSymbol, bool hasFormat, string? format) { var attributes = typeSymbol.GetAttributes(CultureInsensitiveTypeAttributeSymbol); foreach (var attr in attributes) @@ -269,8 +297,11 @@ private bool IsCultureSensitiveTypeUsingAttribute(ITypeSymbol typeSymbol, string if (attr.ConstructorArguments.IsEmpty) return false; // no format is set, so the type is culture insensitive - var attrFormat = attr.ConstructorArguments[0].Value; - if (attrFormat is null || (attrFormat is string attrFormatValue && (string.IsNullOrEmpty(attrFormatValue) || attrFormatValue == format))) + if (!hasFormat) + continue; + + var attrFormat = attr.ConstructorArguments[0].Value as string; + if (attrFormat == format) return false; // no format is set, so the type is culture insensitive } @@ -284,8 +315,11 @@ private bool IsCultureSensitiveTypeUsingAttribute(ITypeSymbol typeSymbol, string if (attribute.ConstructorArguments.Length == 1) return false; - var attrFormat = attribute.ConstructorArguments[1].Value; - if (attrFormat is null || (attrFormat is string attrFormatValue && (string.IsNullOrEmpty(attrFormatValue) || attrFormatValue == format))) + if (!hasFormat) + continue; + + var attrFormat = attribute.ConstructorArguments[1].Value as string; + if (attrFormat == format) return false; // no format is set, so the type is culture insensitive } } @@ -298,6 +332,7 @@ private bool IsCultureSensitiveType(ITypeSymbol? symbol, IOperation? format, IOp if (!IsCultureSensitiveType(symbol, options)) return false; + var hasFormatString = format is { ConstantValue.HasValue: true }; var formatString = format?.ConstantValue.Value as string; if (instance is not null) @@ -320,7 +355,7 @@ private bool IsCultureSensitiveType(ITypeSymbol? symbol, IOperation? format, IOp return false; } - if (symbol is not null && !IsCultureSensitiveTypeUsingAttribute(symbol, formatString)) + if (symbol is not null && !IsCultureSensitiveTypeUsingAttribute(symbol, hasFormatString, formatString)) return false; return true; diff --git a/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs index c0ca66197..2223900f6 100644 --- a/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs @@ -37,25 +37,6 @@ private sealed class AnalyzerContext(Compilation compilation) { private readonly CultureSensitiveFormattingContext _cultureSensitiveContext = new(compilation); private readonly OverloadFinder _overloadFinder = new(compilation); - private readonly HashSet _excludedMethods = CreateExcludedMethods(compilation); - - private static HashSet CreateExcludedMethods(Compilation compilation) - { - var result = new HashSet(SymbolEqualityComparer.Default); - AddDocumentationId(result, compilation, "M:System.Convert.ToChar(System.String)"); - AddDocumentationId(result, compilation, "M:System.Convert.ToChar(System.Object)"); - AddDocumentationId(result, compilation, "M:System.Convert.ToBoolean(System.String)"); - AddDocumentationId(result, compilation, "M:System.Convert.ToBoolean(System.Object)"); - return result; - - static void AddDocumentationId(HashSet result, Compilation compilation, string id) - { - foreach (var item in DocumentationCommentId.GetSymbolsForDeclarationId(id, compilation)) - { - result.Add(item); - } - } - } public void AnalyzeInvocation(OperationAnalysisContext context) { @@ -65,7 +46,7 @@ public void AnalyzeInvocation(OperationAnalysisContext context) if (IsExcludedMethod(context, operation)) return; - + var options = MustUnwrapNullableTypes(context, operation) ? CultureSensitiveOptions.UnwrapNullableOfT : CultureSensitiveOptions.None; if (!_cultureSensitiveContext.IsCultureSensitiveOperation(operation, options)) return; @@ -81,7 +62,11 @@ public void AnalyzeInvocation(OperationAnalysisContext context) var overload = _overloadFinder.FindOverloadWithAdditionalParameterOfType(operation.TargetMethod, operation, includeObsoleteMethods: false, allowOptionalParameters: false, _cultureSensitiveContext.FormatProviderSymbol); if (overload is not null) { - context.ReportDiagnostic(Rule, operation, operation.TargetMethod.Name, _cultureSensitiveContext.FormatProviderSymbol.ToDisplayString()); + if (_cultureSensitiveContext.IsCultureSensitiveOperation(operation, CultureSensitiveOptions.None)) + { + context.ReportDiagnostic(Rule, operation, operation.TargetMethod.Name, _cultureSensitiveContext.FormatProviderSymbol.ToDisplayString()); + } + return; } @@ -108,17 +93,18 @@ public void AnalyzeInvocation(OperationAnalysisContext context) var overload = _overloadFinder.FindOverloadWithAdditionalParameterOfType(operation.TargetMethod, includeObsoleteMethods: false, allowOptionalParameters: false, _cultureSensitiveContext.CultureInfoSymbol); if (overload is not null) { - context.ReportDiagnostic(Rule, operation, operation.TargetMethod.Name, _cultureSensitiveContext.CultureInfoSymbol.ToDisplayString()); + if (_cultureSensitiveContext.IsCultureSensitiveOperation(operation, CultureSensitiveOptions.None)) + { + context.ReportDiagnostic(Rule, operation, operation.TargetMethod.Name, _cultureSensitiveContext.CultureInfoSymbol.ToDisplayString()); + } + return; } } } - private bool IsExcludedMethod(OperationAnalysisContext context, IInvocationOperation operation) + private static bool IsExcludedMethod(OperationAnalysisContext context, IInvocationOperation operation) { - if (_excludedMethods.Contains(operation.TargetMethod)) - return true; - // ToString show culture-sensitive data by default if (operation?.GetContainingMethod(context.CancellationToken)?.Name == "ToString") { diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseIFormatProviderAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseIFormatProviderAnalyzerTests.cs index 9b24b4ce6..c26f983d5 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/UseIFormatProviderAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseIFormatProviderAnalyzerTests.cs @@ -9,38 +9,24 @@ public sealed class UseIFormatProviderAnalyzerTests private static ProjectBuilder CreateProjectBuilder() { return new ProjectBuilder() - .WithAnalyzer(); + .WithAnalyzer() + .WithOutputKind(Microsoft.CodeAnalysis.OutputKind.ConsoleApplication) + .AddMeziantouAttributes(); } [Fact] public async Task Int32ToStringWithCultureInfo_ShouldNotReportDiagnostic() { - const string SourceCode = @" -class TypeName -{ - public void Test() - { - 1.ToString(System.Globalization.CultureInfo.InvariantCulture); - } -}"; await CreateProjectBuilder() - .WithSourceCode(SourceCode) + .WithSourceCode("1.ToString(System.Globalization.CultureInfo.InvariantCulture);") .ValidateAsync(); } [Fact] public async Task Int32ToStringWithoutCultureInfo_ShouldReportDiagnostic() { - const string SourceCode = @" -class TypeName -{ - public void Test() - { - [||](-1).ToString(); - } -}"; await CreateProjectBuilder() - .WithSourceCode(SourceCode) + .WithSourceCode("[||](-1).ToString();") .ShouldReportDiagnosticWithMessage("Use an overload of 'ToString' that has a 'System.IFormatProvider' parameter") .ValidateAsync(); } @@ -48,86 +34,59 @@ await CreateProjectBuilder() [Fact] public async Task Int32_PositiveToStringWithoutCultureInfo_ShouldReportDiagnostic() { - const string SourceCode = @" -class TypeName -{ - public void Test() - { - 1.ToString(); - } -}"; await CreateProjectBuilder() - .WithSourceCode(SourceCode) + .WithSourceCode("1.ToString();") .ValidateAsync(); } [Theory] - [InlineData("x")] - [InlineData("x8")] - [InlineData("X")] - [InlineData("X8")] - [InlineData("B")] - public async Task Int32_InvariantFormat(string format) - { - await CreateProjectBuilder() - .WithSourceCode($$""" - class TypeName - { - public void Test() - { - (-1).ToString("{{format}}"); - } - } - """) - .ValidateAsync(); - } - - [Fact] - public async Task BooleanToStringWithoutCultureInfo_ShouldNotReportDiagnostic() - { - const string SourceCode = @" -class TypeName -{ - public void Test() - { - true.ToString(); - } -}"; - await CreateProjectBuilder() - .WithSourceCode(SourceCode) - .ValidateAsync(); - } - - [Fact] - public async Task SystemGuidToStringWithoutCultureInfo_ShouldNotReportDiagnostic() - { - const string SourceCode = @" -class TypeName -{ - public void Test() - { - default(System.Guid).ToString(); - default(System.Guid).ToString(""D""); - } -}"; - await CreateProjectBuilder() - .WithSourceCode(SourceCode) - .ValidateAsync(); - } - - [Fact] - public async Task SystemTimeSpanToStringWithoutCultureInfo_ShouldNotReportDiagnostic() - { - const string SourceCode = @" -class TypeName -{ - public void Test() - { - System.TimeSpan.Zero.ToString(); - } -}"; - await CreateProjectBuilder() - .WithSourceCode(SourceCode) + [InlineData(""" (-1).ToString("x") """)] + [InlineData(""" (-1).ToString("x8") """)] + [InlineData(""" (-1).ToString("X" )""")] + [InlineData(""" (-1).ToString("X8") """)] + [InlineData(""" (-1).ToString("B") """)] + [InlineData(""" true.ToString() """)] + [InlineData(""" default(System.Guid).ToString() """)] + [InlineData(""" default(System.Guid).ToString("D") """)] + [InlineData(""" System.TimeSpan.Zero.ToString() """)] + [InlineData(""" System.TimeSpan.Zero.ToString("c") """)] + [InlineData(""" System.TimeSpan.Zero.ToString("T") """)] + [InlineData(""" [||]System.TimeSpan.Zero.ToString("G") """)] + [InlineData(""" ' '.ToString(); """)] + [InlineData(""" [||]System.DateTime.TryParse("", out _) """)] + [InlineData(""" [||]System.DateTimeOffset.TryParse("", out _) """)] + [InlineData(""" [||]"".ToLower() """)] + [InlineData(""" [||]new System.Text.StringBuilder().AppendFormat("{0}", 10) """)] + [InlineData(""" System.DayOfWeek.Monday.ToString() """)] + [InlineData(""" default(System.DateTime).ToString("o") """)] + [InlineData(""" default(System.DateTime).ToString("O") """)] + [InlineData(""" default(System.DateTime).ToString("r") """)] + [InlineData(""" default(System.DateTime).ToString("R") """)] + [InlineData(""" default(System.DateTime).ToString("s") """)] + [InlineData(""" default(System.DateTime).ToString("u") """)] + [InlineData(""" default(System.DateTimeOffset).ToString("o") """)] + [InlineData(""" default(System.DateTimeOffset).ToString("O") """)] + [InlineData(""" default(System.DateTimeOffset).ToString("r") """)] + [InlineData(""" default(System.DateTimeOffset).ToString("R") """)] + [InlineData(""" default(System.DateTimeOffset).ToString("s") """)] + [InlineData(""" default(System.DateTimeOffset).ToString("u") """)] + [InlineData(""" [||]default(System.DateTime).ToString("yyyy") """)] + [InlineData(""" System.Guid.Parse("o") """)] + [InlineData(""" System.Guid.TryParse("o", out _) """)] + [InlineData(""" ((int?)1)?.ToString(System.Globalization.CultureInfo.InvariantCulture) """)] + [InlineData(""" string.Format("", "test", 1, 'c') """)] + [InlineData(""" string.Format(default(System.IFormatProvider), "", -1) """)] + [InlineData(""" string.Format("") """)] + [InlineData(""" [||]string.Format("", -1) """)] + [InlineData(""" [||]string.Format("", 0, 0, 0, 0, 0, 0, -1, 0 ,0 ,0, 0) """)] + [InlineData(""" System.Convert.ToChar((object)null) """)] + [InlineData(""" System.Convert.ToChar("") """)] + [InlineData(""" System.Convert.ToBoolean((object)null) """)] + [InlineData(""" System.Convert.ToBoolean("") """)] + public async Task Tests(string expression) + { + await CreateProjectBuilder() + .WithSourceCode(expression + ";") .ValidateAsync(); } @@ -135,96 +94,21 @@ await CreateProjectBuilder() public async Task SystemTimeSpanImplicitToStringWithoutCultureInfo_InterpolatedString_ShouldNotReportDiagnostic() { const string SourceCode = """ - class TypeName - { - public void Test() - { - var timeSpan = System.TimeSpan.FromSeconds(1); - var myString = $"This is a test: {timeSpan}"; - } - } + var timeSpan = System.TimeSpan.FromSeconds(1); + var myString = $"This is a test: {timeSpan}"; """; await CreateProjectBuilder() .WithSourceCode(SourceCode) .ValidateAsync(); } - [Fact] - public async Task SystemTimeSpanToStringWithoutCultureInfo_FormatC_ShouldNotReportDiagnostic() - { - const string SourceCode = @" -class TypeName -{ - public void Test() - { - System.TimeSpan.Zero.ToString(""c""); - } -}"; - await CreateProjectBuilder() - .WithSourceCode(SourceCode) - .ValidateAsync(); - } - - [Fact] - public async Task SystemTimeSpanToStringWithoutCultureInfo_FormatT_ShouldNotReportDiagnostic() - { - const string SourceCode = @" -class TypeName -{ - public void Test() - { - System.TimeSpan.Zero.ToString(""T""); - } -}"; - await CreateProjectBuilder() - .WithSourceCode(SourceCode) - .ValidateAsync(); - } - - [Fact] - public async Task SystemTimeSpanToStringWithoutCultureInfo_FormatG_ShouldReportDiagnostic() - { - const string SourceCode = @" -class TypeName -{ - public void Test() - { - [||]System.TimeSpan.Zero.ToString(""G""); - } -}"; - await CreateProjectBuilder() - .WithSourceCode(SourceCode) - .ValidateAsync(); - } - - [Fact] - public async Task SystemCharToStringWithoutCultureInfo_ShouldNotReportDiagnostic() - { - const string SourceCode = @" -class TypeName -{ - public void Test() - { - ' '.ToString(); - } -}"; - await CreateProjectBuilder() - .WithSourceCode(SourceCode) - .ValidateAsync(); - } - [Fact] public async Task Int32ParseWithoutCultureInfo_ShouldReportDiagnostic() { - const string SourceCode = @" -class TypeName -{ - public void Test() - { - [||]int.Parse(""""); - [||]int.Parse("""", System.Globalization.NumberStyles.Any); - } -}"; + const string SourceCode = """ + [||]int.Parse(""); + [||]int.Parse("", System.Globalization.NumberStyles.Any); + """; await CreateProjectBuilder() .WithSourceCode(SourceCode) .ShouldReportDiagnosticWithMessage("Use an overload of 'Parse' that has a 'System.IFormatProvider' parameter") @@ -235,122 +119,22 @@ await CreateProjectBuilder() [Fact] public async Task SingleTryParseWithoutCultureInfo_ShouldReportDiagnostic() { - const string SourceCode = @" -class TypeName -{ - public void Test() - { - [||]float.TryParse("""", out _); - } -}"; - await CreateProjectBuilder() - .WithSourceCode(SourceCode) - .ShouldReportDiagnosticWithMessage("Use an overload of 'TryParse' that has a 'System.IFormatProvider' parameter") - .ValidateAsync(); - } - - [Fact] - public async Task DateTimeTryParseWithoutCultureInfo_ShouldReportDiagnostic() - { - const string SourceCode = @" -class TypeName -{ - public void Test() - { - [||]System.DateTime.TryParse("""", out _); - } -}"; - await CreateProjectBuilder() - .WithSourceCode(SourceCode) - .ShouldReportDiagnosticWithMessage("Use an overload of 'TryParse' that has a 'System.IFormatProvider' parameter") - .ValidateAsync(); - } - - [Fact] - public async Task DateTimeOffsetTryParseWithoutCultureInfo_ShouldReportDiagnostic() - { - const string SourceCode = @" -class TypeName -{ - public void Test() - { - [||]System.DateTimeOffset.TryParse("""", out _); - } -}"; + const string SourceCode = """ + [||]float.TryParse("", out _); + """; await CreateProjectBuilder() .WithSourceCode(SourceCode) .ShouldReportDiagnosticWithMessage("Use an overload of 'TryParse' that has a 'System.IFormatProvider' parameter") .ValidateAsync(); } - [Fact] - public async Task StringToLower_ShouldReportDiagnostic() - { - const string SourceCode = @" -class TypeName -{ - public void Test() - { - [||]"""".ToLower(); - } -}"; - await CreateProjectBuilder() - .WithSourceCode(SourceCode) - .ShouldReportDiagnosticWithMessage("Use an overload of 'ToLower' that has a 'System.Globalization.CultureInfo' parameter") - .ValidateAsync(); - } - - [Fact] - public async Task StringBuilderAppendFormat_ShouldReportDiagnostic() - { - const string SourceCode = @" -class TypeName -{ - public void Test() - { - [||]new System.Text.StringBuilder().AppendFormat(""{0}"", 10); - } -}"; - await CreateProjectBuilder() - .WithSourceCode(SourceCode) - .ShouldReportDiagnosticWithMessage("Use an overload of 'AppendFormat' that has a 'System.IFormatProvider' parameter") - .ValidateAsync(); - } - - [Fact] - public async Task EnumValueToString() - { - const string SourceCode = @" -class TypeName -{ - public void Test() - { - _ = A.Value1.ToString(); - } -} - -enum A -{ - Value1 -} -"; - await CreateProjectBuilder() - .WithSourceCode(SourceCode) - .ValidateAsync(); - } - [Fact] public async Task EnumToString() { - const string SourceCode = @" -class TypeName -{ - public void Test(System.Enum value) - { - _ = value.ToString(); - } -} -"; + const string SourceCode = """ + System.Enum value = default; + _ = value.ToString(); + """; await CreateProjectBuilder() .WithSourceCode(SourceCode) .ValidateAsync(); @@ -360,15 +144,10 @@ await CreateProjectBuilder() public async Task StringBuilder_AppendLine_AllStringParams() { const string SourceCode = """ -class TypeName -{ - public void Test(System.Text.StringBuilder sb) - { - var str = ""; - sb.AppendLine($"foo{str}var{str}"); - } -} -"""; + var sb = new System.Text.StringBuilder(); + var str = ""; + sb.AppendLine($"foo{str}var{str}"); + """; await CreateProjectBuilder() .WithSourceCode(SourceCode) .ValidateAsync(); @@ -378,16 +157,11 @@ await CreateProjectBuilder() public async Task StringBuilder_AppendLine_AllStringParams_Net7() { const string SourceCode = """ -using System; -class TypeName -{ - public void Test(System.Text.StringBuilder sb) - { - var str = ""; - sb.AppendLine($"foo{str}var{str}{'a'}{Guid.NewGuid()}"); - } -} -"""; + using System; + var sb = new System.Text.StringBuilder(); + var str = ""; + sb.AppendLine($"foo{str}var{str}{'a'}{Guid.NewGuid()}"); + """; await CreateProjectBuilder() .WithTargetFramework(TargetFramework.Net7_0) .WithSourceCode(SourceCode) @@ -399,15 +173,10 @@ await CreateProjectBuilder() public async Task StringBuilder_AppendLine_Int32Params_Net7() { const string SourceCode = """ -class TypeName -{ - public void Test(System.Text.StringBuilder sb) - { - int value = 0; - [||]sb.AppendLine($"foo{value}"); - } -} -"""; + var sb = new System.Text.StringBuilder(); + int value = 0; + [||]sb.AppendLine($"foo{value}"); + """; await CreateProjectBuilder() .WithTargetFramework(TargetFramework.Net7_0) .WithSourceCode(SourceCode) @@ -425,15 +194,10 @@ await CreateProjectBuilder() public async Task StringBuilder_AppendLine_DateTime_InvariantFormat_Net7(string format) { var sourceCode = $$""" -class TypeName -{ - public void Test(System.Text.StringBuilder sb) - { - System.DateTime value = default; - sb.AppendLine($"foo{value:{{format}}}"); - } -} -"""; + var sb = new System.Text.StringBuilder(); + System.DateTime value = default; + sb.AppendLine($"foo{value:{{format}}}"); + """; await CreateProjectBuilder() .WithTargetFramework(TargetFramework.Net7_0) .WithSourceCode(sourceCode) @@ -445,15 +209,10 @@ await CreateProjectBuilder() public async Task StringBuilder_AppendLine_DateTime_Net7() { var sourceCode = """ -class TypeName -{ - public void Test(System.Text.StringBuilder sb) - { - System.DateTime value = default; - [||]sb.AppendLine($"foo{value:yyyy}"); - } -} -"""; + var sb = new System.Text.StringBuilder(); + System.DateTime value = default; + [||]sb.AppendLine($"foo{value:yyyy}"); + """; await CreateProjectBuilder() .WithTargetFramework(TargetFramework.Net7_0) .WithSourceCode(sourceCode) @@ -461,96 +220,13 @@ await CreateProjectBuilder() } #endif - [Fact] - public async Task InvariantDateTimeFormat() - { - const string SourceCode = @" -class TypeName -{ - public void Test() - { - _ = default(System.DateTime).ToString(""o""); - _ = default(System.DateTime).ToString(""O""); - _ = default(System.DateTime).ToString(""r""); - _ = default(System.DateTime).ToString(""R""); - _ = default(System.DateTime).ToString(""s""); - _ = default(System.DateTime).ToString(""u""); - _ = default(System.DateTimeOffset).ToString(""o""); - _ = default(System.DateTimeOffset).ToString(""O""); - _ = default(System.DateTimeOffset).ToString(""r""); - _ = default(System.DateTimeOffset).ToString(""R""); - _ = default(System.DateTimeOffset).ToString(""s""); - _ = default(System.DateTimeOffset).ToString(""u""); - } -} -"; - await CreateProjectBuilder() - .WithSourceCode(SourceCode) - .ValidateAsync(); - } - [Fact] - public async Task DateTimeToString() - { - const string SourceCode = @" -class TypeName -{ - public void Test() - { - _ = [||]default(System.DateTime).ToString(""yyyy""); - } -} -"; - await CreateProjectBuilder() - .WithSourceCode(SourceCode) - .ValidateAsync(); - } - - [Fact] - public async Task GuidParse() - { - const string SourceCode = @" -class TypeName -{ - public void Test() - { - System.Guid.Parse(""o""); - System.Guid.TryParse(""o"", out _); - } -}"; - await CreateProjectBuilder() - .WithTargetFramework(TargetFramework.Net7_0) - .WithSourceCode(SourceCode) - .ValidateAsync(); - } - - [Fact] - public async Task NullableInt32ToStringWithCultureInfo() - { - const string SourceCode = @" -class TypeName -{ - public void Test() - { - ((int?)1)?.ToString(System.Globalization.CultureInfo.InvariantCulture); - } -}"; - await CreateProjectBuilder() - .WithSourceCode(SourceCode) - .ValidateAsync(); - } - [Fact] public async Task NullableInt32ToStringWithoutCultureInfo() { - const string SourceCode = @" -class TypeName -{ - public void Test() - { - int? i = -1; - [||]i.ToString(); - } -}"; + const string SourceCode = """ + int? i = -1; + [||]i.ToString(); + """; await CreateProjectBuilder() .WithSourceCode(SourceCode) .ValidateAsync(); @@ -559,14 +235,9 @@ await CreateProjectBuilder() [Fact] public async Task NullableInt32ToStringWithoutCultureInfo_DisabledConfig() { - const string SourceCode = @" -class TypeName -{ - public void Test() - { - ((int?)1).ToString(); - } -}"; + const string SourceCode = """ + ((int?)1).ToString(); + """; await CreateProjectBuilder() .WithSourceCode(SourceCode) .AddAnalyzerConfiguration("MA0011.consider_nullable_types", "false") @@ -574,118 +245,12 @@ await CreateProjectBuilder() } [Fact] - public async Task StringFormat_ArgsAreNonCultureSensitive() - { - var sourceCode = $$""" -class TypeName -{ - public void Test() - { - _ = string.Format("", "test", 1, 'c'); - } -} -"""; - await CreateProjectBuilder() - .WithSourceCode(sourceCode) - .ValidateAsync(); - } - - [Fact] - public async Task StringFormat_AlreadyHasFormatProvider() + public async Task CultureInsensitiveTypeAttribute_Assembly() { - var sourceCode = $$""" -class TypeName -{ - public void Test() - { - _ = string.Format(default(System.IFormatProvider), "", -1); - } -} -"""; - await CreateProjectBuilder() - .WithSourceCode(sourceCode) - .ValidateAsync(); - } - - [Fact] - public async Task StringFormat_NoArgument() - { - var sourceCode = $$""" -class TypeName -{ - public void Test() - { - _ = string.Format(""); - } -} -"""; - await CreateProjectBuilder() - .WithSourceCode(sourceCode) - .ValidateAsync(); - } - - [Fact] - public async Task StringFormat_Report() - { - var sourceCode = $$""" -class TypeName -{ - public void Test() - { - _ = [||]string.Format("", -1); - } -} -"""; - await CreateProjectBuilder() - .WithSourceCode(sourceCode) - .ValidateAsync(); - } - - [Fact] - public async Task StringFormat_ManyArgs_Report() - { - var sourceCode = $$""" -class TypeName -{ - public void Test() - { - _ = [||]string.Format("", 0, 0, 0, 0, 0, 0, -1, 0 ,0 ,0, 0); - } -} -"""; - await CreateProjectBuilder() - .WithSourceCode(sourceCode) - .ValidateAsync(); - } - - [Fact] - public async Task Convert_ToChar_Object() - { - var sourceCode = $$""" -class TypeName -{ - public void Test() - { - _ = System.Convert.ToChar((object)null); - } -} -"""; - await CreateProjectBuilder() - .WithSourceCode(sourceCode) - .ValidateAsync(); - } - - [Fact] - public async Task Convert_ToChar_String() - { - var sourceCode = $$""" -class TypeName -{ - public void Test() - { - _ = System.Convert.ToChar(""); - } -} + var sourceCode = """ +[assembly: Meziantou.Analyzer.Annotations.CultureInsensitiveTypeAttribute(typeof(System.DateTime))] +_ = new System.DateTime().ToString(); +_ = new System.DateTime().ToString("whatever"); """; await CreateProjectBuilder() .WithSourceCode(sourceCode) @@ -693,16 +258,15 @@ await CreateProjectBuilder() } [Fact] - public async Task Convert_ToBoolean_Object() - { - var sourceCode = $$""" -class TypeName -{ - public void Test() + public async Task CultureInsensitiveTypeAttribute_Assembly_Format() { - _ = System.Convert.ToBoolean((object)null); - } -} + var sourceCode = """ +[assembly: Meziantou.Analyzer.Annotations.CultureInsensitiveTypeAttribute(typeof(System.DateTime), "custom")] +[assembly: Meziantou.Analyzer.Annotations.CultureInsensitiveTypeAttribute(typeof(System.DateTime), "")] +[assembly: Meziantou.Analyzer.Annotations.CultureInsensitiveTypeAttribute(typeof(System.DateTime), null)] +_ = new System.DateTime().ToString("custom"); +_ = new System.DateTime().ToString(""); +_ = [|new System.DateTime().ToString("dummy")|]; """; await CreateProjectBuilder() .WithSourceCode(sourceCode) @@ -710,16 +274,12 @@ await CreateProjectBuilder() } [Fact] - public async Task Convert_ToBoolean_String() - { - var sourceCode = $$""" -class TypeName -{ - public void Test() + public async Task CultureInsensitiveTypeAttribute_Assembly_Format_null1() { - _ = System.Convert.ToBoolean(""); - } -} + var sourceCode = """ +[assembly: Meziantou.Analyzer.Annotations.CultureInsensitiveTypeAttribute(typeof(System.DateTime), null)] +_ = [|new System.DateTime().ToString("dummy")|]; +_ = new System.DateTime().ToString(format: null); """; await CreateProjectBuilder() .WithSourceCode(sourceCode) From e17b7630450225711d9a618de76ca9b960792615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Thu, 23 Oct 2025 21:09:17 -0400 Subject: [PATCH 13/38] Enhance culture-sensitive formatting analysis --- .../CultureSensitiveFormattingContext.cs | 3 +++ .../Internals/OperationExtensions.cs | 17 +++++++++++++++-- .../Internals/TypeSymbolExtensions.cs | 2 +- .../Rules/UseIFormatProviderAnalyzer.cs | 6 ++++++ .../Rules/UseIFormatProviderAnalyzerTests.cs | 17 +++++++++++++++++ 5 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs b/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs index 1b80e42da..ff015a02b 100755 --- a/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs +++ b/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs @@ -68,6 +68,9 @@ public bool IsCultureSensitiveOperation(IOperation operation, CultureSensitiveOp var methodName = invocation.TargetMethod.Name; if (methodName is "ToString") { + if (invocation.HasArgumentOfType(FormatProviderSymbol, inherits: true)) + return false; + // Try get the format. Most of ToString have only 1 string parameter to define the format IOperation? format = null; if (invocation.Arguments.Length > 0) diff --git a/src/Meziantou.Analyzer/Internals/OperationExtensions.cs b/src/Meziantou.Analyzer/Internals/OperationExtensions.cs index 4e0174886..a4dfbe883 100644 --- a/src/Meziantou.Analyzer/Internals/OperationExtensions.cs +++ b/src/Meziantou.Analyzer/Internals/OperationExtensions.cs @@ -119,11 +119,24 @@ public static IOperation UnwrapConversionOperations(this IOperation operation) return operation; } - public static bool HasArgumentOfType(this IInvocationOperation operation, ITypeSymbol argumentTypeSymbol) + public static bool HasArgumentOfType(this IInvocationOperation operation, ITypeSymbol? argumentTypeSymbol, bool inherits = false) { + if (argumentTypeSymbol is null) + return false; + + if (inherits && argumentTypeSymbol.IsSealed) + { + inherits = false; + } + foreach (var arg in operation.Arguments) { - if (argumentTypeSymbol.IsEqualTo(arg.Value.Type)) + if (inherits) + { + if (arg.Value.Type is not null && arg.Value.Type.IsOrInheritFrom(argumentTypeSymbol)) + return true; + } + else if (argumentTypeSymbol.IsEqualTo(arg.Value.Type)) return true; } diff --git a/src/Meziantou.Analyzer/Internals/TypeSymbolExtensions.cs b/src/Meziantou.Analyzer/Internals/TypeSymbolExtensions.cs index 98ca2e8d1..e92414c4c 100755 --- a/src/Meziantou.Analyzer/Internals/TypeSymbolExtensions.cs +++ b/src/Meziantou.Analyzer/Internals/TypeSymbolExtensions.cs @@ -100,7 +100,7 @@ public static bool IsOrInheritFrom(this ITypeSymbol symbol, ITypeSymbol? expecte if (expectedType is null) return false; - return symbol.IsEqualTo(expectedType) || symbol.InheritsFrom(expectedType); + return symbol.IsEqualTo(expectedType) || (!expectedType.IsSealed && symbol.InheritsFrom(expectedType)); } public static bool IsEqualToAny(this ITypeSymbol? symbol, params ITypeSymbol?[]? expectedTypes) diff --git a/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs index 2223900f6..adce88830 100644 --- a/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs @@ -86,6 +86,12 @@ public void AnalyzeInvocation(OperationAnalysisContext context) return; } } + + if (operation.Arguments.IsEmpty && targetMethodType.Implements(_cultureSensitiveContext.SystemIFormattableSymbol) && _overloadFinder.HasOverloadWithAdditionalParameterOfType(operation.TargetMethod, operation, _cultureSensitiveContext.FormatProviderSymbol, compilation.GetSpecialType(SpecialType.System_String))) + { + context.ReportDiagnostic(Rule, operation, operation.TargetMethod.Name, _cultureSensitiveContext.FormatProviderSymbol.ToDisplayString()); + return; + } } if (_cultureSensitiveContext.CultureInfoSymbol is not null && !operation.HasArgumentOfType(_cultureSensitiveContext.CultureInfoSymbol)) diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseIFormatProviderAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseIFormatProviderAnalyzerTests.cs index c26f983d5..f2106efcd 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/UseIFormatProviderAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseIFormatProviderAnalyzerTests.cs @@ -280,6 +280,23 @@ public async Task CultureInsensitiveTypeAttribute_Assembly_Format_null1() [assembly: Meziantou.Analyzer.Annotations.CultureInsensitiveTypeAttribute(typeof(System.DateTime), null)] _ = [|new System.DateTime().ToString("dummy")|]; _ = new System.DateTime().ToString(format: null); +"""; + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ToString_IFormattable() + { + var sourceCode = """ +_ = [|new Sample().ToString()|]; + +class Sample : System.IFormattable +{ + public override string ToString() => throw null; + public string ToString(string format, System.IFormatProvider formatProvider) => throw null; +} """; await CreateProjectBuilder() .WithSourceCode(sourceCode) From b1826a70e0f448f3fb5b822a3c41aa7100238e94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Thu, 23 Oct 2025 21:35:53 -0400 Subject: [PATCH 14/38] Add missing nullable annotation --- .../CultureInsensitiveTypeAttribute.cs | 6 +++--- .../Meziantou.Analyzer.Annotations.csproj | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Meziantou.Analyzer.Annotations/CultureInsensitiveTypeAttribute.cs b/src/Meziantou.Analyzer.Annotations/CultureInsensitiveTypeAttribute.cs index d25bded94..ef777ee82 100644 --- a/src/Meziantou.Analyzer.Annotations/CultureInsensitiveTypeAttribute.cs +++ b/src/Meziantou.Analyzer.Annotations/CultureInsensitiveTypeAttribute.cs @@ -2,7 +2,7 @@ namespace Meziantou.Analyzer.Annotations; /// -/// Indicates that the type is culture insensitive. This can be used to suppress rules such as MA0075, MA0076 or MA0107. +/// Indicates that the type is culture insensitive. This can be used to suppress rules such as MA0075, MA0076. /// [CultureInsensitiveType]class Foo { } /// [assembly: CultureInsensitiveType(typeof(Foo))] /// @@ -11,9 +11,9 @@ namespace Meziantou.Analyzer.Annotations; public sealed class CultureInsensitiveTypeAttribute : System.Attribute { public CultureInsensitiveTypeAttribute() { } - public CultureInsensitiveTypeAttribute(string format) => Format = format; + public CultureInsensitiveTypeAttribute(string? format) => Format = format; public CultureInsensitiveTypeAttribute(System.Type type) => Type = type; - public CultureInsensitiveTypeAttribute(System.Type type, string format) + public CultureInsensitiveTypeAttribute(System.Type type, string? format) => (Type, Format) = (type, format); public Type? Type { get; } diff --git a/src/Meziantou.Analyzer.Annotations/Meziantou.Analyzer.Annotations.csproj b/src/Meziantou.Analyzer.Annotations/Meziantou.Analyzer.Annotations.csproj index 46d6cd038..88155ab08 100644 --- a/src/Meziantou.Analyzer.Annotations/Meziantou.Analyzer.Annotations.csproj +++ b/src/Meziantou.Analyzer.Annotations/Meziantou.Analyzer.Annotations.csproj @@ -2,8 +2,7 @@ netstandard2.0 - 1.1.0 - 1.1.0 + 1.1.1 Annotations to configure Meziantou.Analyzer Meziantou.Analyzer, analyzers True From 63ab35a4aff207ec8b660c1815337e9209435cbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Thu, 23 Oct 2025 21:45:44 -0400 Subject: [PATCH 15/38] Update issue documentation --- README.md | 2 +- docs/README.md | 6 +++--- docs/Rules/MA0107.md | 8 ++------ .../configuration/default.editorconfig | 2 +- .../configuration/none.editorconfig | 2 +- .../DoNotUseImplicitCultureSensitiveToStringAnalyzer.cs | 4 ++-- 6 files changed, 10 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 9d141deba..12cd188a9 100755 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ If you are already using other analyzers, you can check [which rules are duplica |[MA0104](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0104.md)|Design|Do not create a type with a name from the BCL|⚠️|❌|❌| |[MA0105](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0105.md)|Performance|Use the lambda parameters instead of using a closure|ℹ️|✔️|❌| |[MA0106](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0106.md)|Performance|Avoid closure by using an overload with the 'factoryArgument' parameter|ℹ️|✔️|❌| -|[MA0107](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0107.md)|Design|Do not use culture-sensitive object.ToString|ℹ️|❌|❌| +|[MA0107](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0107.md)|Design|Do not use object.ToString|ℹ️|❌|❌| |[MA0108](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0108.md)|Usage|Remove redundant argument value|ℹ️|✔️|✔️| |[MA0109](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0109.md)|Design|Consider adding an overload with a Span\ or Memory\|ℹ️|❌|❌| |[MA0110](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0110.md)|Performance|Use the Regex source generator|ℹ️|✔️|✔️| diff --git a/docs/README.md b/docs/README.md index 40e15d7e6..dd04c0b69 100755 --- a/docs/README.md +++ b/docs/README.md @@ -106,7 +106,7 @@ |[MA0104](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0104.md)|Design|Do not create a type with a name from the BCL|⚠️|❌|❌| |[MA0105](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0105.md)|Performance|Use the lambda parameters instead of using a closure|ℹ️|✔️|❌| |[MA0106](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0106.md)|Performance|Avoid closure by using an overload with the 'factoryArgument' parameter|ℹ️|✔️|❌| -|[MA0107](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0107.md)|Design|Do not use culture-sensitive object.ToString|ℹ️|❌|❌| +|[MA0107](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0107.md)|Design|Do not use object.ToString|ℹ️|❌|❌| |[MA0108](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0108.md)|Usage|Remove redundant argument value|ℹ️|✔️|✔️| |[MA0109](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0109.md)|Design|Consider adding an overload with a Span\ or Memory\|ℹ️|❌|❌| |[MA0110](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0110.md)|Performance|Use the Regex source generator|ℹ️|✔️|✔️| @@ -503,7 +503,7 @@ dotnet_diagnostic.MA0105.severity = suggestion # MA0106: Avoid closure by using an overload with the 'factoryArgument' parameter dotnet_diagnostic.MA0106.severity = suggestion -# MA0107: Do not use culture-sensitive object.ToString +# MA0107: Do not use object.ToString dotnet_diagnostic.MA0107.severity = none # MA0108: Remove redundant argument value @@ -1032,7 +1032,7 @@ dotnet_diagnostic.MA0105.severity = none # MA0106: Avoid closure by using an overload with the 'factoryArgument' parameter dotnet_diagnostic.MA0106.severity = none -# MA0107: Do not use culture-sensitive object.ToString +# MA0107: Do not use object.ToString dotnet_diagnostic.MA0107.severity = none # MA0108: Remove redundant argument value diff --git a/docs/Rules/MA0107.md b/docs/Rules/MA0107.md index 2dd57b0b7..29bf43759 100644 --- a/docs/Rules/MA0107.md +++ b/docs/Rules/MA0107.md @@ -1,10 +1,8 @@ -# MA0107 - Do not use culture-sensitive object.ToString +# MA0107 - Do not use object.ToString ````csharp object a = 10; -_ = a.ToString(); // not-compliant -_ = string.Format(CultureInfo.Invariant, "{0}", a); // compliant -_ = FormattableString.Invariant($"{a}"); // compliant +_ = a.ToString(); // not-compliant as it uses the default object.ToString() implementation ```` ## Configuration @@ -13,5 +11,3 @@ _ = FormattableString.Invariant($"{a}"); // compliant # Exclude ToString methods from analysis MA0107.exclude_tostring_methods=true ```` - -You can also annotate a type with `[Meziantou.Analyzer.Annotations.CultureInsensitiveTypeAttribute]` to disable the rule for this type. diff --git a/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig b/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig index b3dd70576..4995e49d0 100644 --- a/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig +++ b/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig @@ -317,7 +317,7 @@ dotnet_diagnostic.MA0105.severity = suggestion # MA0106: Avoid closure by using an overload with the 'factoryArgument' parameter dotnet_diagnostic.MA0106.severity = suggestion -# MA0107: Do not use culture-sensitive object.ToString +# MA0107: Do not use object.ToString dotnet_diagnostic.MA0107.severity = none # MA0108: Remove redundant argument value diff --git a/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig b/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig index 03c7f6b3f..8172a7306 100644 --- a/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig +++ b/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig @@ -317,7 +317,7 @@ dotnet_diagnostic.MA0105.severity = none # MA0106: Avoid closure by using an overload with the 'factoryArgument' parameter dotnet_diagnostic.MA0106.severity = none -# MA0107: Do not use culture-sensitive object.ToString +# MA0107: Do not use object.ToString dotnet_diagnostic.MA0107.severity = none # MA0108: Remove redundant argument value diff --git a/src/Meziantou.Analyzer/Rules/DoNotUseImplicitCultureSensitiveToStringAnalyzer.cs b/src/Meziantou.Analyzer/Rules/DoNotUseImplicitCultureSensitiveToStringAnalyzer.cs index c35da46ec..6a3807808 100644 --- a/src/Meziantou.Analyzer/Rules/DoNotUseImplicitCultureSensitiveToStringAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/DoNotUseImplicitCultureSensitiveToStringAnalyzer.cs @@ -33,8 +33,8 @@ public sealed class DoNotUseImplicitCultureSensitiveToStringAnalyzer : Diagnosti private static readonly DiagnosticDescriptor ObjectToStringRule = new( RuleIdentifiers.DoNotUseCultureSensitiveObjectToString, - title: "Do not use culture-sensitive object.ToString", - messageFormat: "Do not use culture-sensitive object.ToString", + title: "Do not use object.ToString", + messageFormat: "Do not use object.ToString", RuleCategories.Design, DiagnosticSeverity.Info, isEnabledByDefault: false, From c5ff3361ddf02ef10381770a646c8e4f6e4eea30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Fri, 24 Oct 2025 13:07:35 -0400 Subject: [PATCH 16/38] Add support for culture-insensitive default formats (#898) --- .../CultureInsensitiveTypeAttribute.cs | 4 ++ .../Meziantou.Analyzer.Annotations.csproj | 2 +- .../CultureSensitiveFormattingContext.cs | 42 +++++++++++++++++-- ...itCultureSensitiveToStringAnalyzerTests.cs | 42 +++++++++++++++++++ 4 files changed, 86 insertions(+), 4 deletions(-) diff --git a/src/Meziantou.Analyzer.Annotations/CultureInsensitiveTypeAttribute.cs b/src/Meziantou.Analyzer.Annotations/CultureInsensitiveTypeAttribute.cs index ef777ee82..513c55256 100644 --- a/src/Meziantou.Analyzer.Annotations/CultureInsensitiveTypeAttribute.cs +++ b/src/Meziantou.Analyzer.Annotations/CultureInsensitiveTypeAttribute.cs @@ -12,10 +12,14 @@ public sealed class CultureInsensitiveTypeAttribute : System.Attribute { public CultureInsensitiveTypeAttribute() { } public CultureInsensitiveTypeAttribute(string? format) => Format = format; + public CultureInsensitiveTypeAttribute(bool isDefaultFormatCultureInsensitive) => IsDefaultFormatCultureInsensitive = isDefaultFormatCultureInsensitive; public CultureInsensitiveTypeAttribute(System.Type type) => Type = type; public CultureInsensitiveTypeAttribute(System.Type type, string? format) => (Type, Format) = (type, format); + public CultureInsensitiveTypeAttribute(System.Type type, bool isDefaultFormatCultureInsensitive) + => (Type, IsDefaultFormatCultureInsensitive) = (type, isDefaultFormatCultureInsensitive); public Type? Type { get; } public string? Format { get; } + public bool IsDefaultFormatCultureInsensitive { get; } } diff --git a/src/Meziantou.Analyzer.Annotations/Meziantou.Analyzer.Annotations.csproj b/src/Meziantou.Analyzer.Annotations/Meziantou.Analyzer.Annotations.csproj index 88155ab08..f9dd6ba72 100644 --- a/src/Meziantou.Analyzer.Annotations/Meziantou.Analyzer.Annotations.csproj +++ b/src/Meziantou.Analyzer.Annotations/Meziantou.Analyzer.Annotations.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 1.1.1 + 1.2.0 Annotations to configure Meziantou.Analyzer Meziantou.Analyzer, analyzers True diff --git a/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs b/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs index ff015a02b..0f96fd009 100755 --- a/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs +++ b/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs @@ -286,12 +286,36 @@ private bool IsCultureSensitiveType(ITypeSymbol? typeSymbol, CultureSensitiveOpt if (!typeSymbol.Implements(SystemIFormattableSymbol)) return false; - if (!IsCultureSensitiveTypeUsingAttribute(typeSymbol, hasFormat: false, format: null)) + if (!IsCultureSensitiveTypeUsingAttribute(typeSymbol)) return false; return true; } + private bool IsCultureSensitiveTypeUsingAttribute(ITypeSymbol typeSymbol) + { + var attributes = typeSymbol.GetAttributes(CultureInsensitiveTypeAttributeSymbol); + foreach (var attr in attributes) + { + if (attr.ConstructorArguments.IsEmpty) + return false; // no format is set, so the type is culture insensitive + } + + foreach (var attribute in compilation.Assembly.GetAttributes(CultureInsensitiveTypeAttributeSymbol)) + { + if (attribute.ConstructorArguments.IsEmpty) + continue; + + if (attribute.ConstructorArguments[0].Value is INamedTypeSymbol attributeType && attributeType.IsEqualTo(typeSymbol)) + { + if (attribute.ConstructorArguments.Length == 1) + return false; + } + } + + return true; + } + private bool IsCultureSensitiveTypeUsingAttribute(ITypeSymbol typeSymbol, bool hasFormat, string? format) { var attributes = typeSymbol.GetAttributes(CultureInsensitiveTypeAttributeSymbol); @@ -300,10 +324,16 @@ private bool IsCultureSensitiveTypeUsingAttribute(ITypeSymbol typeSymbol, bool h if (attr.ConstructorArguments.IsEmpty) return false; // no format is set, so the type is culture insensitive + var attrValue = attr.ConstructorArguments[0].Value; if (!hasFormat) + { + if (attrValue is bool isDefaultFormatCultureInsensitive && isDefaultFormatCultureInsensitive) + return false; + continue; + } - var attrFormat = attr.ConstructorArguments[0].Value as string; + var attrFormat = attrValue as string; if (attrFormat == format) return false; // no format is set, so the type is culture insensitive } @@ -318,10 +348,16 @@ private bool IsCultureSensitiveTypeUsingAttribute(ITypeSymbol typeSymbol, bool h if (attribute.ConstructorArguments.Length == 1) return false; + var attrValue = attribute.ConstructorArguments[1].Value; if (!hasFormat) + { + if (attrValue is bool isDefaultFormatCultureInsensitive && isDefaultFormatCultureInsensitive) + return false; + continue; + } - var attrFormat = attribute.ConstructorArguments[1].Value as string; + var attrFormat = attrValue as string; if (attrFormat == format) return false; // no format is set, so the type is culture insensitive } diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseImplicitCultureSensitiveToStringAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseImplicitCultureSensitiveToStringAnalyzerTests.cs index ddb800385..ed0a02749 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseImplicitCultureSensitiveToStringAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseImplicitCultureSensitiveToStringAnalyzerTests.cs @@ -416,6 +416,48 @@ await CreateProjectBuilder() .ValidateAsync(); } + [Theory] + [InlineData(""" $"abc{new System.DateTime()}" """)] + [InlineData(""" $"abc[|{new System.DateTime():a}|]" """)] + public async Task IgnoreTypeUsingAssemblyAttribute_WithFormat_DefaultFormatInvariant(string content) + { + var sourceCode = $$""" +[assembly: Meziantou.Analyzer.Annotations.CultureInsensitiveTypeAttribute(typeof(System.DateTime), isDefaultFormatCultureInsensitive: true)] + +class Test +{ + void A() + { + _ = {{content}}; + } +} +"""; + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Theory] + [InlineData(""" $"abc[|{new System.DateTime()}|]" """)] + [InlineData(""" $"abc[|{new System.DateTime():a}|]" """)] + public async Task IgnoreTypeUsingAssemblyAttribute_WithFormat_DefaultFormatCultureSensitive(string content) + { + var sourceCode = $$""" +[assembly: Meziantou.Analyzer.Annotations.CultureInsensitiveTypeAttribute(typeof(System.DateTime), isDefaultFormatCultureInsensitive: false)] + +class Test +{ + void A() + { + _ = {{content}}; + } +} +"""; + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + [Fact] public async Task IgnoreTypeUsingAssemblyAttribute_WithFormatNotMatchingAttribute() { From 74d1adbf7a6f67d0a41550c53fa77dcde3bbecb8 Mon Sep 17 00:00:00 2001 From: Gert Driesen Date: Sat, 25 Oct 2025 16:57:43 +0200 Subject: [PATCH 17/38] Document members of CultureInsensitiveTypeAttribute. (#902) --- .../CultureInsensitiveTypeAttribute.cs | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/Meziantou.Analyzer.Annotations/CultureInsensitiveTypeAttribute.cs b/src/Meziantou.Analyzer.Annotations/CultureInsensitiveTypeAttribute.cs index 513c55256..f103b8860 100644 --- a/src/Meziantou.Analyzer.Annotations/CultureInsensitiveTypeAttribute.cs +++ b/src/Meziantou.Analyzer.Annotations/CultureInsensitiveTypeAttribute.cs @@ -10,16 +10,84 @@ namespace Meziantou.Analyzer.Annotations; [System.AttributeUsage(System.AttributeTargets.Assembly | System.AttributeTargets.Struct | System.AttributeTargets.Class, AllowMultiple = true, Inherited = false)] public sealed class CultureInsensitiveTypeAttribute : System.Attribute { + /// + /// Initializes a new instance of the class. + /// + /// + /// This can be applied on a given type to mark all its formats as culture insensitive. + /// public CultureInsensitiveTypeAttribute() { } + /// + /// Initializes a new instance of the class with the specified format. + /// + /// + /// This can be applied on a given type to mark the specified format as culture insensitive for that type. + /// public CultureInsensitiveTypeAttribute(string? format) => Format = format; + + /// + /// Initializes a new instance of the class. + /// + /// When , marks the default format (i.e., the ToString() method) of the type on which it is applied as culture insensitive. + /// + /// This can be applied on a given type to mark the default format (i.e., the ToString() method) as culture insensitive. + /// public CultureInsensitiveTypeAttribute(bool isDefaultFormatCultureInsensitive) => IsDefaultFormatCultureInsensitive = isDefaultFormatCultureInsensitive; + + /// + /// Initializes a new instance of the class with the specified . + /// + /// The for which to mark all formats as culture insensitive + /// + /// This can be applied on an to mark all formats of the specified as culture insensitive. + /// public CultureInsensitiveTypeAttribute(System.Type type) => Type = type; + + /// + /// Initializes a new instance of the class with the specified and format. + /// + /// The for which to mark the specified format as culture insensitive + /// The format to mark as culture insensitive + /// + /// This can be applied on an to mark the format of the specified as culture insensitive. + /// public CultureInsensitiveTypeAttribute(System.Type type, string? format) => (Type, Format) = (type, format); + + /// + /// Initializes a new instance of the class. + /// + /// The for which to mark the ToString() method as culture insensitive + /// When , marks the default format (i.e., the ToString() method) of as culture insensitive. + /// + /// This can be applied on an to mark the default format (i.e., the ToString() method) of as culture insensitive. + /// public CultureInsensitiveTypeAttribute(System.Type type, bool isDefaultFormatCultureInsensitive) => (Type, IsDefaultFormatCultureInsensitive) = (type, isDefaultFormatCultureInsensitive); + /// + /// Gets the for which to change the culture insensitivity. + /// + /// + /// The for which to change the culture insensitivity, or to change the culture insensitivity of + /// the type on which the attribute is applied. + /// public Type? Type { get; } + + /// + /// Gets the format for which to change the culture insensitivity. + /// + /// + /// The format for which to change the culture insensitivity. + /// public string? Format { get; } + + /// + /// Gets a value indicating whether the default format (i.e., the ToString() method) is culture insensitive. + /// + /// + /// to mark the default format (i.e., the ToString() method) as culture insensitive; + /// otherwise, . + /// public bool IsDefaultFormatCultureInsensitive { get; } } From 068f2933fae1515f31f0e745b90a5c6803f8955c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Sat, 25 Oct 2025 10:58:02 -0400 Subject: [PATCH 18/38] Bump version from 1.2.0 to 1.2.1 --- .../Meziantou.Analyzer.Annotations.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Meziantou.Analyzer.Annotations/Meziantou.Analyzer.Annotations.csproj b/src/Meziantou.Analyzer.Annotations/Meziantou.Analyzer.Annotations.csproj index f9dd6ba72..3275ccd9b 100644 --- a/src/Meziantou.Analyzer.Annotations/Meziantou.Analyzer.Annotations.csproj +++ b/src/Meziantou.Analyzer.Annotations/Meziantou.Analyzer.Annotations.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 1.2.0 + 1.2.1 Annotations to configure Meziantou.Analyzer Meziantou.Analyzer, analyzers True From 98425d5473a1fead301dbe9364b486e1b69c01a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Sat, 25 Oct 2025 14:12:36 -0400 Subject: [PATCH 19/38] Allow attribute to be defined in multiple projects --- .../CultureSensitiveFormattingContext.cs | 45 ++++++++++++++++--- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs b/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs index 0f96fd009..a47d67e54 100755 --- a/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs +++ b/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs @@ -7,7 +7,6 @@ internal sealed class CultureSensitiveFormattingContext(Compilation compilation) { private readonly HashSet _excludedMethods = CreateExcludedMethods(compilation); - public INamedTypeSymbol? CultureInsensitiveTypeAttributeSymbol { get; } = compilation.GetBestTypeByMetadataName("Meziantou.Analyzer.Annotations.CultureInsensitiveTypeAttribute"); public INamedTypeSymbol? FormatProviderSymbol { get; } = compilation.GetBestTypeByMetadataName("System.IFormatProvider"); public INamedTypeSymbol? CultureInfoSymbol { get; } = compilation.GetBestTypeByMetadataName("System.Globalization.CultureInfo"); public INamedTypeSymbol? NumberStyleSymbol { get; } = compilation.GetBestTypeByMetadataName("System.Globalization.NumberStyles"); @@ -46,7 +45,6 @@ static void AddDocumentationId(HashSet result, Compilation compilation, } } - private static bool MustUnwrapNullableOfT(CultureSensitiveOptions options) { return (options & CultureSensitiveOptions.UnwrapNullableOfT) == CultureSensitiveOptions.UnwrapNullableOfT; @@ -292,17 +290,46 @@ private bool IsCultureSensitiveType(ITypeSymbol? typeSymbol, CultureSensitiveOpt return true; } + private static bool IsCultureSensitiveAttributeSymbol(ITypeSymbol? symbol) + { + // The attribute can be defined in multiple assemblies, so it's identified by its full name only + // Meziantou.Analyzer.Annotations.CultureInsensitiveTypeAttribute + return symbol is + { + Name: "CultureInsensitiveTypeAttribute", + ContainingSymbol: INamespaceSymbol + { + Name: "Annotations", + ContainingSymbol: INamespaceSymbol + { + Name: "Analyzer", + ContainingSymbol: INamespaceSymbol + { + Name: "Meziantou", + ContainingSymbol: INamespaceSymbol { IsGlobalNamespace: true } + } + } + } + }; + } + private bool IsCultureSensitiveTypeUsingAttribute(ITypeSymbol typeSymbol) { - var attributes = typeSymbol.GetAttributes(CultureInsensitiveTypeAttributeSymbol); + var attributes = typeSymbol.GetAttributes(); foreach (var attr in attributes) { + if (!IsCultureSensitiveAttributeSymbol(attr.AttributeClass)) + continue; + if (attr.ConstructorArguments.IsEmpty) return false; // no format is set, so the type is culture insensitive } - foreach (var attribute in compilation.Assembly.GetAttributes(CultureInsensitiveTypeAttributeSymbol)) + foreach (var attribute in compilation.Assembly.GetAttributes()) { + if (!IsCultureSensitiveAttributeSymbol(attribute.AttributeClass)) + continue; + if (attribute.ConstructorArguments.IsEmpty) continue; @@ -318,9 +345,12 @@ private bool IsCultureSensitiveTypeUsingAttribute(ITypeSymbol typeSymbol) private bool IsCultureSensitiveTypeUsingAttribute(ITypeSymbol typeSymbol, bool hasFormat, string? format) { - var attributes = typeSymbol.GetAttributes(CultureInsensitiveTypeAttributeSymbol); + var attributes = typeSymbol.GetAttributes(); foreach (var attr in attributes) { + if (!IsCultureSensitiveAttributeSymbol(attr.AttributeClass)) + continue; + if (attr.ConstructorArguments.IsEmpty) return false; // no format is set, so the type is culture insensitive @@ -338,8 +368,11 @@ private bool IsCultureSensitiveTypeUsingAttribute(ITypeSymbol typeSymbol, bool h return false; // no format is set, so the type is culture insensitive } - foreach (var attribute in compilation.Assembly.GetAttributes(CultureInsensitiveTypeAttributeSymbol)) + foreach (var attribute in compilation.Assembly.GetAttributes()) { + if (!IsCultureSensitiveAttributeSymbol(attribute.AttributeClass)) + continue; + if (attribute.ConstructorArguments.IsEmpty) continue; From 5cd3eeba77f080e01b2f59e3dbb005a9d9bfce64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Sat, 25 Oct 2025 14:24:34 -0400 Subject: [PATCH 20/38] Improve annotation attribute detection --- .../Internals/AnnotationAttributes.cs | 51 +++++++++++++++++++ .../CultureSensitiveFormattingContext.cs | 31 ++--------- .../Rules/DoNotLogClassifiedDataAnalyzer.cs | 4 -- .../Rules/NamedParameterAnalyzer.cs | 38 +++++++------- 4 files changed, 73 insertions(+), 51 deletions(-) create mode 100644 src/Meziantou.Analyzer/Internals/AnnotationAttributes.cs diff --git a/src/Meziantou.Analyzer/Internals/AnnotationAttributes.cs b/src/Meziantou.Analyzer/Internals/AnnotationAttributes.cs new file mode 100644 index 000000000..69281230d --- /dev/null +++ b/src/Meziantou.Analyzer/Internals/AnnotationAttributes.cs @@ -0,0 +1,51 @@ +using Microsoft.CodeAnalysis; + +namespace Meziantou.Analyzer.Internals; + +// The attribute can be defined in multiple assemblies, so it's identified by its full name only +internal static class AnnotationAttributes +{ + public static bool IsCultureSensitiveAttributeSymbol(ITypeSymbol? symbol) + { + // Meziantou.Analyzer.Annotations.CultureInsensitiveTypeAttribute + return symbol is INamedTypeSymbol + { + Name: "CultureInsensitiveTypeAttribute", + ContainingSymbol: INamespaceSymbol + { + Name: "Annotations", + ContainingSymbol: INamespaceSymbol + { + Name: "Analyzer", + ContainingSymbol: INamespaceSymbol + { + Name: "Meziantou", + ContainingSymbol: INamespaceSymbol { IsGlobalNamespace: true } + } + } + } + }; + } + + public static bool IsRequireNamedArgumentAttributeSymbol(ITypeSymbol? symbol) + { + // Meziantou.Analyzer.Annotations.RequireNamedArgumentAttribute + return symbol is INamedTypeSymbol + { + Name: "RequireNamedArgumentAttribute", + ContainingSymbol: INamespaceSymbol + { + Name: "Annotations", + ContainingSymbol: INamespaceSymbol + { + Name: "Analyzer", + ContainingSymbol: INamespaceSymbol + { + Name: "Meziantou", + ContainingSymbol: INamespaceSymbol { IsGlobalNamespace: true } + } + } + } + }; + } +} diff --git a/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs b/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs index a47d67e54..e315a5b19 100755 --- a/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs +++ b/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs @@ -290,35 +290,12 @@ private bool IsCultureSensitiveType(ITypeSymbol? typeSymbol, CultureSensitiveOpt return true; } - private static bool IsCultureSensitiveAttributeSymbol(ITypeSymbol? symbol) - { - // The attribute can be defined in multiple assemblies, so it's identified by its full name only - // Meziantou.Analyzer.Annotations.CultureInsensitiveTypeAttribute - return symbol is - { - Name: "CultureInsensitiveTypeAttribute", - ContainingSymbol: INamespaceSymbol - { - Name: "Annotations", - ContainingSymbol: INamespaceSymbol - { - Name: "Analyzer", - ContainingSymbol: INamespaceSymbol - { - Name: "Meziantou", - ContainingSymbol: INamespaceSymbol { IsGlobalNamespace: true } - } - } - } - }; - } - private bool IsCultureSensitiveTypeUsingAttribute(ITypeSymbol typeSymbol) { var attributes = typeSymbol.GetAttributes(); foreach (var attr in attributes) { - if (!IsCultureSensitiveAttributeSymbol(attr.AttributeClass)) + if (!AnnotationAttributes.IsCultureSensitiveAttributeSymbol(attr.AttributeClass)) continue; if (attr.ConstructorArguments.IsEmpty) @@ -327,7 +304,7 @@ private bool IsCultureSensitiveTypeUsingAttribute(ITypeSymbol typeSymbol) foreach (var attribute in compilation.Assembly.GetAttributes()) { - if (!IsCultureSensitiveAttributeSymbol(attribute.AttributeClass)) + if (!AnnotationAttributes.IsCultureSensitiveAttributeSymbol(attribute.AttributeClass)) continue; if (attribute.ConstructorArguments.IsEmpty) @@ -348,7 +325,7 @@ private bool IsCultureSensitiveTypeUsingAttribute(ITypeSymbol typeSymbol, bool h var attributes = typeSymbol.GetAttributes(); foreach (var attr in attributes) { - if (!IsCultureSensitiveAttributeSymbol(attr.AttributeClass)) + if (!AnnotationAttributes.IsCultureSensitiveAttributeSymbol(attr.AttributeClass)) continue; if (attr.ConstructorArguments.IsEmpty) @@ -370,7 +347,7 @@ private bool IsCultureSensitiveTypeUsingAttribute(ITypeSymbol typeSymbol, bool h foreach (var attribute in compilation.Assembly.GetAttributes()) { - if (!IsCultureSensitiveAttributeSymbol(attribute.AttributeClass)) + if (!AnnotationAttributes.IsCultureSensitiveAttributeSymbol(attribute.AttributeClass)) continue; if (attribute.ConstructorArguments.IsEmpty) diff --git a/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs b/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs index 84251fcd6..3349c5a27 100644 --- a/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs @@ -45,13 +45,9 @@ public AnalyzerContext(Compilation compilation) LoggerExtensionsSymbol = compilation.GetBestTypeByMetadataName("Microsoft.Extensions.Logging.LoggerExtensions"); LoggerMessageSymbol = compilation.GetBestTypeByMetadataName("Microsoft.Extensions.Logging.LoggerMessage"); - StructuredLogFieldAttributeSymbol = compilation.GetBestTypeByMetadataName("Meziantou.Analyzer.Annotations.StructuredLogFieldAttribute"); - DataClassificationAttributeSymbol = compilation.GetBestTypeByMetadataName("Microsoft.Extensions.Compliance.Classification.DataClassificationAttribute"); } - public INamedTypeSymbol? StructuredLogFieldAttributeSymbol { get; private set; } - public INamedTypeSymbol? LoggerSymbol { get; } public INamedTypeSymbol? LoggerExtensionsSymbol { get; } public INamedTypeSymbol? LoggerMessageSymbol { get; } diff --git a/src/Meziantou.Analyzer/Rules/NamedParameterAnalyzer.cs b/src/Meziantou.Analyzer/Rules/NamedParameterAnalyzer.cs index 9fcbbde2d..ee9925c05 100644 --- a/src/Meziantou.Analyzer/Rules/NamedParameterAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/NamedParameterAnalyzer.cs @@ -34,8 +34,6 @@ public override void Initialize(AnalysisContext context) context.RegisterCompilationStartAction(context => { - var callerMustUseNamedArgumentType = context.Compilation.GetBestTypeByMetadataName("Meziantou.Analyzer.Annotations.RequireNamedArgumentAttribute"); - var objectType = context.Compilation.GetSpecialType(SpecialType.System_Object); var taskTokenType = context.Compilation.GetBestTypeByMetadataName("System.Threading.Tasks.Task"); var taskGenericTokenType = context.Compilation.GetBestTypeByMetadataName("System.Threading.Tasks.Task`1"); @@ -63,30 +61,30 @@ public override void Initialize(AnalysisContext context) if (argument.Expression is null) return; - if (callerMustUseNamedArgumentType is not null) + if (IsCallerMustUseNamedArgumentAttribute(syntaxContext, argument)) { - if (IsCallerMustUseNamedArgumentAttribute(syntaxContext, argument, callerMustUseNamedArgumentType)) - { - syntaxContext.ReportDiagnostic(Diagnostic.Create(Rule, syntaxContext.Node.GetLocation(), effectiveSeverity: DiagnosticSeverity.Warning, additionalLocations: null, properties: null)); - return; - } + syntaxContext.ReportDiagnostic(Diagnostic.Create(Rule, syntaxContext.Node.GetLocation(), effectiveSeverity: DiagnosticSeverity.Warning, additionalLocations: null, properties: null)); + return; + } - static bool IsCallerMustUseNamedArgumentAttribute(SyntaxNodeAnalysisContext context, SyntaxNode argument, INamedTypeSymbol callerMustUseNamedArgumentType) + static bool IsCallerMustUseNamedArgumentAttribute(SyntaxNodeAnalysisContext context, SyntaxNode argument) + { + var operation = context.SemanticModel.GetOperation(argument, context.CancellationToken) as IArgumentOperation; + if ((operation?.Parameter) is not null) { - var operation = context.SemanticModel.GetOperation(argument, context.CancellationToken) as IArgumentOperation; - if ((operation?.Parameter) is not null) + var attributes = operation.Parameter.GetAttributes(); + foreach (var attribute in attributes) { - var attribute = operation.Parameter.GetAttribute(callerMustUseNamedArgumentType); - if (attribute is not null) - { - var requireNamedArgument = attribute.ConstructorArguments.Length == 0 || attribute.ConstructorArguments[0].Value is true; - if (requireNamedArgument) - return true; - } - } + if (!AnnotationAttributes.IsRequireNamedArgumentAttributeSymbol(attribute.AttributeClass)) + continue; - return false; + var requireNamedArgument = attribute.ConstructorArguments.Length == 0 || attribute.ConstructorArguments[0].Value is true; + if (requireNamedArgument) + return true; + } } + + return false; } var expression = argument.Expression; From db9b9f304d7783517e40434e74baddc162b2d588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Tue, 28 Oct 2025 22:58:50 -0400 Subject: [PATCH 21/38] Add MA0007.IgnoreCatchAllArm configuration option Introduce a new configuration option `MA0007.IgnoreCatchAllArm` to control diagnostics for missing trailing commas in catch-all arms (`_`) of switch expressions. --- docs/Rules/MA0007.md | 35 +++++++++++++ .../AnalyzerOptionsExtensions.cs | 11 ++++ src/Meziantou.Analyzer/Rules/CommaAnalyzer.cs | 11 +++- .../Rules/CommaAnalyzerTests.cs | 52 +++++++++++++++++++ 4 files changed, 108 insertions(+), 1 deletion(-) diff --git a/docs/Rules/MA0007.md b/docs/Rules/MA0007.md index 88349097d..b38db4ca1 100644 --- a/docs/Rules/MA0007.md +++ b/docs/Rules/MA0007.md @@ -17,3 +17,38 @@ new Sample B = 1, }; ```` + +## Configuration + +### `MA0007.IgnoreCatchAllArm` + +By default, the analyzer reports a diagnostic when a catch-all arm (`_`) in a switch expression is missing a trailing comma. You can disable this behavior by setting `MA0007.IgnoreCatchAllArm` to `true`. + +````csharp +// Default behavior (non-compliant) +_ = value switch +{ + 1 => "one", + _ => "other" // Diagnostic reported +}; + +// With MA0007.IgnoreCatchAllArm = true +_ = value switch +{ + 1 => "one", + _ => "other" // No diagnostic +}; + +// Compliant code +_ = value switch +{ + 1 => "one", + _ => "other", +}; +```` + +**.editorconfig** + +```ini +# Ignore missing trailing comma on catch-all arms in switch expressions +MA0007.IgnoreCatchAllArm = true diff --git a/src/Meziantou.Analyzer/Configurations/AnalyzerOptionsExtensions.cs b/src/Meziantou.Analyzer/Configurations/AnalyzerOptionsExtensions.cs index 62a783b61..4c81147e1 100644 --- a/src/Meziantou.Analyzer/Configurations/AnalyzerOptionsExtensions.cs +++ b/src/Meziantou.Analyzer/Configurations/AnalyzerOptionsExtensions.cs @@ -14,6 +14,11 @@ public static string GetConfigurationValue(this AnalyzerOptions options, SyntaxT return defaultValue; } + public static string GetConfigurationValue(this AnalyzerOptions options, SyntaxNode syntaxNode, string key, string defaultValue) + { + return GetConfigurationValue(options, syntaxNode.SyntaxTree, key, defaultValue); + } + public static string GetConfigurationValue(this AnalyzerOptions options, IOperation operation, string key, string defaultValue) { return GetConfigurationValue(options, operation.Syntax.SyntaxTree, key, defaultValue); @@ -28,6 +33,12 @@ public static bool GetConfigurationValue(this AnalyzerOptions options, SyntaxTre return defaultValue; } + [return: NotNullIfNotNull(nameof(defaultValue))] + public static bool? GetConfigurationValue(this AnalyzerOptions options, SyntaxNode syntaxNode, string key, bool? defaultValue) + { + return GetConfigurationValue(options, syntaxNode.SyntaxTree, key, defaultValue); + } + [return: NotNullIfNotNull(nameof(defaultValue))] public static bool? GetConfigurationValue(this AnalyzerOptions options, SyntaxTree syntaxTree, string key, bool? defaultValue) { diff --git a/src/Meziantou.Analyzer/Rules/CommaAnalyzer.cs b/src/Meziantou.Analyzer/Rules/CommaAnalyzer.cs index 4b6c0cc97..ab43cfe80 100644 --- a/src/Meziantou.Analyzer/Rules/CommaAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/CommaAnalyzer.cs @@ -1,4 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; +using Meziantou.Analyzer.Configurations; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -63,6 +64,14 @@ private void HandleCollectionExpression(SyntaxNodeAnalysisContext context) private void HandleSwitchExpression(SyntaxNodeAnalysisContext context) { var node = (SwitchExpressionSyntax)context.Node; + var options = context.Options.GetConfigurationValue(node, Rule.Id + ".IgnoreCatchAllArm", defaultValue: false); + if (options is true) + { + var last = node.Arms.LastOrDefault(); + if (last is not null && last.Pattern is DiscardPatternSyntax) + return; + } + HandleSeparatedList(context, node, node.Arms); } diff --git a/tests/Meziantou.Analyzer.Test/Rules/CommaAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/CommaAnalyzerTests.cs index a818f4ecb..72d6227e3 100644 --- a/tests/Meziantou.Analyzer.Test/Rules/CommaAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/CommaAnalyzerTests.cs @@ -259,6 +259,58 @@ await CreateProjectBuilder() } #endif + [Fact] + public Task SwitchExpressionWithoutLeadingComma_CatchAll() + => CreateProjectBuilder() + .WithLanguageVersion(Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8) + .WithSourceCode(""" + class TypeName + { + public void Test() + { + _ = 0 switch + { + 1 => 1, + [||]_ => 2 + }; + } + } + """) + .ShouldFixCodeWith(""" + class TypeName + { + public void Test() + { + _ = 0 switch + { + 1 => 1, + _ => 2, + }; + } + } + """) + .ValidateAsync(); + + [Fact] + public Task SwitchExpressionWithoutLeadingComma_IgnoreCatchAll() + => CreateProjectBuilder() + .WithLanguageVersion(Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8) + .AddAnalyzerConfiguration("MA0007.IgnoreCatchAllArm", "true") + .WithSourceCode(""" + class TypeName + { + public void Test() + { + _ = 0 switch + { + 1 => 1, + _ => 2 + }; + } + } + """) + .ValidateAsync(); + [Fact] public Task SwitchExpressionWithoutLeadingComma() => CreateProjectBuilder() From 1a89fdb54d5689ffaaaebbeb9ad325d3a8adeb91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Fri, 31 Oct 2025 17:54:26 -0400 Subject: [PATCH 22/38] Add copilot instructions (#907) --- .github/copilot-instructions.md | 36 +++++++++++++++++++++++ .github/workflows/copilot-setup-steps.yml | 28 ++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 .github/copilot-instructions.md create mode 100644 .github/workflows/copilot-setup-steps.yml diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 000000000..3b1a522fe --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,36 @@ +**Any code you commit SHOULD compile, and new and existing tests related to the change SHOULD pass.** + +You MUST make your best effort to ensure your changes satisfy those criteria before committing. If for any reason you were unable to build or test the changes, you MUST report that. You MUST NOT claim success unless all builds and tests pass as described above. + +Do not complete without checking the relevant code builds and relevant tests still pass after the last edits you make. Do not simply assume that your changes fix test failures you see, actually build and run those tests again to confirm. + +You MUST follow all code-formatting and naming conventions defined in [`.editorconfig`](/.editorconfig). + +In addition to the rules enforced by `.editorconfig`, you SHOULD: + +- Prefer file-scoped namespace declarations and single-line using directives. +- Ensure that the final return statement of a method is on its own line. +- Use pattern matching and switch expressions wherever possible. +- Use `nameof` instead of string literals when referring to member names. +- Always use `is null` or `is not null` instead of `== null` or `!= null`. +- Trust the C# null annotations and don't add null checks when the type system says a value cannot be null. +- Prefer `?.` if applicable (e.g. `scope?.Dispose()`). +- Use `ObjectDisposedException.ThrowIf` where applicable. +- When adding new unit tests, strongly prefer to add them to existing test code files rather than creating new code files. +- When running tests, if possible use filters and check test run counts, or look at test logs, to ensure they actually ran. +- Do not finish work with any tests commented out or disabled that were not previously commented out or disabled. +- Do not update `global.json` file +- When writing tests, do not emit "Act", "Arrange" or "Assert" comments. +- There should be no trailing whitespace in any lines. +- There should be an empty line before the beginning of XML documentation comments if there is code before them. + +## Implementing Roslyn analyzers + +- When creating a new rule, create a new constant in `src/Meziantou.Analyzer/RuleIdentifiers.cs` using the name of the new rule. The value must be unique and incremented from the last rule. +- The analyzers must be under `src/Meziantou.Analyzer/Rules/` +- The code fixers must be under `src/Meziantou.Analyzer.CodeFixers/Rules` +- The tests must be under `tests/Meziantou.Analyzer.Test/Rules` + +The analyzer must use `IOperation` or `ISymbol` to analyze the content. Only fallback to `SyntaxNode` when the other ways are not supported. + +The tests must indicates which part of the snippet must report a diagnostic using the `[|code|]` syntax or `{|id:code|}` syntax. Do not explicitly indicates lines or columns. diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml new file mode 100644 index 000000000..bcbe895af --- /dev/null +++ b/.github/workflows/copilot-setup-steps.yml @@ -0,0 +1,28 @@ +# See https://docs.github.com/en/copilot/customizing-copilot/customizing-the-development-environment-for-copilot-coding-agent +name: "Copilot Setup Steps" +on: + workflow_dispatch: + push: + paths: + - .github/workflows/copilot-setup-steps.yml + pull_request: + paths: + - .github/workflows/copilot-setup-steps.yml + + +jobs: + # The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot. + copilot-setup-steps: + runs-on: ubuntu-latest + + permissions: + contents: read + + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-dotnet@v5 + with: + global-json-file: './global.json' + + - name: Restore solution + run: dotnet restore \ No newline at end of file From bbe4b095c2f723a00d4a0ce53d7599f17bd754f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Fri, 31 Oct 2025 19:09:35 -0400 Subject: [PATCH 23/38] Disable new release when pushing in .github folder --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5c8802e9f..4891cb8cd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ on: - "GenerateDocumentation.cmd" - ".markdownlint.json" - ".github/ISSUE_TEMPLATE/**" - - ".github/FUNDING.yml" + - ".github/*" pull_request: branches: - '*' From b88a2a19ad00a94a4a736c6e7dffa2c834ee88bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Fri, 31 Oct 2025 19:12:02 -0400 Subject: [PATCH 24/38] Update instructions --- .github/copilot-instructions.md | 73 +++++++++++++++++---------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 3b1a522fe..653cbc8a4 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,36 +1,37 @@ -**Any code you commit SHOULD compile, and new and existing tests related to the change SHOULD pass.** - -You MUST make your best effort to ensure your changes satisfy those criteria before committing. If for any reason you were unable to build or test the changes, you MUST report that. You MUST NOT claim success unless all builds and tests pass as described above. - -Do not complete without checking the relevant code builds and relevant tests still pass after the last edits you make. Do not simply assume that your changes fix test failures you see, actually build and run those tests again to confirm. - -You MUST follow all code-formatting and naming conventions defined in [`.editorconfig`](/.editorconfig). - -In addition to the rules enforced by `.editorconfig`, you SHOULD: - -- Prefer file-scoped namespace declarations and single-line using directives. -- Ensure that the final return statement of a method is on its own line. -- Use pattern matching and switch expressions wherever possible. -- Use `nameof` instead of string literals when referring to member names. -- Always use `is null` or `is not null` instead of `== null` or `!= null`. -- Trust the C# null annotations and don't add null checks when the type system says a value cannot be null. -- Prefer `?.` if applicable (e.g. `scope?.Dispose()`). -- Use `ObjectDisposedException.ThrowIf` where applicable. -- When adding new unit tests, strongly prefer to add them to existing test code files rather than creating new code files. -- When running tests, if possible use filters and check test run counts, or look at test logs, to ensure they actually ran. -- Do not finish work with any tests commented out or disabled that were not previously commented out or disabled. -- Do not update `global.json` file -- When writing tests, do not emit "Act", "Arrange" or "Assert" comments. -- There should be no trailing whitespace in any lines. -- There should be an empty line before the beginning of XML documentation comments if there is code before them. - -## Implementing Roslyn analyzers - -- When creating a new rule, create a new constant in `src/Meziantou.Analyzer/RuleIdentifiers.cs` using the name of the new rule. The value must be unique and incremented from the last rule. -- The analyzers must be under `src/Meziantou.Analyzer/Rules/` -- The code fixers must be under `src/Meziantou.Analyzer.CodeFixers/Rules` -- The tests must be under `tests/Meziantou.Analyzer.Test/Rules` - -The analyzer must use `IOperation` or `ISymbol` to analyze the content. Only fallback to `SyntaxNode` when the other ways are not supported. - -The tests must indicates which part of the snippet must report a diagnostic using the `[|code|]` syntax or `{|id:code|}` syntax. Do not explicitly indicates lines or columns. +**Any code you commit SHOULD compile, and new and existing tests related to the change SHOULD pass.** + +You MUST make your best effort to ensure your changes satisfy those criteria before committing. If for any reason you were unable to build or test the changes, you MUST report that. You MUST NOT claim success unless all builds and tests pass as described above. + +Do not complete without checking the relevant code builds and relevant tests still pass after the last edits you make. Do not simply assume that your changes fix test failures you see, actually build and run those tests again to confirm. + +You MUST follow all code-formatting and naming conventions defined in [`.editorconfig`](/.editorconfig). + +In addition to the rules enforced by `.editorconfig`, you SHOULD: + +- Prefer file-scoped namespace declarations and single-line using directives. +- Ensure that the final return statement of a method is on its own line. +- Use pattern matching and switch expressions wherever possible. +- Use `nameof` instead of string literals when referring to member names. +- Always use `is null` or `is not null` instead of `== null` or `!= null`. +- Trust the C# null annotations and don't add null checks when the type system says a value cannot be null. +- Prefer `?.` if applicable (e.g. `scope?.Dispose()`). +- Use `ObjectDisposedException.ThrowIf` where applicable. +- When adding new unit tests, strongly prefer to add them to existing test code files rather than creating new code files. +- When running tests, if possible use filters and check test run counts, or look at test logs, to ensure they actually ran. +- Do not finish work with any tests commented out or disabled that were not previously commented out or disabled. +- Do not update `global.json` file +- When writing tests, do not emit "Act", "Arrange" or "Assert" comments. +- There should be no trailing whitespace in any lines. +- Add a blank line before XML documentation comments (`///`) when they follow other code (methods, properties, fields, etc.). + +## Implementing Roslyn analyzers + +- When creating a new rule, create a new constant in `src/Meziantou.Analyzer/RuleIdentifiers.cs` using the name of the new rule. The value must be unique and incremented from the last rule. +- The analyzers must be under `src/Meziantou.Analyzer/Rules/` +- The code fixers must be under `src/Meziantou.Analyzer.CodeFixers/Rules` +- The tests must be under `tests/Meziantou.Analyzer.Test/Rules` + +The analyzer must use `IOperation` or `ISymbol` to analyze the content. Only fallback to `SyntaxNode` when the other ways are not supported. + +Code snippets in tests must use raw string literals (`"""`) and must be minimized to only include the necessary code to reproduce the issue. Avoid including unnecessary code that does not contribute to the test case. +When reporting a diagnostic, the snippet must use the `[|code|]` syntax or `{|id:code|}` syntax. Do not explicitly indicates lines or columns. From df9c9d59321865014be4432473371419dde825b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Sat, 1 Nov 2025 10:26:35 -0400 Subject: [PATCH 25/38] Update instructions --- .github/copilot-instructions.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 653cbc8a4..d66254290 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -3,6 +3,8 @@ You MUST make your best effort to ensure your changes satisfy those criteria before committing. If for any reason you were unable to build or test the changes, you MUST report that. You MUST NOT claim success unless all builds and tests pass as described above. Do not complete without checking the relevant code builds and relevant tests still pass after the last edits you make. Do not simply assume that your changes fix test failures you see, actually build and run those tests again to confirm. +Also, always run `dotnet run --project src/DocumentationGenerator` to update the markdown documentation after modifying analyzer code or documentation comments. +After running the command, review the changes made to the markdown files and ensure they are accurate and appropriate. You MUST follow all code-formatting and naming conventions defined in [`.editorconfig`](/.editorconfig). From 8a38375cf5c59d91cfdb84ab1c329b8c8a7e7b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Sat, 1 Nov 2025 10:57:28 -0400 Subject: [PATCH 26/38] Add git diff when completing with changes --- src/DocumentationGenerator/Program.cs | 29 +++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/DocumentationGenerator/Program.cs b/src/DocumentationGenerator/Program.cs index d6c7aad06..19453c287 100644 --- a/src/DocumentationGenerator/Program.cs +++ b/src/DocumentationGenerator/Program.cs @@ -4,6 +4,7 @@ #pragma warning disable MA0009 using System.Text.Encodings.Web; using System.Text.RegularExpressions; +using System.Text.Unicode; using Meziantou.Framework; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; @@ -103,15 +104,39 @@ void GenerateFile(FullPath outputPath, Action code) } } +if (fileWritten > 0) +{ + Console.WriteLine($"{fileWritten} file(s) updated."); + Console.WriteLine(); + Console.WriteLine("Changes:"); + + var psi = new System.Diagnostics.ProcessStartInfo + { + FileName = "git", + Arguments = "--no-pager diff", + WorkingDirectory = outputFolder.Value, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, + }; + var process = System.Diagnostics.Process.Start(psi)!; + process.OutputDataReceived += (sender, e) => { if (e.Data is not null) Console.WriteLine(e.Data); }; + process.ErrorDataReceived += (sender, e) => { if (e.Data is not null) Console.WriteLine(e.Data); }; + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + await process.WaitForExitAsync(); +} return fileWritten; void WriteFileIfChanged(FullPath path, string content) { + var encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); content = content.ReplaceLineEndings("\n"); if (!File.Exists(path)) { path.CreateParentDirectory(); - File.WriteAllText(path, content); + File.WriteAllText(path, content, encoding); fileWritten++; return; } @@ -119,7 +144,7 @@ void WriteFileIfChanged(FullPath path, string content) var existingContent = File.ReadAllText(path).ReplaceLineEndings(); if (existingContent != content) { - File.WriteAllText(path, content); + File.WriteAllText(path, content, encoding); fileWritten++; } } From b0d5431743ede52ff52601282c72b7dc0db10a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Sat, 1 Nov 2025 11:04:38 -0400 Subject: [PATCH 27/38] Update instructions --- .github/copilot-instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index d66254290..023fbac37 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -4,7 +4,7 @@ You MUST make your best effort to ensure your changes satisfy those criteria bef Do not complete without checking the relevant code builds and relevant tests still pass after the last edits you make. Do not simply assume that your changes fix test failures you see, actually build and run those tests again to confirm. Also, always run `dotnet run --project src/DocumentationGenerator` to update the markdown documentation after modifying analyzer code or documentation comments. -After running the command, review the changes made to the markdown files and ensure they are accurate and appropriate. +After running the command, review the changes made to the markdown files and ensure they are accurate and appropriate. If you make any changes to the markdown files, you MUST re-run the command to verify that no further changes are necessary. You MUST follow all code-formatting and naming conventions defined in [`.editorconfig`](/.editorconfig). From 9ed6635909e22652d582dda7e8287e41b6facffc Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 1 Nov 2025 11:44:50 -0400 Subject: [PATCH 28/38] Add Roslyn analyzer for multiline XML comments (#908) --- README.md | 1 + docs/README.md | 7 + docs/Rules/MA0177.md | 74 ++++ ...InlineXmlCommentSyntaxWhenPossibleFixer.cs | 78 +++++ .../configuration/default.editorconfig | 3 + .../configuration/none.editorconfig | 3 + src/Meziantou.Analyzer/RuleIdentifiers.cs | 1 + ...ineXmlCommentSyntaxWhenPossibleAnalyzer.cs | 175 ++++++++++ ...lCommentSyntaxWhenPossibleAnalyzerTests.cs | 315 ++++++++++++++++++ 9 files changed, 657 insertions(+) create mode 100644 docs/Rules/MA0177.md create mode 100644 src/Meziantou.Analyzer.CodeFixers/Rules/UseInlineXmlCommentSyntaxWhenPossibleFixer.cs create mode 100644 src/Meziantou.Analyzer/Rules/UseInlineXmlCommentSyntaxWhenPossibleAnalyzer.cs create mode 100644 tests/Meziantou.Analyzer.Test/Rules/UseInlineXmlCommentSyntaxWhenPossibleAnalyzerTests.cs diff --git a/README.md b/README.md index 12cd188a9..2da19c83c 100755 --- a/README.md +++ b/README.md @@ -192,6 +192,7 @@ If you are already using other analyzers, you can check [which rules are duplica |[MA0174](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0174.md)|Style|Record should use explicit 'class' keyword|ℹ️|❌|❌| |[MA0175](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0175.md)|Style|Record should not use explicit 'class' keyword|ℹ️|❌|❌| |[MA0176](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0176.md)|Performance|Optimize guid creation|ℹ️|✔️|✔️| +|[MA0177](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0177.md)|Style|Use single-line XML comment syntax when possible|ℹ️|❌|✔️| diff --git a/docs/README.md b/docs/README.md index dd04c0b69..b908fc630 100755 --- a/docs/README.md +++ b/docs/README.md @@ -176,6 +176,7 @@ |[MA0174](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0174.md)|Style|Record should use explicit 'class' keyword|ℹ️|❌|❌| |[MA0175](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0175.md)|Style|Record should not use explicit 'class' keyword|ℹ️|❌|❌| |[MA0176](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0176.md)|Performance|Optimize guid creation|ℹ️|✔️|✔️| +|[MA0177](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0177.md)|Style|Use single-line XML comment syntax when possible|ℹ️|❌|✔️| |Id|Suppressed rule|Justification| |--|---------------|-------------| @@ -712,6 +713,9 @@ dotnet_diagnostic.MA0175.severity = none # MA0176: Optimize guid creation dotnet_diagnostic.MA0176.severity = suggestion + +# MA0177: Use single-line XML comment syntax when possible +dotnet_diagnostic.MA0177.severity = none ``` # .editorconfig - all rules disabled @@ -1241,4 +1245,7 @@ dotnet_diagnostic.MA0175.severity = none # MA0176: Optimize guid creation dotnet_diagnostic.MA0176.severity = none + +# MA0177: Use single-line XML comment syntax when possible +dotnet_diagnostic.MA0177.severity = none ``` diff --git a/docs/Rules/MA0177.md b/docs/Rules/MA0177.md new file mode 100644 index 000000000..215df7ea8 --- /dev/null +++ b/docs/Rules/MA0177.md @@ -0,0 +1,74 @@ +# MA0177 - Use single-line XML comment syntax when possible + +This rule reports XML documentation comments that span multiple lines but contain only single-line content. Such comments can be more concisely written on a single line for better readability. + +````csharp +/// +/// This is a description +/// +public class Sample { } + +// Should be +/// This is a description +public class Sample { } +```` + +## When to use single-line format + +The analyzer will suggest converting to single-line format when: +- The XML element spans multiple lines +- The element contains only a single line of actual text content (ignoring whitespace) +- The resulting single-line comment would fit within the `max_line_length` configuration (if set) + +## When NOT to use single-line format + +The analyzer will NOT suggest converting when: +- The element already uses single-line format +- The element contains multiple lines of text content +- The element contains CDATA sections +- The element contains nested XML elements (like ``, ``, etc.) +- The single-line version would exceed the `max_line_length` setting + +## Configuration + +The analyzer respects the `max_line_length` setting from your `.editorconfig` file: + +````editorconfig +[*.cs] +max_line_length = 120 +```` + +If the single-line version of the XML comment would exceed this limit, the analyzer will not report a diagnostic. + +## Examples + +````csharp +// Non-compliant: Single-line content on multiple lines +/// +/// Returns the sum of two numbers +/// +public int Add(int a, int b) => a + b; + +// Compliant: Single line +/// Returns the sum of two numbers +public int Add(int a, int b) => a + b; + +// Compliant: Multiple lines of actual content +/// +/// Returns the sum of two numbers. +/// This method handles integer overflow. +/// +public int Add(int a, int b) => a + b; + +// Compliant: Contains nested XML element +/// +/// Returns the sum using method +/// +public int Calculate(int a, int b) => Add(a, b); + +// Compliant: Contains CDATA section +/// +/// ]]> +public void Process() { } +```` diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/UseInlineXmlCommentSyntaxWhenPossibleFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/UseInlineXmlCommentSyntaxWhenPossibleFixer.cs new file mode 100644 index 000000000..d4d65c385 --- /dev/null +++ b/src/Meziantou.Analyzer.CodeFixers/Rules/UseInlineXmlCommentSyntaxWhenPossibleFixer.cs @@ -0,0 +1,78 @@ +using System.Collections.Immutable; +using System.Composition; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Editing; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + +namespace Meziantou.Analyzer.Rules; + +[ExportCodeFixProvider(LanguageNames.CSharp), Shared] +public sealed class UseInlineXmlCommentSyntaxWhenPossibleFixer : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(RuleIdentifiers.UseSingleLineXmlCommentSyntaxWhenPossible); + + public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + var nodeToFix = root?.FindNode(context.Span, getInnermostNodeForTie: true, findInsideTrivia: true); + if (nodeToFix is not XmlElementSyntax elementSyntax) + return; + + var title = "Use single-line XML comment syntax"; + var codeAction = CodeAction.Create( + title, + cancellationToken => Fix(context.Document, elementSyntax, cancellationToken), + equivalenceKey: title); + + context.RegisterCodeFix(codeAction, context.Diagnostics); + } + + private static async Task Fix(Document document, XmlElementSyntax elementSyntax, CancellationToken cancellationToken) + { + var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + + // Extract the text content + var contentText = new StringBuilder(); + foreach (var content in elementSyntax.Content) + { + if (content is XmlTextSyntax textSyntax) + { + foreach (var token in textSyntax.TextTokens) + { + // Skip newline tokens + if (token.IsKind(SyntaxKind.XmlTextLiteralNewLineToken)) + continue; + + var text = token.Text.Trim(); + if (!string.IsNullOrWhiteSpace(text)) + { + if (contentText.Length > 0) + contentText.Append(' '); + contentText.Append(text); + } + } + } + } + + // Create single-line syntax + var elementName = elementSyntax.StartTag.Name; + var attributes = elementSyntax.StartTag.Attributes; + + var newNode = XmlElement( + XmlElementStartTag(elementName, attributes), + SingletonList(XmlText(contentText.ToString())), + XmlElementEndTag(elementName)) + .WithLeadingTrivia(elementSyntax.GetLeadingTrivia()) + .WithTrailingTrivia(elementSyntax.GetTrailingTrivia()); + + editor.ReplaceNode(elementSyntax, newNode); + return editor.GetChangedDocument(); + } +} diff --git a/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig b/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig index 4995e49d0..75332eb90 100644 --- a/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig +++ b/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig @@ -526,3 +526,6 @@ dotnet_diagnostic.MA0175.severity = none # MA0176: Optimize guid creation dotnet_diagnostic.MA0176.severity = suggestion + +# MA0177: Use single-line XML comment syntax when possible +dotnet_diagnostic.MA0177.severity = none diff --git a/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig b/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig index 8172a7306..437df1174 100644 --- a/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig +++ b/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig @@ -526,3 +526,6 @@ dotnet_diagnostic.MA0175.severity = none # MA0176: Optimize guid creation dotnet_diagnostic.MA0176.severity = none + +# MA0177: Use single-line XML comment syntax when possible +dotnet_diagnostic.MA0177.severity = none diff --git a/src/Meziantou.Analyzer/RuleIdentifiers.cs b/src/Meziantou.Analyzer/RuleIdentifiers.cs index 64a7420b3..08fa9ad24 100755 --- a/src/Meziantou.Analyzer/RuleIdentifiers.cs +++ b/src/Meziantou.Analyzer/RuleIdentifiers.cs @@ -177,6 +177,7 @@ internal static class RuleIdentifiers public const string RecordClassDeclarationShouldBeExplicit = "MA0174"; public const string RecordClassDeclarationShouldBeImplicit = "MA0175"; public const string OptimizeGuidCreation = "MA0176"; + public const string UseSingleLineXmlCommentSyntaxWhenPossible = "MA0177"; public static string GetHelpUri(string identifier) { diff --git a/src/Meziantou.Analyzer/Rules/UseInlineXmlCommentSyntaxWhenPossibleAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseInlineXmlCommentSyntaxWhenPossibleAnalyzer.cs new file mode 100644 index 000000000..deb802383 --- /dev/null +++ b/src/Meziantou.Analyzer/Rules/UseInlineXmlCommentSyntaxWhenPossibleAnalyzer.cs @@ -0,0 +1,175 @@ +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Meziantou.Analyzer.Rules; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public sealed class UseInlineXmlCommentSyntaxWhenPossibleAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor Rule = new( + RuleIdentifiers.UseSingleLineXmlCommentSyntaxWhenPossible, + title: "Use single-line XML comment syntax when possible", + messageFormat: "Use single-line XML comment syntax when possible", + RuleCategories.Style, + DiagnosticSeverity.Info, + isEnabledByDefault: false, + description: "", + helpLinkUri: RuleIdentifiers.GetHelpUri(RuleIdentifiers.UseSingleLineXmlCommentSyntaxWhenPossible)); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType, SymbolKind.Method, SymbolKind.Field, SymbolKind.Event, SymbolKind.Property); + } + + private static void AnalyzeSymbol(SymbolAnalysisContext context) + { + var symbol = context.Symbol; + if (symbol.IsImplicitlyDeclared) + return; + + if (symbol is INamedTypeSymbol namedTypeSymbol && (namedTypeSymbol.IsImplicitClass || symbol.Name.Contains('$', StringComparison.Ordinal))) + return; + + foreach (var syntaxReference in symbol.DeclaringSyntaxReferences) + { + var syntax = syntaxReference.GetSyntax(context.CancellationToken); + if (!syntax.HasStructuredTrivia) + continue; + + foreach (var trivia in syntax.GetLeadingTrivia()) + { + var structure = trivia.GetStructure(); + if (structure is null) + continue; + + if (structure is not DocumentationCommentTriviaSyntax documentation) + continue; + + foreach (var childNode in documentation.ChildNodes()) + { + if (childNode is XmlElementSyntax elementSyntax) + { + // Check if element spans multiple lines + var startLine = elementSyntax.StartTag.GetLocation().GetLineSpan().StartLinePosition.Line; + var endLine = elementSyntax.EndTag.GetLocation().GetLineSpan().EndLinePosition.Line; + + if (endLine == startLine) + continue; // Single line, no issue + + // Check if content is single-line (ignoring whitespace) + // Skip if content contains CDATA sections or other non-text elements + var hasCDataOrOtherElements = false; + var meaningfulTextTokenCount = 0; + foreach (var content in elementSyntax.Content) + { + if (content is XmlTextSyntax textSyntax) + { + foreach (var token in textSyntax.TextTokens) + { + // Skip whitespace-only tokens and newline tokens + if (token.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.XmlTextLiteralNewLineToken)) + continue; + + var text = token.Text.Trim(); + if (!string.IsNullOrWhiteSpace(text)) + { + meaningfulTextTokenCount++; + } + } + } + else if (content is XmlCDataSectionSyntax || content is XmlElementSyntax) + { + // Skip elements with CDATA sections or nested elements + hasCDataOrOtherElements = true; + break; + } + } + + // Report diagnostic if content is effectively single-line (0 or 1 meaningful text tokens) + // and doesn't contain CDATA or other nested elements + if (!hasCDataOrOtherElements && meaningfulTextTokenCount <= 1) + { + // Check if the single-line version would fit within max_line_length + if (WouldFitInMaxLineLength(context, elementSyntax)) + { + context.ReportDiagnostic(Diagnostic.Create(Rule, elementSyntax.GetLocation())); + } + } + } + } + } + } + } + + private static bool WouldFitInMaxLineLength(SymbolAnalysisContext context, XmlElementSyntax elementSyntax) + { + // Get max_line_length from .editorconfig + var options = context.Options.AnalyzerConfigOptionsProvider.GetOptions(elementSyntax.SyntaxTree); + if (!options.TryGetValue("max_line_length", out var maxLineLengthValue)) + return true; // No limit configured, allow the change + + if (!int.TryParse(maxLineLengthValue, System.Globalization.NumberStyles.None, System.Globalization.CultureInfo.InvariantCulture, out var maxLineLength) || maxLineLength <= 0) + return true; // Invalid or no limit, allow the change + + // Get the indentation of the current line + var lineSpan = elementSyntax.GetLocation().GetLineSpan(); + var sourceText = elementSyntax.SyntaxTree.GetText(); + var line = sourceText.Lines[lineSpan.StartLinePosition.Line]; + var lineText = line.ToString(); + var indentation = lineText.Length - lineText.TrimStart().Length; + + // Build the single-line content + var contentLength = indentation; + var elementName = elementSyntax.StartTag.Name.LocalName.Text; + var attributes = elementSyntax.StartTag.Attributes; + + // Calculate: "/// " + content + "" + contentLength += 4; // "/// " + contentLength += 1; // "<" + contentLength += elementName.Length; + + // Add attribute lengths + foreach (var attribute in attributes) + { + contentLength += attribute.Span.Length + 1; // +1 for space before attribute + } + + contentLength += 1; // ">" + + // Add text content + var hasContent = false; + foreach (var content in elementSyntax.Content) + { + if (content is XmlTextSyntax textSyntax) + { + foreach (var token in textSyntax.TextTokens) + { + if (token.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.XmlTextLiteralNewLineToken)) + continue; + + var text = token.Text.Trim(); + if (!string.IsNullOrWhiteSpace(text)) + { + if (hasContent) + contentLength += 1; // space separator between multiple text tokens + contentLength += text.Length; + hasContent = true; + } + } + } + } + + contentLength += 2; // "" + + return contentLength <= maxLineLength; + } +} diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseInlineXmlCommentSyntaxWhenPossibleAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseInlineXmlCommentSyntaxWhenPossibleAnalyzerTests.cs new file mode 100644 index 000000000..18ae4a3df --- /dev/null +++ b/tests/Meziantou.Analyzer.Test/Rules/UseInlineXmlCommentSyntaxWhenPossibleAnalyzerTests.cs @@ -0,0 +1,315 @@ +using Meziantou.Analyzer.Rules; +using Meziantou.Analyzer.Test.Helpers; +using TestHelper; + +namespace Meziantou.Analyzer.Test.Rules; + +public sealed class UseInlineXmlCommentSyntaxWhenPossibleAnalyzerTests +{ + private static ProjectBuilder CreateProjectBuilder() + { + return new ProjectBuilder() + .WithAnalyzer() + .WithCodeFixProvider() + .WithTargetFramework(TargetFramework.NetLatest); + } + + [Fact] + public async Task SingleLineDescription_ShouldReportDiagnostic() + { + await CreateProjectBuilder() + .WithSourceCode(""" + /// [| + /// description + /// |] + class Sample { } + """) + .ShouldFixCodeWith(""" + /// description + class Sample { } + """) + .ValidateAsync(); + } + + [Fact] + public async Task MultiLineDescription_ShouldNotReportDiagnostic() + { + await CreateProjectBuilder() + .WithSourceCode(""" + /// + /// description line 1 + /// description line 2 + /// + class Sample { } + """) + .ValidateAsync(); + } + + [Fact] + public async Task AlreadyInline_ShouldNotReportDiagnostic() + { + await CreateProjectBuilder() + .WithSourceCode(""" + /// description + class Sample { } + """) + .ValidateAsync(); + } + + [Fact] + public async Task ParamSingleLine_ShouldReportDiagnostic() + { + await CreateProjectBuilder() + .WithSourceCode(""" + class Sample + { + /// [| + /// The value + /// |] + public void Method(int value) { } + } + """) + .ShouldFixCodeWith(""" + class Sample + { + /// The value + public void Method(int value) { } + } + """) + .ValidateAsync(); + } + + [Fact] + public async Task RemarksSingleLine_ShouldReportDiagnostic() + { + await CreateProjectBuilder() + .WithSourceCode(""" + /// [| + /// This is a remark + /// |] + class Sample { } + """) + .ShouldFixCodeWith(""" + /// This is a remark + class Sample { } + """) + .ValidateAsync(); + } + + [Fact] + public async Task ReturnsSingleLine_ShouldReportDiagnostic() + { + await CreateProjectBuilder() + .WithSourceCode(""" + class Sample + { + /// [| + /// The result + /// |] + public int Method() => 42; + } + """) + .ShouldFixCodeWith(""" + class Sample + { + /// The result + public int Method() => 42; + } + """) + .ValidateAsync(); + } + + [Fact] + public async Task InnerXmlElements_ShouldNotReportDiagnostic() + { + await CreateProjectBuilder() + .WithSourceCode(""" + /// + /// This has + /// code + /// inside + /// + class Sample { } + """) + .ValidateAsync(); + } + + [Fact] + public async Task EmptyContent_ShouldReportDiagnostic() + { + await CreateProjectBuilder() + .WithSourceCode(""" + /// [| + /// |] + class Sample { } + """) + .ShouldFixCodeWith(""" + /// + class Sample { } + """) + .ValidateAsync(); + } + + [Fact] + public async Task TypeParamSingleLine_ShouldReportDiagnostic() + { + await CreateProjectBuilder() + .WithSourceCode(""" + /// [| + /// The type parameter + /// |] + class Sample { } + """) + .ShouldFixCodeWith(""" + /// The type parameter + class Sample { } + """) + .ValidateAsync(); + } + + [Fact] + public async Task ExceptionSingleLine_ShouldReportDiagnostic() + { + await CreateProjectBuilder() + .WithSourceCode(""" + class Sample + { + /// [| + /// Thrown when argument is null + /// |] + public void Method(string value) { } + } + """) + .ShouldFixCodeWith(""" + class Sample + { + /// Thrown when argument is null + public void Method(string value) { } + } + """) + .ValidateAsync(); + } + + [Fact] + public async Task ValueSingleLine_ShouldReportDiagnostic() + { + await CreateProjectBuilder() + .WithSourceCode(""" + class Sample + { + /// [| + /// The property value + /// |] + public int Property { get; set; } + } + """) + .ShouldFixCodeWith(""" + class Sample + { + /// The property value + public int Property { get; set; } + } + """) + .ValidateAsync(); + } + + [Fact] + public async Task ContentOnSameLineAsOpenTag_ShouldReportDiagnostic() + { + await CreateProjectBuilder() + .WithSourceCode(""" + /// [|line 1 + /// |] + class Sample { } + """) + .ShouldFixCodeWith(""" + /// line 1 + class Sample { } + """) + .ValidateAsync(); + } + + [Fact] + public async Task ContentOnSameLineAsOpenTagAndCloseTag_ShouldNotReportDiagnostic() + { + await CreateProjectBuilder() + .WithSourceCode(""" + /// line 1 + /// line 2 + class Sample { } + """) + .ValidateAsync(); + } + + [Fact] + public async Task CDataSection_ShouldNotReportDiagnostic() + { + await CreateProjectBuilder() + .WithSourceCode(""" + /// + class Sample { } + """) + .ValidateAsync(); + } + + [Fact] + public async Task EntityReference_ShouldNotReportDiagnostic() + { + await CreateProjectBuilder() + .WithSourceCode(""" + /// line1 line2 + class Sample { } + """) + .ValidateAsync(); + } + + [Fact] + public async Task MaxLineLength_WouldExceedLimit_ShouldNotReportDiagnostic() + { + await CreateProjectBuilder() + .AddAnalyzerConfiguration("max_line_length", "50") + .WithSourceCode(""" + /// + /// This is a very long description that would exceed the max line length limit + /// + class Sample { } + """) + .ValidateAsync(); + } + + [Fact] + public async Task MaxLineLength_WithinLimit_ShouldReportDiagnostic() + { + await CreateProjectBuilder() + .AddAnalyzerConfiguration("max_line_length", "100") + .WithSourceCode(""" + /// [| + /// Short description + /// |] + class Sample { } + """) + .ShouldFixCodeWith(""" + /// Short description + class Sample { } + """) + .ValidateAsync(); + } + + [Fact] + public async Task MaxLineLength_NotConfigured_ShouldReportDiagnostic() + { + await CreateProjectBuilder() + .WithSourceCode(""" + /// [| + /// This is a very long description that could potentially exceed some line length limit + /// |] + class Sample { } + """) + .ShouldFixCodeWith(""" + /// This is a very long description that could potentially exceed some line length limit + class Sample { } + """) + .ValidateAsync(); + } +} From 6ae9486ae91b2c4343927357764e806cf0ab564c Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 1 Nov 2025 21:10:04 -0400 Subject: [PATCH 29/38] Fix MA0011 not reported for types with ToString(IFormatProvider) but no IFormattable (#910) --- .../CultureSensitiveFormattingContext.cs | 20 ++- .../Internals/OverloadFinder.cs | 146 ++++++++++++------ .../Internals/OverloadOptions.cs | 5 + .../Internals/OverloadParameterType.cs | 5 + ...otUseBlockingCallInAsyncContextAnalyzer.cs | 4 +- ...CaughtExceptionAsInnerExceptionAnalyzer.cs | 2 +- ...verloadThatHasCancellationTokenAnalyzer.cs | 2 +- ...seAnOverloadThatHasTimeProviderAnalyzer.cs | 2 +- .../Rules/UseIFormatProviderAnalyzer.cs | 10 +- .../Rules/UseStringComparerAnalyzer.cs | 8 +- .../Rules/UseStringComparisonAnalyzer.cs | 2 +- .../Rules/UseIFormatProviderAnalyzerTests.cs | 17 ++ 12 files changed, 156 insertions(+), 67 deletions(-) create mode 100644 src/Meziantou.Analyzer/Internals/OverloadOptions.cs create mode 100644 src/Meziantou.Analyzer/Internals/OverloadParameterType.cs diff --git a/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs b/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs index e315a5b19..cdf10d059 100755 --- a/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs +++ b/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs @@ -63,12 +63,12 @@ public bool IsCultureSensitiveOperation(IOperation operation, CultureSensitiveOp if (_excludedMethods.Contains(invocation.TargetMethod)) return false; + if (invocation.HasArgumentOfType(FormatProviderSymbol, inherits: true)) + return false; + var methodName = invocation.TargetMethod.Name; if (methodName is "ToString") { - if (invocation.HasArgumentOfType(FormatProviderSymbol, inherits: true)) - return false; - // Try get the format. Most of ToString have only 1 string parameter to define the format IOperation? format = null; if (invocation.Arguments.Length > 0) @@ -281,13 +281,25 @@ private bool IsCultureSensitiveType(ITypeSymbol? typeSymbol, CultureSensitiveOpt if (typeSymbol.IsOrInheritFrom(NuGetVersioningSemanticVersionSymbol)) return false; - if (!typeSymbol.Implements(SystemIFormattableSymbol)) + if (!IsFormattableType(typeSymbol)) return false; if (!IsCultureSensitiveTypeUsingAttribute(typeSymbol)) return false; return true; + + bool IsFormattableType(ITypeSymbol type) + { + if (type.Implements(SystemIFormattableSymbol)) + return true; + + // May have ToString(IFormatProvider) even if IFormattable is not implemented directly + if (type.GetAllMembers().OfType().Any(m => m is { Name: "ToString", IsStatic: false, ReturnType: { SpecialType: SpecialType.System_String }, Parameters: [var param1] } && param1.Type.IsOrInheritFrom(FormatProviderSymbol) && m.DeclaredAccessibility is Accessibility.Public)) + return true; + + return false; + } } private bool IsCultureSensitiveTypeUsingAttribute(ITypeSymbol typeSymbol) diff --git a/src/Meziantou.Analyzer/Internals/OverloadFinder.cs b/src/Meziantou.Analyzer/Internals/OverloadFinder.cs index a1ca6eeb8..44c2a7533 100644 --- a/src/Meziantou.Analyzer/Internals/OverloadFinder.cs +++ b/src/Meziantou.Analyzer/Internals/OverloadFinder.cs @@ -1,77 +1,115 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Operations; namespace Meziantou.Analyzer.Internals; + internal sealed class OverloadFinder(Compilation compilation) { private readonly ITypeSymbol? _obsoleteSymbol = compilation.GetBestTypeByMetadataName("System.ObsoleteAttribute"); - public bool HasOverloadWithAdditionalParameterOfType( - IMethodSymbol methodSymbol, - params ITypeSymbol[] additionalParameterTypes) + private static ReadOnlySpan Wrap(ReadOnlySpan types) { - return FindOverloadWithAdditionalParameterOfType(methodSymbol, additionalParameterTypes) is not null; + var result = new OverloadParameterType[types.Length]; + for (var i = 0; i < types.Length; i++) + { + result[i] = new OverloadParameterType(types[i]); + } + + return result; } - public bool HasOverloadWithAdditionalParameterOfType( - IMethodSymbol methodSymbol, - IOperation currentOperation, - params ITypeSymbol[] additionalParameterTypes) + private static ReadOnlySpan RemoveNulls(ReadOnlySpan types) { - if (currentOperation.SemanticModel is null) - return false; + foreach (var type in types) + { + if (type.Symbol is not null) + continue; + + var list = new List(types.Length - 1); // We know there is at least one null item + foreach (var t in types) + { + if (t.Symbol is not null) + { + list.Add(t); + } + } - return FindOverloadWithAdditionalParameterOfType(methodSymbol, syntaxNode: currentOperation.Syntax, includeObsoleteMethods: false, allowOptionalParameters: false, additionalParameterTypes) is not null; + return list.ToArray(); + } + + return types; } - private IMethodSymbol? FindOverloadWithAdditionalParameterOfType( - IMethodSymbol methodSymbol, - params ITypeSymbol[] additionalParameterTypes) + public bool HasOverloadWithAdditionalParameterOfType(IObjectCreationOperation operation, OverloadOptions options, ReadOnlySpan additionalParameterTypes) { - return FindOverloadWithAdditionalParameterOfType(methodSymbol, includeObsoleteMethods: false, allowOptionalParameters: false, additionalParameterTypes); + return FindOverloadWithAdditionalParameterOfType(operation, options, additionalParameterTypes) is not null; } - public IMethodSymbol? FindOverloadWithAdditionalParameterOfType( - IMethodSymbol methodSymbol, - bool includeObsoleteMethods, - bool allowOptionalParameters, - params ITypeSymbol[] additionalParameterTypes) + public bool HasOverloadWithAdditionalParameterOfType(IInvocationOperation operation, OverloadOptions options, ReadOnlySpan additionalParameterTypes) { - return FindOverloadWithAdditionalParameterOfType(methodSymbol, syntaxNode: null, includeObsoleteMethods, allowOptionalParameters, additionalParameterTypes); + return FindOverloadWithAdditionalParameterOfType(operation, options, additionalParameterTypes) is not null; } - public IMethodSymbol? FindOverloadWithAdditionalParameterOfType( - IMethodSymbol methodSymbol, - IOperation operation, - bool includeObsoleteMethods, - bool allowOptionalParameters, - params ITypeSymbol[] additionalParameterTypes) + public bool HasOverloadWithAdditionalParameterOfType(IInvocationOperation operation, OverloadOptions options, ReadOnlySpan additionalParameterTypes) { - if (operation.SemanticModel is null) - return null; + return FindOverloadWithAdditionalParameterOfType(operation, options, additionalParameterTypes) is not null; + } - return FindOverloadWithAdditionalParameterOfType(methodSymbol, operation.Syntax, includeObsoleteMethods, allowOptionalParameters, additionalParameterTypes); + public bool HasOverloadWithAdditionalParameterOfType(IMethodSymbol methodSymbol, OverloadOptions options, ReadOnlySpan additionalParameterTypes) + { + return FindOverloadWithAdditionalParameterOfType(methodSymbol, options, additionalParameterTypes) is not null; } - public IMethodSymbol? FindOverloadWithAdditionalParameterOfType( - IMethodSymbol methodSymbol, - SyntaxNode? syntaxNode, - bool includeObsoleteMethods, - bool allowOptionalParameters, - params ITypeSymbol[] additionalParameterTypes) + public bool HasOverloadWithAdditionalParameterOfType(IMethodSymbol methodSymbol, OverloadOptions options, ReadOnlySpan additionalParameterTypes) { - if (additionalParameterTypes is null) + return FindOverloadWithAdditionalParameterOfType(methodSymbol, options, additionalParameterTypes) is not null; + } + + public IMethodSymbol? FindOverloadWithAdditionalParameterOfType(IInvocationOperation operation, OverloadOptions options, ReadOnlySpan additionalParameterTypes) + { + if (options.SyntaxNode is null) + { + options = options with { SyntaxNode = operation.Syntax }; + } + + return FindOverloadWithAdditionalParameterOfType(operation.TargetMethod, options, Wrap(additionalParameterTypes)); + } + + public IMethodSymbol? FindOverloadWithAdditionalParameterOfType(IInvocationOperation operation, OverloadOptions options, ReadOnlySpan additionalParameterTypes) + { + if (options.SyntaxNode is null) + { + options = options with { SyntaxNode = operation.Syntax }; + } + + return FindOverloadWithAdditionalParameterOfType(operation.TargetMethod, options, additionalParameterTypes); + } + + public IMethodSymbol? FindOverloadWithAdditionalParameterOfType(IObjectCreationOperation operation, OverloadOptions options, ReadOnlySpan additionalParameterTypes) + { + if (operation.Constructor is null) return null; - additionalParameterTypes = [.. additionalParameterTypes.Where(type => type is not null)]; - if (additionalParameterTypes.Length == 0) + return FindOverloadWithAdditionalParameterOfType(operation.Constructor, options, Wrap(additionalParameterTypes)); + } + + public IMethodSymbol? FindOverloadWithAdditionalParameterOfType(IMethodSymbol methodSymbol, OverloadOptions options, ReadOnlySpan additionalParameterTypes) + { + return FindOverloadWithAdditionalParameterOfType(methodSymbol, options, Wrap(additionalParameterTypes)); + } + + public IMethodSymbol? FindOverloadWithAdditionalParameterOfType(IMethodSymbol methodSymbol, OverloadOptions options, ReadOnlySpan additionalParameterTypes) + { + additionalParameterTypes = RemoveNulls(additionalParameterTypes); + if (additionalParameterTypes.IsEmpty) return null; ImmutableArray members; - if (syntaxNode is not null) + if (options.SyntaxNode is not null) { - var semanticModel = compilation.GetSemanticModel(syntaxNode.SyntaxTree); - members = semanticModel.LookupSymbols(syntaxNode.GetLocation().SourceSpan.End, methodSymbol.ContainingType, methodSymbol.Name, includeReducedExtensionMethods: true); + var semanticModel = compilation.GetSemanticModel(options.SyntaxNode.SyntaxTree); + members = semanticModel.LookupSymbols(options.SyntaxNode.GetLocation().SourceSpan.End, methodSymbol.ContainingType, methodSymbol.Name, includeReducedExtensionMethods: true); } else { @@ -82,10 +120,10 @@ public bool HasOverloadWithAdditionalParameterOfType( { if (member is IMethodSymbol method) { - if (!includeObsoleteMethods && IsObsolete(method)) + if (!options.IncludeObsoleteMembers && IsObsolete(method)) continue; - if (HasSimilarParameters(methodSymbol, method, allowOptionalParameters, additionalParameterTypes)) + if (HasSimilarParameters(methodSymbol, method, options.AllowOptionalParameters, additionalParameterTypes)) return method; } } @@ -93,6 +131,11 @@ public bool HasOverloadWithAdditionalParameterOfType( return null; } + public static bool HasSimilarParameters(IMethodSymbol method, IMethodSymbol otherMethod, bool allowOptionalParameters, params ReadOnlySpan additionalParameterTypes) + { + return HasSimilarParameters(method, otherMethod, allowOptionalParameters, Wrap(additionalParameterTypes)); + } + /// /// Methods are similar if: /// @@ -102,7 +145,7 @@ public bool HasOverloadWithAdditionalParameterOfType( /// If , can have more parameters if they are optional /// /// - public static bool HasSimilarParameters(IMethodSymbol method, IMethodSymbol otherMethod, bool allowOptionalParameters, params ITypeSymbol[] additionalParameterTypes) + public static bool HasSimilarParameters(IMethodSymbol method, IMethodSymbol otherMethod, bool allowOptionalParameters, params ReadOnlySpan additionalParameterTypes) { if (method.IsEqualTo(otherMethod)) return false; @@ -135,13 +178,13 @@ public static bool HasSimilarParameters(IMethodSymbol method, IMethodSymbol othe break; var additionalParameter = additionalParameterTypes[additionalParameterIndex]; - if (methodParameter.Type.IsEqualTo(additionalParameter)) + if (IsEqualTo(methodParameter.Type, additionalParameter)) { i++; continue; } - if (otherMethodParameter.Type.IsEqualTo(additionalParameter)) + if (IsEqualTo(otherMethodParameter.Type, additionalParameter)) { j++; continue; @@ -181,7 +224,7 @@ public static bool HasSimilarParameters(IMethodSymbol method, IMethodSymbol othe var found = false; for (var i = 0; i < otherMethodParameters.Length; i++) { - if (otherMethodParameters[i].Type.IsEqualTo(paramType)) + if (IsEqualTo(otherMethodParameters[i].Type, paramType)) { otherMethodParameters = otherMethodParameters.RemoveAt(i); found = true; @@ -204,6 +247,13 @@ public static bool HasSimilarParameters(IMethodSymbol method, IMethodSymbol othe return false; } + + static bool IsEqualTo(ITypeSymbol left, OverloadParameterType right) + { + return right.AllowInherits + ? left.IsOrInheritFrom(right.Symbol) + : left.IsEqualTo(right.Symbol); + } } private bool IsObsolete(IMethodSymbol methodSymbol) diff --git a/src/Meziantou.Analyzer/Internals/OverloadOptions.cs b/src/Meziantou.Analyzer/Internals/OverloadOptions.cs new file mode 100644 index 000000000..9d619e48c --- /dev/null +++ b/src/Meziantou.Analyzer/Internals/OverloadOptions.cs @@ -0,0 +1,5 @@ +using Microsoft.CodeAnalysis; + +namespace Meziantou.Analyzer.Internals; + +internal record struct OverloadOptions(bool IncludeObsoleteMembers = false, bool AllowOptionalParameters = false, bool IncludeExtensionsMethods = false, SyntaxNode? SyntaxNode = null); diff --git a/src/Meziantou.Analyzer/Internals/OverloadParameterType.cs b/src/Meziantou.Analyzer/Internals/OverloadParameterType.cs new file mode 100644 index 000000000..c7058e7eb --- /dev/null +++ b/src/Meziantou.Analyzer/Internals/OverloadParameterType.cs @@ -0,0 +1,5 @@ +using Microsoft.CodeAnalysis; + +namespace Meziantou.Analyzer.Internals; + +internal record struct OverloadParameterType(ITypeSymbol? Symbol, bool AllowInherits = false); diff --git a/src/Meziantou.Analyzer/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer.cs b/src/Meziantou.Analyzer/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer.cs index c4f13fab3..8c7d394ae 100755 --- a/src/Meziantou.Analyzer/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer.cs @@ -295,10 +295,10 @@ private bool IsPotentialMember(IInvocationOperation operation, IMethodSymbol met if (methodSymbol.HasAttribute(ObsoleteAttributeSymbol)) return false; - if (OverloadFinder.HasSimilarParameters(method, methodSymbol, allowOptionalParameters: false)) + if (OverloadFinder.HasSimilarParameters(method, methodSymbol, allowOptionalParameters: false, default(ReadOnlySpan))) return true; - if (CancellationTokenSymbol is not null && OverloadFinder.HasSimilarParameters(method, methodSymbol, allowOptionalParameters: false, CancellationTokenSymbol)) + if (CancellationTokenSymbol is not null && OverloadFinder.HasSimilarParameters(method, methodSymbol, allowOptionalParameters: false, [CancellationTokenSymbol])) return true; } diff --git a/src/Meziantou.Analyzer/Rules/EmbedCaughtExceptionAsInnerExceptionAnalyzer.cs b/src/Meziantou.Analyzer/Rules/EmbedCaughtExceptionAsInnerExceptionAnalyzer.cs index dbd71a21f..9be3a32fe 100644 --- a/src/Meziantou.Analyzer/Rules/EmbedCaughtExceptionAsInnerExceptionAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/EmbedCaughtExceptionAsInnerExceptionAnalyzer.cs @@ -55,7 +55,7 @@ private static void AnalyzeThrow(OperationAnalysisContext context, OverloadFinde var argument = objectCreationOperation.Arguments.FirstOrDefault(arg => IsPotentialParameter(arg?.Parameter, exceptionSymbol)); if (argument is null) { - if (overloadFinder.HasOverloadWithAdditionalParameterOfType(objectCreationOperation.Constructor, exceptionSymbol)) + if (overloadFinder.HasOverloadWithAdditionalParameterOfType(objectCreationOperation, options: default, [exceptionSymbol])) { context.ReportDiagnostic(Rule, objectCreationOperation); } diff --git a/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasCancellationTokenAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasCancellationTokenAnalyzer.cs index a34712da4..a385b6576 100644 --- a/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasCancellationTokenAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasCancellationTokenAnalyzer.cs @@ -111,7 +111,7 @@ private bool HasAnOverloadWithCancellationToken(OperationAnalysisContext context return true; var allowOptionalParameters = context.Options.GetConfigurationValue(operation, "MA0032.allowOverloadsWithOptionalParameters", defaultValue: false); - var overload = _overloadFinder.FindOverloadWithAdditionalParameterOfType(operation.TargetMethod, operation, includeObsoleteMethods: false, allowOptionalParameters, CancellationTokenSymbol); + var overload = _overloadFinder.FindOverloadWithAdditionalParameterOfType(operation, new OverloadOptions(AllowOptionalParameters: allowOptionalParameters), [CancellationTokenSymbol]); if (overload is not null) { for (var i = 0; i < overload.Parameters.Length; i++) diff --git a/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasTimeProviderAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasTimeProviderAnalyzer.cs index db7c11eeb..527764e9e 100644 --- a/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasTimeProviderAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasTimeProviderAnalyzer.cs @@ -75,7 +75,7 @@ private bool HasAnOverloadWithTimeProvider(IInvocationOperation operation, [NotN if (IsArgumentImplicitlyDeclared(operation, TimeProviderSymbol, out parameterInfo)) return true; - var overload = _overloadFinder.FindOverloadWithAdditionalParameterOfType(operation.TargetMethod, operation, includeObsoleteMethods: false, allowOptionalParameters: true, TimeProviderSymbol); + var overload = _overloadFinder.FindOverloadWithAdditionalParameterOfType(operation, new OverloadOptions(IncludeObsoleteMembers: false, AllowOptionalParameters: true), [TimeProviderSymbol]); if (overload is not null) { for (var i = 0; i < overload.Parameters.Length; i++) diff --git a/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs index adce88830..e17245c94 100644 --- a/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs @@ -59,7 +59,7 @@ public void AnalyzeInvocation(OperationAnalysisContext context) return; } - var overload = _overloadFinder.FindOverloadWithAdditionalParameterOfType(operation.TargetMethod, operation, includeObsoleteMethods: false, allowOptionalParameters: false, _cultureSensitiveContext.FormatProviderSymbol); + var overload = _overloadFinder.FindOverloadWithAdditionalParameterOfType(operation, new OverloadOptions(IncludeObsoleteMembers: false, AllowOptionalParameters: false), [_cultureSensitiveContext.FormatProviderSymbol]); if (overload is not null) { if (_cultureSensitiveContext.IsCultureSensitiveOperation(operation, CultureSensitiveOptions.None)) @@ -71,7 +71,7 @@ public void AnalyzeInvocation(OperationAnalysisContext context) } var targetMethodType = operation.TargetMethod.ContainingType; - if (targetMethodType.IsNumberType() && _cultureSensitiveContext.NumberStyleSymbol is not null && _overloadFinder.HasOverloadWithAdditionalParameterOfType(operation.TargetMethod, operation, _cultureSensitiveContext.FormatProviderSymbol, _cultureSensitiveContext.NumberStyleSymbol)) + if (targetMethodType.IsNumberType() && _cultureSensitiveContext.NumberStyleSymbol is not null && _overloadFinder.HasOverloadWithAdditionalParameterOfType(operation, options: default, [_cultureSensitiveContext.FormatProviderSymbol, _cultureSensitiveContext.NumberStyleSymbol])) { context.ReportDiagnostic(Rule, operation, operation.TargetMethod.Name, _cultureSensitiveContext.FormatProviderSymbol.ToDisplayString()); return; @@ -80,14 +80,14 @@ public void AnalyzeInvocation(OperationAnalysisContext context) var isDateTime = targetMethodType.IsDateTime() || targetMethodType.IsEqualToAny(_cultureSensitiveContext.DateTimeOffsetSymbol, _cultureSensitiveContext.DateOnlySymbol, _cultureSensitiveContext.TimeOnlySymbol); if (isDateTime) { - if (_cultureSensitiveContext.DateTimeStyleSymbol is not null && _overloadFinder.HasOverloadWithAdditionalParameterOfType(operation.TargetMethod, operation, _cultureSensitiveContext.FormatProviderSymbol, _cultureSensitiveContext.DateTimeStyleSymbol)) + if (_cultureSensitiveContext.DateTimeStyleSymbol is not null && _overloadFinder.HasOverloadWithAdditionalParameterOfType(operation, options: default, [_cultureSensitiveContext.FormatProviderSymbol, _cultureSensitiveContext.DateTimeStyleSymbol])) { context.ReportDiagnostic(Rule, operation, operation.TargetMethod.Name, _cultureSensitiveContext.FormatProviderSymbol.ToDisplayString()); return; } } - if (operation.Arguments.IsEmpty && targetMethodType.Implements(_cultureSensitiveContext.SystemIFormattableSymbol) && _overloadFinder.HasOverloadWithAdditionalParameterOfType(operation.TargetMethod, operation, _cultureSensitiveContext.FormatProviderSymbol, compilation.GetSpecialType(SpecialType.System_String))) + if (operation.Arguments.IsEmpty && targetMethodType.Implements(_cultureSensitiveContext.SystemIFormattableSymbol) && _overloadFinder.HasOverloadWithAdditionalParameterOfType(operation, options: default, [_cultureSensitiveContext.FormatProviderSymbol, compilation.GetSpecialType(SpecialType.System_String)])) { context.ReportDiagnostic(Rule, operation, operation.TargetMethod.Name, _cultureSensitiveContext.FormatProviderSymbol.ToDisplayString()); return; @@ -96,7 +96,7 @@ public void AnalyzeInvocation(OperationAnalysisContext context) if (_cultureSensitiveContext.CultureInfoSymbol is not null && !operation.HasArgumentOfType(_cultureSensitiveContext.CultureInfoSymbol)) { - var overload = _overloadFinder.FindOverloadWithAdditionalParameterOfType(operation.TargetMethod, includeObsoleteMethods: false, allowOptionalParameters: false, _cultureSensitiveContext.CultureInfoSymbol); + var overload = _overloadFinder.FindOverloadWithAdditionalParameterOfType(operation, new OverloadOptions(IncludeObsoleteMembers: false, AllowOptionalParameters: false), [_cultureSensitiveContext.CultureInfoSymbol]); if (overload is not null) { if (_cultureSensitiveContext.IsCultureSensitiveOperation(operation, CultureSensitiveOptions.None)) diff --git a/src/Meziantou.Analyzer/Rules/UseStringComparerAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseStringComparerAnalyzer.cs index c04e42f34..5def15657 100644 --- a/src/Meziantou.Analyzer/Rules/UseStringComparerAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseStringComparerAnalyzer.cs @@ -89,8 +89,8 @@ public void AnalyzeConstructor(OperationAnalysisContext ctx) if (method is null) return; - if ((EqualityComparerStringType is not null && _overloadFinder.HasOverloadWithAdditionalParameterOfType(method, EqualityComparerStringType)) || - (ComparerStringType is not null && _overloadFinder.HasOverloadWithAdditionalParameterOfType(method, ComparerStringType))) + if ((EqualityComparerStringType is not null && _overloadFinder.HasOverloadWithAdditionalParameterOfType(method, options: default, [EqualityComparerStringType])) || + (ComparerStringType is not null && _overloadFinder.HasOverloadWithAdditionalParameterOfType(method, options: default, [ComparerStringType]))) { ctx.ReportDiagnostic(Rule, operation); } @@ -126,8 +126,8 @@ public void AnalyzeInvocation(OperationAnalysisContext ctx) if (operation.IsImplicit && IsQueryOperator(operation) && ctx.Options.GetConfigurationValue(operation, Rule.Id + ".exclude_query_operator_syntaxes", defaultValue: false)) return; - if ((EqualityComparerStringType is not null && _overloadFinder.HasOverloadWithAdditionalParameterOfType(method, operation, EqualityComparerStringType)) || - (ComparerStringType is not null && _overloadFinder.HasOverloadWithAdditionalParameterOfType(method, operation, ComparerStringType))) + if ((EqualityComparerStringType is not null && _overloadFinder.HasOverloadWithAdditionalParameterOfType(operation, options: default, [EqualityComparerStringType])) || + (ComparerStringType is not null && _overloadFinder.HasOverloadWithAdditionalParameterOfType(operation, options: default, [ComparerStringType]))) { ctx.ReportDiagnostic(Rule, operation, DefaultDiagnosticInvocationReportOptions); return; diff --git a/src/Meziantou.Analyzer/Rules/UseStringComparisonAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseStringComparisonAnalyzer.cs index d1ccde79f..dd1dc15f0 100644 --- a/src/Meziantou.Analyzer/Rules/UseStringComparisonAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseStringComparisonAnalyzer.cs @@ -67,7 +67,7 @@ public void AnalyzeInvocation(OperationAnalysisContext context) return; // Check if there is an overload with a StringComparison - if (_overloadFinder.HasOverloadWithAdditionalParameterOfType(operation.TargetMethod, operation, _stringComparisonSymbol)) + if (_overloadFinder.HasOverloadWithAdditionalParameterOfType(operation, options: default, [_stringComparisonSymbol])) { if (IsNonCultureSensitiveMethod(operation)) { diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseIFormatProviderAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseIFormatProviderAnalyzerTests.cs index f2106efcd..752da8f39 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/UseIFormatProviderAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseIFormatProviderAnalyzerTests.cs @@ -297,6 +297,23 @@ class Sample : System.IFormattable public override string ToString() => throw null; public string ToString(string format, System.IFormatProvider formatProvider) => throw null; } +"""; + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ToString_WithIFormatProviderOverload_WithoutIFormattable() + { + var sourceCode = """ +_ = [|new Location().ToString()|]; + +class Location +{ + public override string ToString() => throw null; + public string ToString(System.IFormatProvider formatProvider) => throw null; +} """; await CreateProjectBuilder() .WithSourceCode(sourceCode) From ca5314e85eb0a6058b8d786aed6de1f1f42e0356 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 2 Nov 2025 00:15:42 -0400 Subject: [PATCH 30/38] Fix MA0015 not detecting static ThrowIf methods on ArgumentException and related types (#912) --- docs/Rules/MA0015.md | 18 + ...eptionShouldSpecifyArgumentNameAnalyzer.cs | 124 ++++- ...nShouldSpecifyArgumentNameAnalyzerTests.cs | 497 +++++++++++++++++- 3 files changed, 636 insertions(+), 3 deletions(-) diff --git a/docs/Rules/MA0015.md b/docs/Rules/MA0015.md index a94b52330..1cbc58229 100644 --- a/docs/Rules/MA0015.md +++ b/docs/Rules/MA0015.md @@ -12,4 +12,22 @@ void Sample(string str) if (str == "") throw new ArgumentException("Error message", paramName: nameof(str)); // ok } + +class Sample +{ + void Test(string test) + { + ArgumentNullException.ThrowIfNull(Name); // non-compliant: 'Name' is not a parameter + } + + public static string Name { get; } +} + +class Sample +{ + void Test(string test) + { + ArgumentNullException.ThrowIfNull(test); // ok + } +} ```` diff --git a/src/Meziantou.Analyzer/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzer.cs b/src/Meziantou.Analyzer/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzer.cs index ce48df78c..31b51367c 100644 --- a/src/Meziantou.Analyzer/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzer.cs @@ -37,10 +37,27 @@ public override void Initialize(AnalysisContext context) context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); - context.RegisterOperationAction(Analyze, OperationKind.ObjectCreation); + context.RegisterCompilationStartAction(context => + { + var argumentExceptionType = context.Compilation.GetBestTypeByMetadataName("System.ArgumentException"); + var argumentNullExceptionType = context.Compilation.GetBestTypeByMetadataName("System.ArgumentNullException"); + var argumentOutOfRangeExceptionType = context.Compilation.GetBestTypeByMetadataName("System.ArgumentOutOfRangeException"); + var callerArgumentExpressionAttribute = context.Compilation.GetBestTypeByMetadataName("System.Runtime.CompilerServices.CallerArgumentExpressionAttribute"); + + if (argumentExceptionType is null || argumentNullExceptionType is null) + return; + + context.RegisterOperationAction(AnalyzeObjectCreation, OperationKind.ObjectCreation); + + if (callerArgumentExpressionAttribute is not null) + { + context.RegisterOperationAction(ctx => AnalyzeInvocation(ctx, argumentExceptionType, argumentNullExceptionType, argumentOutOfRangeExceptionType, callerArgumentExpressionAttribute), OperationKind.Invocation); + } + }); } - private static void Analyze(OperationAnalysisContext context) + // Validate throw new ArgumentException("message", "paramName"); + private static void AnalyzeObjectCreation(OperationAnalysisContext context) { var op = (IObjectCreationOperation)context.Operation; if (op is null) @@ -112,6 +129,109 @@ private static void Analyze(OperationAnalysisContext context) } } + private static void AnalyzeInvocation(OperationAnalysisContext context, INamedTypeSymbol argumentExceptionType, INamedTypeSymbol argumentNullExceptionType, INamedTypeSymbol? argumentOutOfRangeExceptionType, INamedTypeSymbol callerArgumentExpressionAttribute) + { + var op = (IInvocationOperation)context.Operation; + if (op is null) + return; + + var method = op.TargetMethod; + if (method is null || !method.IsStatic) + return; + + // Check if the method name starts with "ThrowIf" + if (!method.Name.StartsWith("ThrowIf", StringComparison.Ordinal)) + return; + + // There must be at least one argument + if (op.Arguments.Length == 0) + return; + + // Check if this is a ThrowIfXxx method on ArgumentException, ArgumentNullException, or ArgumentOutOfRangeException + var containingType = method.ContainingType; + if (containingType is null) + return; + + if (!containingType.IsEqualToAny(argumentExceptionType, argumentNullExceptionType, argumentOutOfRangeExceptionType)) + return; + + // Find the parameter with CallerArgumentExpressionAttribute + foreach (var parameter in method.Parameters) + { + if (!parameter.Type.IsString()) + continue; + + var attribute = parameter.GetAttribute(callerArgumentExpressionAttribute); + if (attribute is null) + continue; + + if (attribute.ConstructorArguments.Length == 0) + continue; + + // Get the parameter name referenced by the CallerArgumentExpressionAttribute + var referencedParameterName = attribute.ConstructorArguments[0].Value as string; + if (string.IsNullOrEmpty(referencedParameterName)) + continue; + + // Find the parameter being referenced + var referencedParameter = method.Parameters.FirstOrDefault(p => p.Name == referencedParameterName); + if (referencedParameter is null) + continue; + + // Find the argument for the paramName parameter + var paramNameArgument = op.Arguments.FirstOrDefault(arg => arg.Parameter is not null && arg.Parameter.IsEqualTo(parameter)); + if (paramNameArgument is not null && !paramNameArgument.IsImplicit && paramNameArgument.Value is not null) + { + ValidateParamNameArgument(context, paramNameArgument); + return; + } + + // Find the argument for the referenced parameter (the one being validated) + var referencedArgument = op.Arguments.FirstOrDefault(arg => arg.Parameter is not null && arg.Parameter.IsEqualTo(referencedParameter)); + if (referencedArgument is not null) + { + ValidateExpression(context, referencedArgument); + return; + } + } + } + + private static void ValidateParamNameArgument(OperationAnalysisContext context, IArgumentOperation paramNameArgument) + { + // Check if the argument is a constant string value + if (!paramNameArgument.Value.ConstantValue.HasValue || paramNameArgument.Value.ConstantValue.Value is not string paramNameValue) + return; + + var availableParameterNames = GetParameterNames(paramNameArgument, context.CancellationToken); + if (availableParameterNames.Contains(paramNameValue, StringComparer.Ordinal)) + { + if (paramNameArgument.Value is not INameOfOperation) + { + var properties = ImmutableDictionary.Empty.Add(ArgumentExceptionShouldSpecifyArgumentNameAnalyzerCommon.ArgumentNameKey, paramNameValue); + context.ReportDiagnostic(NameofRule, properties, paramNameArgument.Value); + } + + return; + } + + context.ReportDiagnostic(Rule, paramNameArgument, $"'{paramNameValue}' is not a valid parameter name"); + } + + private static void ValidateExpression(OperationAnalysisContext context, IArgumentOperation argument) + { + if (argument.Value is null) + return; + + var unwrappedValue = argument.Value.UnwrapImplicitConversionOperations(); + if (unwrappedValue is IParameterReferenceOperation) + { + // Parameter references are always valid - no need to validate the name + return; + } + + context.ReportDiagnostic(Rule, argument, "The expression does not match a parameter"); + } + private static IEnumerable GetParameterNames(IOperation operation, CancellationToken cancellationToken) { var symbols = operation.LookupAvailableSymbols(cancellationToken); diff --git a/tests/Meziantou.Analyzer.Test/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzerTests.cs index a8be3f61f..28ae1fc9d 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzerTests.cs @@ -1,4 +1,5 @@ using Meziantou.Analyzer.Rules; +using Meziantou.Analyzer.Test.Helpers; using TestHelper; namespace Meziantou.Analyzer.Test.Rules; @@ -8,7 +9,8 @@ public sealed class ArgumentExceptionShouldSpecifyArgumentNameAnalyzerTests private static ProjectBuilder CreateProjectBuilder() { return new ProjectBuilder() - .WithAnalyzer(id: "MA0015"); + .WithAnalyzer(id: "MA0015") + .WithTargetFramework(TargetFramework.NetLatest); } [Fact] @@ -450,4 +452,497 @@ await CreateProjectBuilder() .ValidateAsync(); } #endif + + [Fact] + public async Task ThrowIfNull_ValidParameter_ShouldNotReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(string test) + { + ArgumentNullException.ThrowIfNull(test); + } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ThrowIfNull_InvalidParameter_ShouldReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(string test) + { + ArgumentNullException.ThrowIfNull([|Name|]); + } + + public static string Name { get; } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ThrowIfNullOrEmpty_ValidParameter_ShouldNotReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(string test) + { + ArgumentNullException.ThrowIfNullOrEmpty(test); + } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ThrowIfNullOrEmpty_InvalidParameter_ShouldReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(string test) + { + ArgumentNullException.ThrowIfNullOrEmpty([|Name|]); + } + + public static string Name { get; } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ThrowIfNullOrWhiteSpace_ValidParameter_ShouldNotReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(string test) + { + ArgumentNullException.ThrowIfNullOrWhiteSpace(test); + } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ThrowIfNullOrWhiteSpace_InvalidParameter_ShouldReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(string test) + { + ArgumentNullException.ThrowIfNullOrWhiteSpace([|Name|]); + } + + public static string Name { get; } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ArgumentException_ThrowIfNullOrEmpty_ValidParameter_ShouldNotReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(string test) + { + ArgumentException.ThrowIfNullOrEmpty(test); + } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ArgumentException_ThrowIfNullOrEmpty_InvalidParameter_ShouldReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(string test) + { + ArgumentException.ThrowIfNullOrEmpty([|Name|]); + } + + public static string Name { get; } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ArgumentException_ThrowIfNullOrWhiteSpace_ValidParameter_ShouldNotReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(string test) + { + ArgumentException.ThrowIfNullOrWhiteSpace(test); + } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ArgumentException_ThrowIfNullOrWhiteSpace_InvalidParameter_ShouldReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(string test) + { + ArgumentException.ThrowIfNullOrWhiteSpace([|Name|]); + } + + public static string Name { get; } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ThrowIfNull_WithValidParamNameArgument_ShouldNotReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(string test) + { + ArgumentNullException.ThrowIfNull(test, nameof(test)); + } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ThrowIfNull_WithInvalidParamNameArgument_ShouldReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(string test) + { + ArgumentNullException.ThrowIfNull(test, [|"invalid"|]); + } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ShouldReportDiagnosticWithMessage("'invalid' is not a valid parameter name") + .ValidateAsync(); + } + + [Fact] + public async Task ArgumentException_ThrowIfNullOrEmpty_WithValidParamNameArgument_ShouldNotReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(string test) + { + ArgumentException.ThrowIfNullOrEmpty(test, nameof(test)); + } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ArgumentException_ThrowIfNullOrEmpty_WithInvalidParamNameArgument_ShouldReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(string test) + { + ArgumentException.ThrowIfNullOrEmpty(test, [|"invalid"|]); + } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ShouldReportDiagnosticWithMessage("'invalid' is not a valid parameter name") + .ValidateAsync(); + } + + [Fact] + public async Task ArgumentOutOfRangeException_ThrowIfNegative_ValidParameter_ShouldNotReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(int value) + { + ArgumentOutOfRangeException.ThrowIfNegative(value); + } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ArgumentOutOfRangeException_ThrowIfNegative_InvalidParameter_ShouldReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(int value) + { + ArgumentOutOfRangeException.ThrowIfNegative([|Count|]); + } + + public static int Count { get; } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ArgumentOutOfRangeException_ThrowIfNegativeOrZero_ValidParameter_ShouldNotReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(int value) + { + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value); + } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ArgumentOutOfRangeException_ThrowIfGreaterThan_ValidParameter_ShouldNotReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(int value) + { + ArgumentOutOfRangeException.ThrowIfGreaterThan(value, 100); + } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ArgumentOutOfRangeException_ThrowIfGreaterThanOrEqual_InvalidParameter_ShouldReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(int value) + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual([|MaxValue|], 100); + } + + public static int MaxValue { get; } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ThrowIfNull_WithNullExpression_ShouldReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(string test) + { + ArgumentNullException.ThrowIfNull([|""|]); + } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ThrowIfNull_WithNullExpressionAndValidParamName_ShouldNotReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(string test) + { + ArgumentNullException.ThrowIfNull("", nameof(test)); + } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ThrowIfNull_WithNullExpressionAndInvalidParamName_ShouldReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(string test) + { + ArgumentNullException.ThrowIfNull("", [|"invalid"|]); + } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ThrowIfNull_WithBooleanExpression_ShouldReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(string test) + { + ArgumentNullException.ThrowIfNull([|0 == 1|]); + } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ShouldReportDiagnosticWithMessage("The expression does not match a parameter") + .ValidateAsync(); + } + + [Fact] + public async Task ThrowIfNull_WithBooleanExpressionAndValidParamName_ShouldNotReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(string test) + { + ArgumentNullException.ThrowIfNull(0 == 1, nameof(test)); + } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task ThrowIfNull_WithBooleanExpressionAndInvalidParamName_ShouldReportError() + { + var sourceCode = """ + using System; + class Sample + { + void Test(string test) + { + ArgumentNullException.ThrowIfNull(0 == 1, [|"invalid"|]); + } + } + """; + + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ShouldReportDiagnosticWithMessage("'invalid' is not a valid parameter name") + .ValidateAsync(); + } } From 9a3573b7a7be543ffdfe241ae7a8878b721cca0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Sun, 2 Nov 2025 13:08:17 -0500 Subject: [PATCH 31/38] Add link to source files in documentation --- .github/copilot-instructions.md | 2 +- docs/Rules/MA0001.md | 3 + docs/Rules/MA0002.md | 3 + docs/Rules/MA0003.md | 3 + docs/Rules/MA0004.md | 3 + docs/Rules/MA0005.md | 3 + docs/Rules/MA0006.md | 3 + docs/Rules/MA0007.md | 3 + docs/Rules/MA0008.md | 3 + docs/Rules/MA0009.md | 3 + docs/Rules/MA0010.md | 3 + docs/Rules/MA0011.md | 3 + docs/Rules/MA0012.md | 3 + docs/Rules/MA0013.md | 3 + docs/Rules/MA0014.md | 3 + docs/Rules/MA0015.md | 3 + docs/Rules/MA0016.md | 3 + docs/Rules/MA0017.md | 3 + docs/Rules/MA0018.md | 3 + docs/Rules/MA0019.md | 3 + docs/Rules/MA0020.md | 3 + docs/Rules/MA0021.md | 3 + docs/Rules/MA0022.md | 3 + docs/Rules/MA0023.md | 3 + docs/Rules/MA0024.md | 3 + docs/Rules/MA0025.md | 3 + docs/Rules/MA0026.md | 3 + docs/Rules/MA0027.md | 3 + docs/Rules/MA0028.md | 3 + docs/Rules/MA0029.md | 3 + docs/Rules/MA0030.md | 3 + docs/Rules/MA0031.md | 3 + docs/Rules/MA0032.md | 3 + docs/Rules/MA0033.md | 3 + docs/Rules/MA0035.md | 3 + docs/Rules/MA0036.md | 3 + docs/Rules/MA0037.md | 3 + docs/Rules/MA0038.md | 3 + docs/Rules/MA0039.md | 3 + docs/Rules/MA0040.md | 3 + docs/Rules/MA0041.md | 3 + docs/Rules/MA0042.md | 3 + docs/Rules/MA0043.md | 3 + docs/Rules/MA0044.md | 3 + docs/Rules/MA0045.md | 3 + docs/Rules/MA0046.md | 3 + docs/Rules/MA0047.md | 3 + docs/Rules/MA0048.md | 3 + docs/Rules/MA0049.md | 3 + docs/Rules/MA0050.md | 3 + docs/Rules/MA0051.md | 3 + docs/Rules/MA0052.md | 3 + docs/Rules/MA0053.md | 3 + docs/Rules/MA0054.md | 3 + docs/Rules/MA0055.md | 3 + docs/Rules/MA0056.md | 3 + docs/Rules/MA0057.md | 3 + docs/Rules/MA0058.md | 3 + docs/Rules/MA0059.md | 3 + docs/Rules/MA0060.md | 3 + docs/Rules/MA0061.md | 3 + docs/Rules/MA0062.md | 3 + docs/Rules/MA0063.md | 3 + docs/Rules/MA0064.md | 3 + docs/Rules/MA0065.md | 3 + docs/Rules/MA0066.md | 3 + docs/Rules/MA0067.md | 3 + docs/Rules/MA0068.md | 3 + docs/Rules/MA0069.md | 3 + docs/Rules/MA0070.md | 3 + docs/Rules/MA0071.md | 3 + docs/Rules/MA0072.md | 3 + docs/Rules/MA0073.md | 3 + docs/Rules/MA0074.md | 3 + docs/Rules/MA0075.md | 3 + docs/Rules/MA0076.md | 3 + docs/Rules/MA0077.md | 3 + docs/Rules/MA0078.md | 3 + docs/Rules/MA0079.md | 3 + docs/Rules/MA0080.md | 3 + docs/Rules/MA0081.md | 3 + docs/Rules/MA0082.md | 3 + docs/Rules/MA0083.md | 3 + docs/Rules/MA0084.md | 3 + docs/Rules/MA0085.md | 3 + docs/Rules/MA0086.md | 3 + docs/Rules/MA0087.md | 3 + docs/Rules/MA0088.md | 3 + docs/Rules/MA0089.md | 3 + docs/Rules/MA0090.md | 3 + docs/Rules/MA0091.md | 3 + docs/Rules/MA0092.md | 3 + docs/Rules/MA0093.md | 3 + docs/Rules/MA0094.md | 3 + docs/Rules/MA0095.md | 3 + docs/Rules/MA0096.md | 3 + docs/Rules/MA0097.md | 3 + docs/Rules/MA0098.md | 3 + docs/Rules/MA0099.md | 3 + docs/Rules/MA0100.md | 3 + docs/Rules/MA0101.md | 3 + docs/Rules/MA0102.md | 3 + docs/Rules/MA0103.md | 3 + docs/Rules/MA0104.md | 3 + docs/Rules/MA0105.md | 3 + docs/Rules/MA0106.md | 3 + docs/Rules/MA0107.md | 3 + docs/Rules/MA0108.md | 3 + docs/Rules/MA0109.md | 3 + docs/Rules/MA0110.md | 3 + docs/Rules/MA0111.md | 3 + docs/Rules/MA0112.md | 3 + docs/Rules/MA0113.md | 3 + docs/Rules/MA0114.md | 3 + docs/Rules/MA0115.md | 3 + docs/Rules/MA0116.md | 3 + docs/Rules/MA0117.md | 3 + docs/Rules/MA0118.md | 3 + docs/Rules/MA0119.md | 3 + docs/Rules/MA0120.md | 3 + docs/Rules/MA0121.md | 3 + docs/Rules/MA0122.md | 3 + docs/Rules/MA0123.md | 3 + docs/Rules/MA0124.md | 3 + docs/Rules/MA0125.md | 3 + docs/Rules/MA0126.md | 3 + docs/Rules/MA0127.md | 3 + docs/Rules/MA0128.md | 3 + docs/Rules/MA0129.md | 3 + docs/Rules/MA0130.md | 3 + docs/Rules/MA0131.md | 3 + docs/Rules/MA0132.md | 3 + docs/Rules/MA0133.md | 3 + docs/Rules/MA0134.md | 3 + docs/Rules/MA0135.md | 3 + docs/Rules/MA0136.md | 3 + docs/Rules/MA0137.md | 3 + docs/Rules/MA0138.md | 3 + docs/Rules/MA0139.md | 3 + docs/Rules/MA0140.md | 3 + docs/Rules/MA0141.md | 3 + docs/Rules/MA0142.md | 3 + docs/Rules/MA0143.md | 3 + docs/Rules/MA0144.md | 3 + docs/Rules/MA0145.md | 3 + docs/Rules/MA0146.md | 3 + docs/Rules/MA0147.md | 3 + docs/Rules/MA0148.md | 3 + docs/Rules/MA0149.md | 3 + docs/Rules/MA0150.md | 3 + docs/Rules/MA0151.md | 3 + docs/Rules/MA0152.md | 3 + docs/Rules/MA0153.md | 3 + docs/Rules/MA0154.md | 3 + docs/Rules/MA0155.md | 3 + docs/Rules/MA0156.md | 3 + docs/Rules/MA0157.md | 3 + docs/Rules/MA0158.md | 3 + docs/Rules/MA0159.md | 3 + docs/Rules/MA0160.md | 3 + docs/Rules/MA0161.md | 3 + docs/Rules/MA0162.md | 3 + docs/Rules/MA0163.md | 3 + docs/Rules/MA0164.md | 3 + docs/Rules/MA0165.md | 3 + docs/Rules/MA0166.md | 3 + docs/Rules/MA0167.md | 3 + docs/Rules/MA0168.md | 3 + docs/Rules/MA0169.md | 3 + docs/Rules/MA0170.md | 3 + docs/Rules/MA0171.md | 3 + docs/Rules/MA0172.md | 3 + docs/Rules/MA0173.md | 3 + docs/Rules/MA0174.md | 3 + docs/Rules/MA0175.md | 3 + docs/Rules/MA0176.md | 3 + docs/Rules/MA0177.md | 3 + src/DocumentationGenerator/Program.cs | 84 ++++++++++++++++++++++----- 178 files changed, 600 insertions(+), 14 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 023fbac37..4b76e33e7 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -3,7 +3,7 @@ You MUST make your best effort to ensure your changes satisfy those criteria before committing. If for any reason you were unable to build or test the changes, you MUST report that. You MUST NOT claim success unless all builds and tests pass as described above. Do not complete without checking the relevant code builds and relevant tests still pass after the last edits you make. Do not simply assume that your changes fix test failures you see, actually build and run those tests again to confirm. -Also, always run `dotnet run --project src/DocumentationGenerator` to update the markdown documentation after modifying analyzer code or documentation comments. +Also, always run `dotnet run --project src/DocumentationGenerator` to update the markdown documentation after modifying analyzer code or documentation comments. Note that this command returns a non-zero exit code if any markdown files were changed. After running the command, review the changes made to the markdown files and ensure they are accurate and appropriate. If you make any changes to the markdown files, you MUST re-run the command to verify that no further changes are necessary. You MUST follow all code-formatting and naming conventions defined in [`.editorconfig`](/.editorconfig). diff --git a/docs/Rules/MA0001.md b/docs/Rules/MA0001.md index 0637900ab..c39a83806 100644 --- a/docs/Rules/MA0001.md +++ b/docs/Rules/MA0001.md @@ -1,4 +1,7 @@ # MA0001 - StringComparison is missing + +Sources: [UseStringComparisonAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseStringComparisonAnalyzer.cs), [UseStringComparisonFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UseStringComparisonFixer.cs) + String manipulation methods in .NET do not all use the same default rules for string comparison by culture and case. For instance, `string.Equals(string, string)` uses `StringComparison.Ordinal` whereas `IndexOf(String)` uses `StringComparison.CurrentCulture`. So, you should use an overload that does not rely on default behavior. diff --git a/docs/Rules/MA0002.md b/docs/Rules/MA0002.md index 5f584fa79..086a391af 100644 --- a/docs/Rules/MA0002.md +++ b/docs/Rules/MA0002.md @@ -1,4 +1,7 @@ # MA0002 - IEqualityComparer\ or IComparer\ is missing + +Sources: [UseStringComparerAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseStringComparerAnalyzer.cs), [UseStringComparerFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UseStringComparerFixer.cs) + You should be explicit in the way strings ought to be compared in `HashSet`, `Dictionary`, `ConcurrentDictionary` or any methods that compare strings. diff --git a/docs/Rules/MA0003.md b/docs/Rules/MA0003.md index 3a7b2cb88..000d73855 100644 --- a/docs/Rules/MA0003.md +++ b/docs/Rules/MA0003.md @@ -1,4 +1,7 @@ # MA0003 - Add parameter name to improve readability + +Sources: [NamedParameterAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/NamedParameterAnalyzer.cs), [NamedParameterFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/NamedParameterFixer.cs) + You should name the parameter when you call a method with a literal value diff --git a/docs/Rules/MA0004.md b/docs/Rules/MA0004.md index ce27d988b..5655ed71a 100644 --- a/docs/Rules/MA0004.md +++ b/docs/Rules/MA0004.md @@ -1,4 +1,7 @@ # MA0004 - Use Task.ConfigureAwait + +Sources: [UseConfigureAwaitAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseConfigureAwaitAnalyzer.cs), [UseConfigureAwaitFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UseConfigureAwaitFixer.cs) + - If the `SynchronizationContext` is not needed, you should use `ConfigureAwait(false)` - If the `SynchronizationContext` is needed (WinForm, WPF, ASP.NET, Blazor, etc.), you can use `ConfigureAwait(true)` or `ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)` to make it explicit, or you can disable the rule locally diff --git a/docs/Rules/MA0005.md b/docs/Rules/MA0005.md index f40af5041..a8c1e8015 100644 --- a/docs/Rules/MA0005.md +++ b/docs/Rules/MA0005.md @@ -1,4 +1,7 @@ # MA0005 - Use Array.Empty\() + +Sources: [UseArrayEmptyAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseArrayEmptyAnalyzer.cs), [UseArrayEmptyFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UseArrayEmptyFixer.cs) + You should use `Array.Empty()` instead of `new T[0]` to avoid an allocation. diff --git a/docs/Rules/MA0006.md b/docs/Rules/MA0006.md index e2446089f..6d578d5e6 100644 --- a/docs/Rules/MA0006.md +++ b/docs/Rules/MA0006.md @@ -1,4 +1,7 @@ # MA0006 - Use String.Equals instead of equality operator + +Sources: [UseStringEqualsAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseStringEqualsAnalyzer.cs), [UseStringEqualsFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UseStringEqualsFixer.cs) + You should use `string.Equals` instead of `==` or `!=`, to make string comparison rules explicit. diff --git a/docs/Rules/MA0007.md b/docs/Rules/MA0007.md index b38db4ca1..108a7cb69 100644 --- a/docs/Rules/MA0007.md +++ b/docs/Rules/MA0007.md @@ -1,4 +1,7 @@ # MA0007 - Add a comma after the last value + +Sources: [CommaAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/CommaAnalyzer.cs), [CommaFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/CommaFixer.cs) + The last statement in a multi-line C# initializer or list is missing a trailing comma. diff --git a/docs/Rules/MA0008.md b/docs/Rules/MA0008.md index c45a45b60..6af58de98 100644 --- a/docs/Rules/MA0008.md +++ b/docs/Rules/MA0008.md @@ -1,4 +1,7 @@ # MA0008 - Add StructLayoutAttribute + +Sources: [UseStructLayoutAttributeAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseStructLayoutAttributeAnalyzer.cs), [UseStructLayoutAttributeFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UseStructLayoutAttributeFixer.cs) + The rule reports `struct`s where - There are at least 2 fields diff --git a/docs/Rules/MA0009.md b/docs/Rules/MA0009.md index 9681c9727..6c3a7dd0d 100644 --- a/docs/Rules/MA0009.md +++ b/docs/Rules/MA0009.md @@ -1,4 +1,7 @@ # MA0009 - Add regex evaluation timeout + +Source: [RegexMethodUsageAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/RegexMethodUsageAnalyzer.cs) + ````csharp new Regex(""); // not compliant diff --git a/docs/Rules/MA0010.md b/docs/Rules/MA0010.md index c48862eef..c020cda76 100644 --- a/docs/Rules/MA0010.md +++ b/docs/Rules/MA0010.md @@ -1,4 +1,7 @@ # MA0010 - Mark attributes with AttributeUsageAttribute + +Sources: [MarkAttributesWithAttributeUsageAttributeAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/MarkAttributesWithAttributeUsageAttributeAnalyzer.cs), [MarkAttributesWithAttributeUsageAttributeFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/MarkAttributesWithAttributeUsageAttributeFixer.cs) + When you define a custom attribute, mark it with `[AttributeUsage]` to indicate where in the source code the custom attribute can be applied. diff --git a/docs/Rules/MA0011.md b/docs/Rules/MA0011.md index 60067fd1a..cef8cd3c6 100644 --- a/docs/Rules/MA0011.md +++ b/docs/Rules/MA0011.md @@ -1,4 +1,7 @@ # MA0011 - IFormatProvider is missing + +Source: [UseIFormatProviderAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs) + This rule warns about the usage of overloads of methods like `Parse`, `TryParse` and `ToString` that do not take a parameter of type `IFormatProvider`. diff --git a/docs/Rules/MA0012.md b/docs/Rules/MA0012.md index 5ec180bd6..f046d95e2 100644 --- a/docs/Rules/MA0012.md +++ b/docs/Rules/MA0012.md @@ -1,4 +1,7 @@ # MA0012 - Do not raise reserved exception type + +Source: [DoNotRaiseReservedExceptionTypeAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotRaiseReservedExceptionTypeAnalyzer.cs) + The following exception types are reserved and should be thrown only by the Common Language Runtime: diff --git a/docs/Rules/MA0013.md b/docs/Rules/MA0013.md index 83e971e0f..c5987213b 100644 --- a/docs/Rules/MA0013.md +++ b/docs/Rules/MA0013.md @@ -1,4 +1,7 @@ # MA0013 - Types should not extend System.ApplicationException + +Source: [TypesShouldNotExtendSystemApplicationExceptionAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/TypesShouldNotExtendSystemApplicationExceptionAnalyzer.cs) + Exceptions should derive from `System.Exception` or one of its subclasses in the System namespace. diff --git a/docs/Rules/MA0014.md b/docs/Rules/MA0014.md index 40a9233d6..db9abd079 100644 --- a/docs/Rules/MA0014.md +++ b/docs/Rules/MA0014.md @@ -1,4 +1,7 @@ # MA0014 - Do not raise System.ApplicationException type + +Source: [DoNotRaiseApplicationExceptionAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotRaiseApplicationExceptionAnalyzer.cs) + From [the .NET documentation](https://learn.microsoft.com/en-us/dotnet/api/system.applicationexception?view=netframework-4.7.2&WT.mc_id=DT-MVP-5003978#remarks): diff --git a/docs/Rules/MA0015.md b/docs/Rules/MA0015.md index 1cbc58229..f820bf7a5 100644 --- a/docs/Rules/MA0015.md +++ b/docs/Rules/MA0015.md @@ -1,4 +1,7 @@ # MA0015 - Specify the parameter name in ArgumentException + +Source: [ArgumentExceptionShouldSpecifyArgumentNameAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzer.cs) + ````c# void Sample(string str) diff --git a/docs/Rules/MA0016.md b/docs/Rules/MA0016.md index 01b240aba..50a0ab412 100644 --- a/docs/Rules/MA0016.md +++ b/docs/Rules/MA0016.md @@ -1,4 +1,7 @@ # MA0016 - Prefer using collection abstraction instead of implementation + +Source: [PreferReturningCollectionAbstractionInsteadOfImplementationAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/PreferReturningCollectionAbstractionInsteadOfImplementationAnalyzer.cs) + Methods and properties visible to other assemblies (`public` or `protected`) should use collection interfaces instead of collection implementations. diff --git a/docs/Rules/MA0017.md b/docs/Rules/MA0017.md index c67e734bf..da12997cf 100644 --- a/docs/Rules/MA0017.md +++ b/docs/Rules/MA0017.md @@ -1,4 +1,7 @@ # MA0017 - Abstract types should not have public or internal constructors + +Sources: [AbstractTypesShouldNotHaveConstructorsAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/AbstractTypesShouldNotHaveConstructorsAnalyzer.cs), [AbstractTypesShouldNotHaveConstructorsFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/AbstractTypesShouldNotHaveConstructorsFixer.cs) + Constructors on abstract types can be called only by derived types. Because public constructors create instances of a type, and you cannot create instances of an abstract type, an abstract type that has a public constructor is incorrectly designed. diff --git a/docs/Rules/MA0018.md b/docs/Rules/MA0018.md index 40ddfe036..dacbb4a93 100644 --- a/docs/Rules/MA0018.md +++ b/docs/Rules/MA0018.md @@ -1,4 +1,7 @@ # MA0018 - Do not declare static members on generic types (deprecated; use CA1000 instead) + +Source: [DoNotDeclareStaticMembersOnGenericTypes.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotDeclareStaticMembersOnGenericTypes.cs) + When a static member of a generic type is called, the type argument must be specified for the type. When a generic instance member that does not support inference is called, the type argument must be specified for the member. diff --git a/docs/Rules/MA0019.md b/docs/Rules/MA0019.md index ef045ca0d..b3415ea45 100644 --- a/docs/Rules/MA0019.md +++ b/docs/Rules/MA0019.md @@ -1,4 +1,7 @@ # MA0019 - Use EventArgs.Empty + +Sources: [UseEventArgsEmptyAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseEventArgsEmptyAnalyzer.cs), [UseEventArgsEmptyFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UseEventArgsEmptyFixer.cs) + ````c# MyEvent(this, new EventArgs()); // non-compliant diff --git a/docs/Rules/MA0020.md b/docs/Rules/MA0020.md index bd161747f..891a61f69 100644 --- a/docs/Rules/MA0020.md +++ b/docs/Rules/MA0020.md @@ -1,4 +1,7 @@ # MA0020 - Use direct methods instead of LINQ methods + +Sources: [OptimizeLinqUsageAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/OptimizeLinqUsageAnalyzer.cs), [OptimizeLinqUsageFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeLinqUsageFixer.cs) + By default, the rule generates a diagnostic only when arguments do not need conversion. You can configure the rule to always report diagnostics, as follows: diff --git a/docs/Rules/MA0021.md b/docs/Rules/MA0021.md index 8d521c5b9..65da33107 100644 --- a/docs/Rules/MA0021.md +++ b/docs/Rules/MA0021.md @@ -1,4 +1,7 @@ # MA0021 - Use StringComparer.GetHashCode instead of string.GetHashCode + +Sources: [DoNotUseStringGetHashCodeAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotUseStringGetHashCodeAnalyzer.cs), [DoNotUseStringGetHashCodeFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseStringGetHashCodeFixer.cs) + ````csharp "".GetHashCode(); diff --git a/docs/Rules/MA0022.md b/docs/Rules/MA0022.md index e8e65b35d..43cff7d93 100644 --- a/docs/Rules/MA0022.md +++ b/docs/Rules/MA0022.md @@ -1,4 +1,7 @@ # MA0022 - Return Task.FromResult instead of returning null + +Sources: [ReturnTaskFromResultInsteadOfReturningNullAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/ReturnTaskFromResultInsteadOfReturningNullAnalyzer.cs), [ReturnTaskFromResultInsteadOfReturningNullFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/ReturnTaskFromResultInsteadOfReturningNullFixer.cs) + ````csharp Task Sample() diff --git a/docs/Rules/MA0023.md b/docs/Rules/MA0023.md index 13ec8f4d6..faa77b75d 100644 --- a/docs/Rules/MA0023.md +++ b/docs/Rules/MA0023.md @@ -1,4 +1,7 @@ # MA0023 - Add RegexOptions.ExplicitCapture + +Source: [RegexMethodUsageAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/RegexMethodUsageAnalyzer.cs) + Using named groups clarifies what is to be captured. It also makes the regex more performant, as unnamed groups will not be captured needlessly. diff --git a/docs/Rules/MA0024.md b/docs/Rules/MA0024.md index 8fcbb42aa..9b788a4c2 100644 --- a/docs/Rules/MA0024.md +++ b/docs/Rules/MA0024.md @@ -1,4 +1,7 @@ # MA0024 - Use an explicit StringComparer when possible + +Sources: [DoNotUseEqualityComparerDefaultOfStringAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotUseEqualityComparerDefaultOfStringAnalyzer.cs), [DoNotUseEqualityComparerDefaultOfStringFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseEqualityComparerDefaultOfStringFixer.cs) + You should use an explicit comparer for string. diff --git a/docs/Rules/MA0025.md b/docs/Rules/MA0025.md index 4eb5b77bb..954b47d90 100644 --- a/docs/Rules/MA0025.md +++ b/docs/Rules/MA0025.md @@ -1,4 +1,7 @@ # MA0025 - Implement the functionality instead of throwing NotImplementedException + +Source: [DoNotRaiseNotImplementedExceptionAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotRaiseNotImplementedExceptionAnalyzer.cs) + The `NotImplementedException` exception is thrown when a particular method, get accessor, or set accessor is present as a member of a type but is not implemented. If you don't plan to implement the functionality, you should consider using the `NotSupportedException` exception instead. diff --git a/docs/Rules/MA0026.md b/docs/Rules/MA0026.md index a5e0c74d8..80e7753c6 100644 --- a/docs/Rules/MA0026.md +++ b/docs/Rules/MA0026.md @@ -1,4 +1,7 @@ # MA0026 - Fix TODO comment + +Source: [FixToDoAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/FixToDoAnalyzer.cs) + Resolve the TODO comments in the code. diff --git a/docs/Rules/MA0027.md b/docs/Rules/MA0027.md index 63d57a9bf..760af79a6 100644 --- a/docs/Rules/MA0027.md +++ b/docs/Rules/MA0027.md @@ -1,4 +1,7 @@ # MA0027 - Prefer rethrowing an exception implicitly + +Sources: [DoNotRemoveOriginalExceptionFromThrowStatementAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotRemoveOriginalExceptionFromThrowStatementAnalyzer.cs), [DoNotRemoveOriginalExceptionFromThrowStatementFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotRemoveOriginalExceptionFromThrowStatementFixer.cs) + When an exception is rethrown explicitly (i.e. by specifying it), the original stack trace will be lost, replaced by a new one. To preserve the original call stack, rethrow the exception implicitly (i.e. without specifying it). diff --git a/docs/Rules/MA0028.md b/docs/Rules/MA0028.md index 525a840ae..30d2c41d9 100644 --- a/docs/Rules/MA0028.md +++ b/docs/Rules/MA0028.md @@ -1,4 +1,7 @@ # MA0028 - Optimize StringBuilder usage + +Sources: [OptimizeStringBuilderUsageAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/OptimizeStringBuilderUsageAnalyzer.cs), [OptimizeStringBuilderUsageFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeStringBuilderUsageFixer.cs) + ```csharp new StringBuilder().Append($"a{10}"); diff --git a/docs/Rules/MA0029.md b/docs/Rules/MA0029.md index c1d5d719b..bac38b4f0 100644 --- a/docs/Rules/MA0029.md +++ b/docs/Rules/MA0029.md @@ -1,4 +1,7 @@ # MA0029 - Combine LINQ methods + +Sources: [OptimizeLinqUsageAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/OptimizeLinqUsageAnalyzer.cs), [OptimizeLinqUsageFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeLinqUsageFixer.cs) + Combine LINQ methods when possible diff --git a/docs/Rules/MA0030.md b/docs/Rules/MA0030.md index 353cb0a24..f4678e8d0 100644 --- a/docs/Rules/MA0030.md +++ b/docs/Rules/MA0030.md @@ -1,4 +1,7 @@ # MA0030 - Remove useless OrderBy call + +Sources: [OptimizeLinqUsageAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/OptimizeLinqUsageAnalyzer.cs), [OptimizeLinqUsageFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeLinqUsageFixer.cs) + Remove duplicate `OrderBy` methods, or replace the second `OrderBy` with `ThenBy` diff --git a/docs/Rules/MA0031.md b/docs/Rules/MA0031.md index c9425d3b1..038132e6a 100644 --- a/docs/Rules/MA0031.md +++ b/docs/Rules/MA0031.md @@ -1,4 +1,7 @@ # MA0031 - Optimize Enumerable.Count() usage + +Sources: [OptimizeLinqUsageAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/OptimizeLinqUsageAnalyzer.cs), [OptimizeLinqUsageFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeLinqUsageFixer.cs) + Replace `Count()` by a more optimized method diff --git a/docs/Rules/MA0032.md b/docs/Rules/MA0032.md index 8657073bf..599eb20ee 100644 --- a/docs/Rules/MA0032.md +++ b/docs/Rules/MA0032.md @@ -1,4 +1,7 @@ # MA0032 - Use an overload with a CancellationToken argument + +Source: [UseAnOverloadThatHasCancellationTokenAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasCancellationTokenAnalyzer.cs) + You should pass a `CancellationToken` when calling a method if there is an overload of the method that supports it. diff --git a/docs/Rules/MA0033.md b/docs/Rules/MA0033.md index 98e9414d5..0d4458cd8 100644 --- a/docs/Rules/MA0033.md +++ b/docs/Rules/MA0033.md @@ -1,4 +1,7 @@ # MA0033 - Do not tag instance fields with ThreadStaticAttribute + +Source: [DontTagInstanceFieldsWithThreadStaticAttributeAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DontTagInstanceFieldsWithThreadStaticAttributeAnalyzer.cs) + ```csharp [ThreadStatic] diff --git a/docs/Rules/MA0035.md b/docs/Rules/MA0035.md index 5509d512a..51587bdbb 100644 --- a/docs/Rules/MA0035.md +++ b/docs/Rules/MA0035.md @@ -1,4 +1,7 @@ # MA0035 - Do not use dangerous threading methods + +Source: [DontUseDangerousThreadingMethodsAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DontUseDangerousThreadingMethodsAnalyzer.cs) + This rule warns about using the methods `Abort()`, `Suspend()` or `Resume()` declared by the `Thread` class. diff --git a/docs/Rules/MA0036.md b/docs/Rules/MA0036.md index 9b471f18a..13935a4b2 100644 --- a/docs/Rules/MA0036.md +++ b/docs/Rules/MA0036.md @@ -1,4 +1,7 @@ # MA0036 - Make class static + +Sources: [MakeClassStaticAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/MakeClassStaticAnalyzer.cs), [MakeClassStaticFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/MakeClassStaticFixer.cs) + If the class only contains static or const members, you should consider making the class static. diff --git a/docs/Rules/MA0037.md b/docs/Rules/MA0037.md index 2c7f51e3a..fc8c61e56 100644 --- a/docs/Rules/MA0037.md +++ b/docs/Rules/MA0037.md @@ -1,4 +1,7 @@ # MA0037 - Remove empty statement + +Sources: [RemoveEmptyStatementAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/RemoveEmptyStatementAnalyzer.cs), [RemoveEmptyStatementFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/RemoveEmptyStatementFixer.cs) + Empty statements are useless and may be due to typing errors. diff --git a/docs/Rules/MA0038.md b/docs/Rules/MA0038.md index 409cb4e68..68c5c4006 100644 --- a/docs/Rules/MA0038.md +++ b/docs/Rules/MA0038.md @@ -1,4 +1,7 @@ # MA0038 - Make method static (deprecated, use CA1822 instead) + +Sources: [MakeMethodStaticAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/MakeMethodStaticAnalyzer.cs), [MakeMethodStaticFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/MakeMethodStaticFixer.cs) + > **Warning** > This rule is deprecated and will be removed in the future. It's recommended to use use [CA1822](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1822?WT.mc_id=DT-MVP-5003978) instead. diff --git a/docs/Rules/MA0039.md b/docs/Rules/MA0039.md index 5e3f59437..12a3df4d6 100644 --- a/docs/Rules/MA0039.md +++ b/docs/Rules/MA0039.md @@ -1,4 +1,7 @@ # MA0039 - Do not write your own certificate validation method + +Source: [DoNotUseServerCertificateValidationCallbackAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotUseServerCertificateValidationCallbackAnalyzer.cs) + You should not write methods to validate certificates. Those methods are often used to bypass validation altogether, which is bad for security. diff --git a/docs/Rules/MA0040.md b/docs/Rules/MA0040.md index 525c865aa..7e88c5da5 100644 --- a/docs/Rules/MA0040.md +++ b/docs/Rules/MA0040.md @@ -1,4 +1,7 @@ # MA0040 - Forward the CancellationToken parameter to methods that take one + +Sources: [UseAnOverloadThatHasCancellationTokenAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasCancellationTokenAnalyzer.cs), [UseAnOverloadThatHasCancellationTokenFixer_Argument.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UseAnOverloadThatHasCancellationTokenFixer_Argument.cs) + If there is a `CancellationToken` in scope, it should be passed along when calling a method that supports it. diff --git a/docs/Rules/MA0041.md b/docs/Rules/MA0041.md index 2e3676f15..fe7e23455 100644 --- a/docs/Rules/MA0041.md +++ b/docs/Rules/MA0041.md @@ -1,4 +1,7 @@ # MA0041 - Make property static (deprecated, use CA1822 instead) + +Sources: [MakeMethodStaticAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/MakeMethodStaticAnalyzer.cs), [MakeMethodStaticFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/MakeMethodStaticFixer.cs) + > **Warning** > This rule is deprecated and will be removed in the future. It's recommended to use use [CA1822](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1822?WT.mc_id=DT-MVP-5003978) instead. diff --git a/docs/Rules/MA0042.md b/docs/Rules/MA0042.md index b35b1992b..7fe42c3b2 100644 --- a/docs/Rules/MA0042.md +++ b/docs/Rules/MA0042.md @@ -1,4 +1,7 @@ # MA0042 - Do not use blocking calls in an async method + +Sources: [DoNotUseBlockingCallInAsyncContextAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer.cs), [DoNotUseBlockingCallInAsyncContextFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseBlockingCallInAsyncContextFixer.cs) + You should replace blocking calls, such as `Wait` or `Result`, with an async call using `await`. diff --git a/docs/Rules/MA0043.md b/docs/Rules/MA0043.md index 5501b59d0..a7de27d6d 100644 --- a/docs/Rules/MA0043.md +++ b/docs/Rules/MA0043.md @@ -1,4 +1,7 @@ # MA0043 - Use nameof operator in ArgumentException + +Sources: [ArgumentExceptionShouldSpecifyArgumentNameAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/ArgumentExceptionShouldSpecifyArgumentNameAnalyzer.cs), [ArgumentExceptionShouldSpecifyArgumentNameFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/ArgumentExceptionShouldSpecifyArgumentNameFixer.cs) + When throwing an `ArgumentException`, you should use the `nameof` operator to specify the argument name. diff --git a/docs/Rules/MA0044.md b/docs/Rules/MA0044.md index 8034d673b..f20b964b2 100644 --- a/docs/Rules/MA0044.md +++ b/docs/Rules/MA0044.md @@ -1,4 +1,7 @@ # MA0044 - Remove useless ToString call + +Sources: [RemoveUselessToStringAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/RemoveUselessToStringAnalyzer.cs), [RemoveUselessToStringFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/RemoveUselessToStringFixer.cs) + `string.ToString` is a no-op call. You should remove the call to `ToString()`. diff --git a/docs/Rules/MA0045.md b/docs/Rules/MA0045.md index a2ed58f31..5a7a1061b 100644 --- a/docs/Rules/MA0045.md +++ b/docs/Rules/MA0045.md @@ -1,4 +1,7 @@ # MA0045 - Do not use blocking calls in a sync method (need to make calling method async) + +Sources: [DoNotUseBlockingCallInAsyncContextAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotUseBlockingCallInAsyncContextAnalyzer.cs), [DoNotUseBlockingCallInAsyncContextFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseBlockingCallInAsyncContextFixer.cs) + You should replace blocking calls, such as `Wait` or `Result`, with an async call using `await`. The calling method will need to become `async`. diff --git a/docs/Rules/MA0046.md b/docs/Rules/MA0046.md index 7ddc10d5e..38931ac37 100644 --- a/docs/Rules/MA0046.md +++ b/docs/Rules/MA0046.md @@ -1,4 +1,7 @@ # MA0046 - Use EventHandler\ to declare events + +Source: [UseEventHandlerOfTAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseEventHandlerOfTAnalyzer.cs) + You should use `EventHandler` to declare events, where `T` inherits from `System.EventArgs`. diff --git a/docs/Rules/MA0047.md b/docs/Rules/MA0047.md index fff387533..a00ab15b1 100644 --- a/docs/Rules/MA0047.md +++ b/docs/Rules/MA0047.md @@ -1,3 +1,6 @@ # MA0047 - Declare types in namespaces + +Source: [DeclareTypesInNamespacesAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DeclareTypesInNamespacesAnalyzer.cs) + Types are declared in namespaces to prevent name collisions, and as a way to organize related types in an object hierarchy. Types that are outside any named namespace are in a global namespace that cannot be referenced in code. diff --git a/docs/Rules/MA0048.md b/docs/Rules/MA0048.md index 2a6cfcc0c..3e220884e 100644 --- a/docs/Rules/MA0048.md +++ b/docs/Rules/MA0048.md @@ -1,4 +1,7 @@ # MA0048 - File name must match type name + +Source: [FileNameMustMatchTypeNameAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/FileNameMustMatchTypeNameAnalyzer.cs) + The name of the class must match to name of the file. This rule has two main reasons: - Ensuring the type name is the same as the file name. This prevents renaming a type without renaming the file. diff --git a/docs/Rules/MA0049.md b/docs/Rules/MA0049.md index 7cead79f9..348116456 100644 --- a/docs/Rules/MA0049.md +++ b/docs/Rules/MA0049.md @@ -1,4 +1,7 @@ # MA0049 - Type name should not match containing namespace + +Source: [TypeNameMustNotMatchNamespaceAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/TypeNameMustNotMatchNamespaceAnalyzer.cs) + For usability, you should not name a type as the namespace. This would be harder to use it in the code. diff --git a/docs/Rules/MA0050.md b/docs/Rules/MA0050.md index 2b7d3b6ac..9e18ed5a6 100644 --- a/docs/Rules/MA0050.md +++ b/docs/Rules/MA0050.md @@ -1,4 +1,7 @@ # MA0050 - Validate arguments correctly in iterator methods + +Sources: [ValidateArgumentsCorrectlyAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/ValidateArgumentsCorrectlyAnalyzer.cs), [ValidateArgumentsCorrectlyFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/ValidateArgumentsCorrectlyFixer.cs) + When a method contains a `yield` statement, the evaluation of the method is deferred until the first enumeration. This rule ensures that arguments are validated immediately, while execution of the rest of the method is still deferred. diff --git a/docs/Rules/MA0051.md b/docs/Rules/MA0051.md index 7a86512c8..c972f8788 100644 --- a/docs/Rules/MA0051.md +++ b/docs/Rules/MA0051.md @@ -1,4 +1,7 @@ # MA0051 - Method is too long + +Source: [MethodShouldNotBeTooLongAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/MethodShouldNotBeTooLongAnalyzer.cs) + Long methods are harder to understand. You should refactor them when possible. diff --git a/docs/Rules/MA0052.md b/docs/Rules/MA0052.md index 41946ea51..508a85cea 100644 --- a/docs/Rules/MA0052.md +++ b/docs/Rules/MA0052.md @@ -1,4 +1,7 @@ # MA0052 - Replace constant Enum.ToString with nameof + +Sources: [ReplaceEnumToStringWithNameofAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/ReplaceEnumToStringWithNameofAnalyzer.cs), [ReplaceEnumToStringWithNameofFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/ReplaceEnumToStringWithNameofFixer.cs) + You should use `nameof` instead of calling `ToString` on a constant enumeration value. This is mainly for performance reason. diff --git a/docs/Rules/MA0053.md b/docs/Rules/MA0053.md index ade066fc2..5a8498595 100644 --- a/docs/Rules/MA0053.md +++ b/docs/Rules/MA0053.md @@ -1,4 +1,7 @@ # MA0053 - Make class sealed + +Sources: [ClassMustBeSealedAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/ClassMustBeSealedAnalyzer.cs), [ClassMustBeSealedFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/ClassMustBeSealedFixer.cs) + Classes should be sealed when there is no inheritor. diff --git a/docs/Rules/MA0054.md b/docs/Rules/MA0054.md index 4f752da55..44f7c24b7 100644 --- a/docs/Rules/MA0054.md +++ b/docs/Rules/MA0054.md @@ -1,4 +1,7 @@ # MA0054 - Embed the caught exception as innerException + +Source: [EmbedCaughtExceptionAsInnerExceptionAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/EmbedCaughtExceptionAsInnerExceptionAnalyzer.cs) + You should include the original exception when throwing a new exception from a `catch` block. Providing the original exception could make it easier to debug. diff --git a/docs/Rules/MA0055.md b/docs/Rules/MA0055.md index 165f479c9..47caaf4c8 100644 --- a/docs/Rules/MA0055.md +++ b/docs/Rules/MA0055.md @@ -1,4 +1,7 @@ # MA0055 - Do not use finalizer + +Source: [DoNotUseFinalizerAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotUseFinalizerAnalyzer.cs) + Finalizers should be used to ensure native resources are released. Since .NET 2.0, you should better encapsulate the native resources into a [`System.Runtime.InteropServices.SafeHandle`](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.safehandle?WT.mc_id=DT-MVP-5003978). diff --git a/docs/Rules/MA0056.md b/docs/Rules/MA0056.md index 6de5a8130..597b3de6b 100644 --- a/docs/Rules/MA0056.md +++ b/docs/Rules/MA0056.md @@ -1,4 +1,7 @@ # MA0056 - Do not call overridable members in constructor + +Source: [DoNotCallVirtualMethodInConstructorAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotCallVirtualMethodInConstructorAnalyzer.cs) + When a virtual method is called, the actual type that executes the method is not selected until run time. When a constructor calls a virtual method, it's possible that the constructor for the actual instance type has not been executed yet. diff --git a/docs/Rules/MA0057.md b/docs/Rules/MA0057.md index 23b1a1d06..238721d12 100644 --- a/docs/Rules/MA0057.md +++ b/docs/Rules/MA0057.md @@ -1,4 +1,7 @@ # MA0057 - Class name should end with 'Attribute' + +Source: [AttributeNameShouldEndWithAttributeAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/AttributeNameShouldEndWithAttributeAnalyzer.cs) + A class that derives from `System.Attribute` should have a name that ends with 'Attribute'. diff --git a/docs/Rules/MA0058.md b/docs/Rules/MA0058.md index 424d4d318..a4c770c80 100644 --- a/docs/Rules/MA0058.md +++ b/docs/Rules/MA0058.md @@ -1,4 +1,7 @@ # MA0058 - Class name should end with 'Exception' + +Source: [ExceptionNameShouldEndWithExceptionAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/ExceptionNameShouldEndWithExceptionAnalyzer.cs) + A class that derives from `System.Exception` should have a name that ends with 'Exception'. diff --git a/docs/Rules/MA0059.md b/docs/Rules/MA0059.md index 04c9e5b1b..f4d6662b4 100644 --- a/docs/Rules/MA0059.md +++ b/docs/Rules/MA0059.md @@ -1,4 +1,7 @@ # MA0059 - Class name should end with 'EventArgs' + +Source: [EventArgsNameShouldEndWithEventArgsAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/EventArgsNameShouldEndWithEventArgsAnalyzer.cs) + A class that derives from `System.EventArgs` should have a name that ends with 'EventArgs'. diff --git a/docs/Rules/MA0060.md b/docs/Rules/MA0060.md index 9b191d484..b5b05e087 100644 --- a/docs/Rules/MA0060.md +++ b/docs/Rules/MA0060.md @@ -1,4 +1,7 @@ # MA0060 - The value returned by Stream.Read/Stream.ReadAsync is not used + +Source: [ValueReturnedByStreamReadShouldBeUsedAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/ValueReturnedByStreamReadShouldBeUsedAnalyzer.cs) + You should use the value of `Stream.Read` to know how many bytes were actually read. This can be less than the number of bytes requested, if that many bytes are not currently available, or zero if the end of the stream was reached. diff --git a/docs/Rules/MA0061.md b/docs/Rules/MA0061.md index 43e77d8ff..0f0c947d1 100644 --- a/docs/Rules/MA0061.md +++ b/docs/Rules/MA0061.md @@ -1,4 +1,7 @@ # MA0061 - Method overrides should not change default values + +Sources: [MethodOverridesShouldNotChangeParameterDefaultsAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/MethodOverridesShouldNotChangeParameterDefaultsAnalyzer.cs), [MethodOverridesShouldNotChangeParameterDefaultsFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/MethodOverridesShouldNotChangeParameterDefaultsFixer.cs) + Default arguments used in a call are determined by the static type of the object. If a default argument is different in an overriding method, the value used will be different when calls are made via the base or derived types, which may be contrary to developers' expectations. diff --git a/docs/Rules/MA0062.md b/docs/Rules/MA0062.md index b343936e2..50cc3942f 100644 --- a/docs/Rules/MA0062.md +++ b/docs/Rules/MA0062.md @@ -1,4 +1,7 @@ # MA0062 - Non-flags enums should not be marked with "FlagsAttribute" + +Source: [NonFlagsEnumsShouldNotBeMarkedWithFlagsAttributeAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/NonFlagsEnumsShouldNotBeMarkedWithFlagsAttributeAnalyzer.cs) + An enumeration marked with `FlagsAttribute` should only contain members whose values are powers of two or a combination of such members. diff --git a/docs/Rules/MA0063.md b/docs/Rules/MA0063.md index 3cbaae9ea..b96963716 100644 --- a/docs/Rules/MA0063.md +++ b/docs/Rules/MA0063.md @@ -1,4 +1,7 @@ # MA0063 - Use Where before OrderBy + +Source: [OptimizeLinqUsageAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/OptimizeLinqUsageAnalyzer.cs) + Using `Where` clause after `OrderBy` clause requires the whole collection to be sorted and then filtered. `Where` should be called first to sort only the filtered items. diff --git a/docs/Rules/MA0064.md b/docs/Rules/MA0064.md index 2be11d601..226b9a2e5 100644 --- a/docs/Rules/MA0064.md +++ b/docs/Rules/MA0064.md @@ -1,4 +1,7 @@ # MA0064 - Avoid locking on publicly accessible instance + +Source: [AvoidLockingOnPubliclyAccessibleInstanceAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/AvoidLockingOnPubliclyAccessibleInstanceAnalyzer.cs) + ````csharp lock(this) { } // non compliant diff --git a/docs/Rules/MA0065.md b/docs/Rules/MA0065.md index 8c08029d2..665392e5b 100644 --- a/docs/Rules/MA0065.md +++ b/docs/Rules/MA0065.md @@ -1,4 +1,7 @@ # MA0065 - Default ValueType.Equals or HashCode is used for struct equality + +Source: [DoNotUseDefaultEqualsOnValueTypeAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotUseDefaultEqualsOnValueTypeAnalyzer.cs) + The default implementation of `Equals` and `GetHashCode` is not performant. You should override those methods in the type. diff --git a/docs/Rules/MA0066.md b/docs/Rules/MA0066.md index 1fc8fc9b0..3b476f195 100644 --- a/docs/Rules/MA0066.md +++ b/docs/Rules/MA0066.md @@ -1,4 +1,7 @@ # MA0066 - Hash table unfriendly type is used in a hash table + +Source: [DoNotUseDefaultEqualsOnValueTypeAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotUseDefaultEqualsOnValueTypeAnalyzer.cs) + The default implementation of `Equals` and `GetHashCode` is not performant. Those methods are used by `HashSet`, `Dictionary`, and similar types. You should override them in the type or use a custom `IEqualityComparer`. diff --git a/docs/Rules/MA0067.md b/docs/Rules/MA0067.md index ac4c4829a..5b540ab4e 100644 --- a/docs/Rules/MA0067.md +++ b/docs/Rules/MA0067.md @@ -1,4 +1,7 @@ # MA0067 - Use Guid.Empty + +Sources: [UseGuidEmptyAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseGuidEmptyAnalyzer.cs), [UseGuidEmptyFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UseGuidEmptyFixer.cs) + ````csharp _ = new Guid(); // non-compliant diff --git a/docs/Rules/MA0068.md b/docs/Rules/MA0068.md index cf0e1c77d..6ae0ed45c 100644 --- a/docs/Rules/MA0068.md +++ b/docs/Rules/MA0068.md @@ -1,4 +1,7 @@ # MA0068 - Invalid parameter name for nullable attribute + +Source: [NullableAttributeUsageAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/NullableAttributeUsageAnalyzer.cs) + ````csharp class Test diff --git a/docs/Rules/MA0069.md b/docs/Rules/MA0069.md index 3b8cb47eb..b84430196 100644 --- a/docs/Rules/MA0069.md +++ b/docs/Rules/MA0069.md @@ -1,4 +1,7 @@ # MA0069 - Non-constant static fields should not be visible + +Source: [NonConstantStaticFieldsShouldNotBeVisibleAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/NonConstantStaticFieldsShouldNotBeVisibleAnalyzer.cs) + Static fields that are neither constant nor read-only are not thread-safe. Access to such fields must be carefully controlled and requires advanced programming techniques for synchronizing access diff --git a/docs/Rules/MA0070.md b/docs/Rules/MA0070.md index a3b9ce737..82a4dac01 100644 --- a/docs/Rules/MA0070.md +++ b/docs/Rules/MA0070.md @@ -1,4 +1,7 @@ # MA0070 - Obsolete attributes should include explanations + +Source: [ObsoleteAttributesShouldIncludeExplanationsAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/ObsoleteAttributesShouldIncludeExplanationsAnalyzer.cs) + ````csharp [Obsolete] // non-compliant diff --git a/docs/Rules/MA0071.md b/docs/Rules/MA0071.md index 4f4dcacd4..4a771e7ba 100644 --- a/docs/Rules/MA0071.md +++ b/docs/Rules/MA0071.md @@ -1,4 +1,7 @@ # MA0071 - Avoid using redundant else + +Sources: [AvoidUsingRedundantElseAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/AvoidUsingRedundantElseAnalyzer.cs), [AvoidUsingRedundantElseFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/AvoidUsingRedundantElseFixer.cs) + When an `if` block contains a jump statement (`break`, `continue`, `goto`, `return`, `throw`, `yield break`), using an `else` block is redundant and needlessly maintains a higher nesting level. diff --git a/docs/Rules/MA0072.md b/docs/Rules/MA0072.md index 61cce941d..17f49a66d 100644 --- a/docs/Rules/MA0072.md +++ b/docs/Rules/MA0072.md @@ -1,4 +1,7 @@ # MA0072 - Do not throw from a finally block + +Source: [DoNotThrowFromFinallyBlockAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotThrowFromFinallyBlockAnalyzer.cs) + Throwing an exception from a `finally` block may hide an exception thrown from the `try` or `catch` block. diff --git a/docs/Rules/MA0073.md b/docs/Rules/MA0073.md index d91e5f866..4f35f2d5c 100644 --- a/docs/Rules/MA0073.md +++ b/docs/Rules/MA0073.md @@ -1,4 +1,7 @@ # MA0073 - Avoid comparison with bool constant + +Sources: [AvoidComparisonWithBoolConstantAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/AvoidComparisonWithBoolConstantAnalyzer.cs), [AvoidComparisonWithBoolConstantFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/AvoidComparisonWithBoolConstantFixer.cs) + ## Non-compliant code diff --git a/docs/Rules/MA0074.md b/docs/Rules/MA0074.md index fbf62882d..edc2ac429 100644 --- a/docs/Rules/MA0074.md +++ b/docs/Rules/MA0074.md @@ -1,4 +1,7 @@ # MA0074 - Avoid implicit culture-sensitive methods + +Sources: [UseStringComparisonAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseStringComparisonAnalyzer.cs), [UseStringComparisonFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UseStringComparisonFixer.cs) + String manipulation methods in .NET do not all use the same default rules for string comparison by culture and case. For instance, `string.Equals(string, string)` uses `StringComparison.Ordinal` whereas `IndexOf(String)` uses `StringComparison.CurrentCulture`. So, you should use an overload that does not rely on default behavior. diff --git a/docs/Rules/MA0075.md b/docs/Rules/MA0075.md index 21955540b..d09509b65 100644 --- a/docs/Rules/MA0075.md +++ b/docs/Rules/MA0075.md @@ -1,4 +1,7 @@ # MA0075 - Do not use implicit culture-sensitive ToString + +Source: [DoNotUseImplicitCultureSensitiveToStringAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotUseImplicitCultureSensitiveToStringAnalyzer.cs) + This rule reports cases where an implicit call to `obj.ToString()` may differ based on the current culture: diff --git a/docs/Rules/MA0076.md b/docs/Rules/MA0076.md index 38bd1f162..68d65c19b 100644 --- a/docs/Rules/MA0076.md +++ b/docs/Rules/MA0076.md @@ -1,4 +1,7 @@ # MA0076 - Do not use implicit culture-sensitive ToString in interpolated strings + +Source: [DoNotUseImplicitCultureSensitiveToStringAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotUseImplicitCultureSensitiveToStringAnalyzer.cs) + ````csharp _ = $"abc{-1}"; // non-compliant as the result depends on the current culture diff --git a/docs/Rules/MA0077.md b/docs/Rules/MA0077.md index 1fd9b2fb2..45283d0e8 100644 --- a/docs/Rules/MA0077.md +++ b/docs/Rules/MA0077.md @@ -1,4 +1,7 @@ # MA0077 - A class that provides Equals(T) should implement IEquatable\ + +Sources: [EqualityShouldBeCorrectlyImplementedAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/EqualityShouldBeCorrectlyImplementedAnalyzer.cs), [EqualityShouldBeCorrectlyImplementedFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/EqualityShouldBeCorrectlyImplementedFixer.cs) + ## Non-compliant code diff --git a/docs/Rules/MA0078.md b/docs/Rules/MA0078.md index 74221eaef..a8c990d00 100644 --- a/docs/Rules/MA0078.md +++ b/docs/Rules/MA0078.md @@ -1,4 +1,7 @@ # MA0078 - Use 'Cast' instead of 'Select' to cast + +Sources: [OptimizeLinqUsageAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/OptimizeLinqUsageAnalyzer.cs), [OptimizeLinqUsageFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeLinqUsageFixer.cs) + ## Non-compliant code diff --git a/docs/Rules/MA0079.md b/docs/Rules/MA0079.md index 48e33bf67..0242d39e1 100644 --- a/docs/Rules/MA0079.md +++ b/docs/Rules/MA0079.md @@ -1,4 +1,7 @@ # MA0079 - Forward the CancellationToken using .WithCancellation() + +Sources: [UseAnOverloadThatHasCancellationTokenAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasCancellationTokenAnalyzer.cs), [UseAnOverloadThatHasCancellationTokenFixer_AwaitForEach.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UseAnOverloadThatHasCancellationTokenFixer_AwaitForEach.cs) + ````c# CancellationToken cancellationToken; diff --git a/docs/Rules/MA0080.md b/docs/Rules/MA0080.md index 7c397e27d..9f6863775 100644 --- a/docs/Rules/MA0080.md +++ b/docs/Rules/MA0080.md @@ -1,4 +1,7 @@ # MA0080 - Use a cancellation token using .WithCancellation() + +Source: [UseAnOverloadThatHasCancellationTokenAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasCancellationTokenAnalyzer.cs) + ````c# IAsyncEnumeration enumerable; diff --git a/docs/Rules/MA0081.md b/docs/Rules/MA0081.md index d15a10ee9..a0bf602b3 100644 --- a/docs/Rules/MA0081.md +++ b/docs/Rules/MA0081.md @@ -1,4 +1,7 @@ # MA0081 - Method overrides should not omit params keyword + +Sources: [PreserveParamsOnOverrideAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/PreserveParamsOnOverrideAnalyzer.cs), [PreserveParamsOnOverrideFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/PreserveParamsOnOverrideFixer.cs) + ````csharp class Base diff --git a/docs/Rules/MA0082.md b/docs/Rules/MA0082.md index 501d0d572..a070be690 100644 --- a/docs/Rules/MA0082.md +++ b/docs/Rules/MA0082.md @@ -1,4 +1,7 @@ # MA0082 - NaN should not be used in comparisons + +Source: [DoNotNaNInComparisonsAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotNaNInComparisonsAnalyzer.cs) + ````c# if (a == double.NaN) // non-compliant as it always returns false, should be double.IsNaN(a) diff --git a/docs/Rules/MA0083.md b/docs/Rules/MA0083.md index 6c52029bd..a81b8a584 100644 --- a/docs/Rules/MA0083.md +++ b/docs/Rules/MA0083.md @@ -1,4 +1,7 @@ # MA0083 - ConstructorArgument parameters should exist in constructors + +Source: [ConstructorArgumentParametersShouldExistInConstructorsAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/ConstructorArgumentParametersShouldExistInConstructorsAnalyzer.cs) + ````c# using System.Windows.Markup; diff --git a/docs/Rules/MA0084.md b/docs/Rules/MA0084.md index 8ef78d43a..264c50e8a 100644 --- a/docs/Rules/MA0084.md +++ b/docs/Rules/MA0084.md @@ -1,4 +1,7 @@ # MA0084 - Local variables should not hide other symbols + +Source: [LocalVariablesShouldNotHideSymbolsAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/LocalVariablesShouldNotHideSymbolsAnalyzer.cs) + ````csharp class Test diff --git a/docs/Rules/MA0085.md b/docs/Rules/MA0085.md index 85e6bcae4..8832c1ce1 100644 --- a/docs/Rules/MA0085.md +++ b/docs/Rules/MA0085.md @@ -1,4 +1,7 @@ # MA0085 - Anonymous delegates should not be used to unsubscribe from Events + +Source: [AnonymousDelegatesShouldNotBeUsedToUnsubscribeFromEventsAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/AnonymousDelegatesShouldNotBeUsedToUnsubscribeFromEventsAnalyzer.cs) + ````c# MyEvent += (sender, e) => { } diff --git a/docs/Rules/MA0086.md b/docs/Rules/MA0086.md index fb101388f..7cebe3fba 100644 --- a/docs/Rules/MA0086.md +++ b/docs/Rules/MA0086.md @@ -1,4 +1,7 @@ # MA0086 - Do not throw from a finalizer + +Source: [DoNotThrowFromFinalizerAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotThrowFromFinalizerAnalyzer.cs) + ````csharp class Test diff --git a/docs/Rules/MA0087.md b/docs/Rules/MA0087.md index f48cb7def..6beb2046e 100644 --- a/docs/Rules/MA0087.md +++ b/docs/Rules/MA0087.md @@ -1,4 +1,7 @@ # MA0087 - Parameters with \[DefaultParameterValue\] attributes should also be marked \[Optional\] + +Source: [OptionalParametersAttributeAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/OptionalParametersAttributeAnalyzer.cs) + ````csharp using System.Runtime.InteropServices; diff --git a/docs/Rules/MA0088.md b/docs/Rules/MA0088.md index 33d0066ae..8da7710c5 100644 --- a/docs/Rules/MA0088.md +++ b/docs/Rules/MA0088.md @@ -1,4 +1,7 @@ # MA0088 - Use \[DefaultParameterValue\] instead of \[DefaultValue\] + +Source: [OptionalParametersAttributeAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/OptionalParametersAttributeAnalyzer.cs) + ````csharp using System.ComponentModel; diff --git a/docs/Rules/MA0089.md b/docs/Rules/MA0089.md index 4d49f01d7..025097ec0 100644 --- a/docs/Rules/MA0089.md +++ b/docs/Rules/MA0089.md @@ -1,4 +1,7 @@ # MA0089 - Optimize string method usage + +Sources: [OptimizeStartsWithAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/OptimizeStartsWithAnalyzer.cs), [OptimizeStartsWithFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeStartsWithFixer.cs) + ````c# string str; diff --git a/docs/Rules/MA0090.md b/docs/Rules/MA0090.md index 3f20269fb..d00d8f740 100644 --- a/docs/Rules/MA0090.md +++ b/docs/Rules/MA0090.md @@ -1,4 +1,7 @@ # MA0090 - Remove empty else/finally block + +Source: [RemoveEmptyBlockAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/RemoveEmptyBlockAnalyzer.cs) + This rule detects empty `else` and `finally` blocks that contain no executable code and suggests removing them to improve code readability and reduce clutter. Empty blocks serve no functional purpose and can make code harder to read by adding unnecessary visual noise. diff --git a/docs/Rules/MA0091.md b/docs/Rules/MA0091.md index a92bace73..ae5083b27 100644 --- a/docs/Rules/MA0091.md +++ b/docs/Rules/MA0091.md @@ -1,4 +1,7 @@ # MA0091 - Sender should be 'this' for instance events + +Sources: [EventsShouldHaveProperArgumentsAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/EventsShouldHaveProperArgumentsAnalyzer.cs), [EventsShouldHaveProperArgumentsFixer.MA0091.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/EventsShouldHaveProperArgumentsFixer.MA0091.cs) + ````c# using System; diff --git a/docs/Rules/MA0092.md b/docs/Rules/MA0092.md index 011940f93..324d9316a 100644 --- a/docs/Rules/MA0092.md +++ b/docs/Rules/MA0092.md @@ -1,4 +1,7 @@ # MA0092 - Sender should be 'null' for static events + +Source: [EventsShouldHaveProperArgumentsAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/EventsShouldHaveProperArgumentsAnalyzer.cs) + ````c# using System; diff --git a/docs/Rules/MA0093.md b/docs/Rules/MA0093.md index d3f305be0..c611bd607 100644 --- a/docs/Rules/MA0093.md +++ b/docs/Rules/MA0093.md @@ -1,4 +1,7 @@ # MA0093 - EventArgs should not be null + +Sources: [EventsShouldHaveProperArgumentsAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/EventsShouldHaveProperArgumentsAnalyzer.cs), [UseEventArgsEmptyFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UseEventArgsEmptyFixer.cs) + ````c# using System; diff --git a/docs/Rules/MA0094.md b/docs/Rules/MA0094.md index baa96b231..1fec1857e 100644 --- a/docs/Rules/MA0094.md +++ b/docs/Rules/MA0094.md @@ -1,4 +1,7 @@ # MA0094 - A class that provides CompareTo(T) should implement IComparable\ + +Source: [EqualityShouldBeCorrectlyImplementedAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/EqualityShouldBeCorrectlyImplementedAnalyzer.cs) + ````c# class Test // non-compliant diff --git a/docs/Rules/MA0095.md b/docs/Rules/MA0095.md index 7e3519222..52497acb4 100644 --- a/docs/Rules/MA0095.md +++ b/docs/Rules/MA0095.md @@ -1,4 +1,7 @@ # MA0095 - A class that implements IEquatable\ should override Equals(object) + +Source: [EqualityShouldBeCorrectlyImplementedAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/EqualityShouldBeCorrectlyImplementedAnalyzer.cs) + ````c# class Test : IEquatable // non-compliant diff --git a/docs/Rules/MA0096.md b/docs/Rules/MA0096.md index f2dff74f7..be4a7f2d5 100644 --- a/docs/Rules/MA0096.md +++ b/docs/Rules/MA0096.md @@ -1,4 +1,7 @@ # MA0096 - A class that implements IComparable\ should also implement IEquatable\ + +Source: [EqualityShouldBeCorrectlyImplementedAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/EqualityShouldBeCorrectlyImplementedAnalyzer.cs) + ````c# class Test : IComparable // non-compliant diff --git a/docs/Rules/MA0097.md b/docs/Rules/MA0097.md index b8c4d711a..01c055d97 100644 --- a/docs/Rules/MA0097.md +++ b/docs/Rules/MA0097.md @@ -1,4 +1,7 @@ # MA0097 - A class that implements IComparable\ or IComparable should override comparison operators + +Source: [EqualityShouldBeCorrectlyImplementedAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/EqualityShouldBeCorrectlyImplementedAnalyzer.cs) + ````c# class Test : IComparable // non-compliant diff --git a/docs/Rules/MA0098.md b/docs/Rules/MA0098.md index 48a0f87a8..7e3e76099 100644 --- a/docs/Rules/MA0098.md +++ b/docs/Rules/MA0098.md @@ -1,4 +1,7 @@ # MA0098 - Use indexer instead of LINQ methods + +Sources: [OptimizeLinqUsageAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/OptimizeLinqUsageAnalyzer.cs), [OptimizeLinqUsageFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeLinqUsageFixer.cs) + When working with collections that support indexing (such as arrays, `List`, `IList`, or `IReadOnlyList`), using the indexer syntax `[index]` is more efficient than LINQ methods like `ElementAt()`, `First()`, and `Last()`. This rule suggests replacing these LINQ methods with direct indexer access for better performance. diff --git a/docs/Rules/MA0099.md b/docs/Rules/MA0099.md index e115358ee..e97b1522b 100644 --- a/docs/Rules/MA0099.md +++ b/docs/Rules/MA0099.md @@ -1,4 +1,7 @@ # MA0099 - Use Explicit enum value instead of 0 + +Source: [DoNotUseZeroToInitializeAnEnumValue.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotUseZeroToInitializeAnEnumValue.cs) + ````c# enum MyEnum { A = 0, B = 1 } diff --git a/docs/Rules/MA0100.md b/docs/Rules/MA0100.md index ee68af261..0e3352556 100644 --- a/docs/Rules/MA0100.md +++ b/docs/Rules/MA0100.md @@ -1,4 +1,7 @@ # MA0100 - Await task before disposing of resources + +Source: [AwaitTaskBeforeDisposingResourcesAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/AwaitTaskBeforeDisposingResourcesAnalyzer.cs) + The rule detects `Task`, `Task`, `ValueTask`, `ValueTask`, or any type that follows the awaitable pattern. diff --git a/docs/Rules/MA0101.md b/docs/Rules/MA0101.md index 594862516..319612cb1 100644 --- a/docs/Rules/MA0101.md +++ b/docs/Rules/MA0101.md @@ -1,4 +1,7 @@ # MA0101 - String contains an implicit end of line character + +Sources: [StringShouldNotContainsNonDeterministicEndOfLineAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/StringShouldNotContainsNonDeterministicEndOfLineAnalyzer.cs), [StringShouldNotContainsNonDeterministicEndOfLineFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/StringShouldNotContainsNonDeterministicEndOfLineFixer.cs) + When using a verbatim string `@""` that contains a new line, the end of line may depend on the user's configuration (`\n` or `\r\n`). Also, some tools such as git can change the end of line from `\n` to `\r\n` or `\r\n` to `\n`. diff --git a/docs/Rules/MA0102.md b/docs/Rules/MA0102.md index c62646277..eb9c244f0 100644 --- a/docs/Rules/MA0102.md +++ b/docs/Rules/MA0102.md @@ -1,4 +1,7 @@ # MA0102 - Make member readonly + +Sources: [MakeMemberReadOnlyAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/MakeMemberReadOnlyAnalyzer.cs), [MakeMemberReadOnlyFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/MakeMemberReadOnlyFixer.cs) + A struct member should use the `readonly` modifier when it does not modify state. diff --git a/docs/Rules/MA0103.md b/docs/Rules/MA0103.md index 0d280f745..da59e3a54 100644 --- a/docs/Rules/MA0103.md +++ b/docs/Rules/MA0103.md @@ -1,4 +1,7 @@ # MA0103 - Use SequenceEqual instead of equality operator + +Sources: [DoNotUseEqualityOperatorsForSpanOfCharAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotUseEqualityOperatorsForSpanOfCharAnalyzer.cs), [DoNotUseEqualityOperatorsForSpanOfCharFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/DoNotUseEqualityOperatorsForSpanOfCharFixer.cs) + ````c# using System; diff --git a/docs/Rules/MA0104.md b/docs/Rules/MA0104.md index 939ee9177..1ac2657ae 100644 --- a/docs/Rules/MA0104.md +++ b/docs/Rules/MA0104.md @@ -1,4 +1,7 @@ # MA0104 - Do not create a type with a name from the BCL + +Source: [DotNotUseNameFromBCLAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DotNotUseNameFromBCLAnalyzer.cs) + ````c# public class String // MA0104 diff --git a/docs/Rules/MA0105.md b/docs/Rules/MA0105.md index 61968c114..53d602d32 100644 --- a/docs/Rules/MA0105.md +++ b/docs/Rules/MA0105.md @@ -1,4 +1,7 @@ # MA0105 - Use the lambda parameters instead of using a closure + +Source: [AvoidClosureWhenUsingConcurrentDictionaryAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/AvoidClosureWhenUsingConcurrentDictionaryAnalyzer.cs) + ````c# var key = 1; diff --git a/docs/Rules/MA0106.md b/docs/Rules/MA0106.md index a8454f42c..063b5f269 100644 --- a/docs/Rules/MA0106.md +++ b/docs/Rules/MA0106.md @@ -1,4 +1,7 @@ # MA0106 - Avoid closure by using an overload with the 'factoryArgument' parameter + +Source: [AvoidClosureWhenUsingConcurrentDictionaryAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/AvoidClosureWhenUsingConcurrentDictionaryAnalyzer.cs) + ````c# using System.Collections.Concurrent; diff --git a/docs/Rules/MA0107.md b/docs/Rules/MA0107.md index 29bf43759..3d9eff084 100644 --- a/docs/Rules/MA0107.md +++ b/docs/Rules/MA0107.md @@ -1,4 +1,7 @@ # MA0107 - Do not use object.ToString + +Source: [DoNotUseImplicitCultureSensitiveToStringAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotUseImplicitCultureSensitiveToStringAnalyzer.cs) + ````csharp object a = 10; diff --git a/docs/Rules/MA0108.md b/docs/Rules/MA0108.md index 1ff9250fe..675c785ae 100644 --- a/docs/Rules/MA0108.md +++ b/docs/Rules/MA0108.md @@ -1,4 +1,7 @@ # MA0108 - Remove redundant argument value + +Sources: [SimplifyCallerArgumentExpressionAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/SimplifyCallerArgumentExpressionAnalyzer.cs), [SimplifyCallerArgumentExpressionFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/SimplifyCallerArgumentExpressionFixer.cs) + ````c# using System.Runtime.CompilerServices; diff --git a/docs/Rules/MA0109.md b/docs/Rules/MA0109.md index df61a27eb..c454a9853 100644 --- a/docs/Rules/MA0109.md +++ b/docs/Rules/MA0109.md @@ -1,4 +1,7 @@ # MA0109 - Consider adding an overload with a Span\ or Memory\ + +Source: [AddOverloadWithSpanOrMemoryAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/AddOverloadWithSpanOrMemoryAnalyzer.cs) + ````c# void A(string[] a) { } // ok as there is an overload with Span diff --git a/docs/Rules/MA0110.md b/docs/Rules/MA0110.md index 9841efed4..d7d156a48 100644 --- a/docs/Rules/MA0110.md +++ b/docs/Rules/MA0110.md @@ -1,4 +1,7 @@ # MA0110 - Use the Regex source generator + +Sources: [UseRegexSourceGeneratorAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseRegexSourceGeneratorAnalyzer.cs), [UseRegexSourceGeneratorFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UseRegexSourceGeneratorFixer.cs) + Use the Regex Source Generator when possible. diff --git a/docs/Rules/MA0111.md b/docs/Rules/MA0111.md index a705593ea..a12bf7bea 100644 --- a/docs/Rules/MA0111.md +++ b/docs/Rules/MA0111.md @@ -1,4 +1,7 @@ # MA0111 - Use string.Create instead of FormattableString + +Sources: [UseStringCreateInsteadOfFormattableStringAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseStringCreateInsteadOfFormattableStringAnalyzer.cs), [UseStringCreateInsteadOfFormattableStringFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UseStringCreateInsteadOfFormattableStringFixer.cs) + Use `string.Create` instead of `FormattableString` when possible. diff --git a/docs/Rules/MA0112.md b/docs/Rules/MA0112.md index 592d8c85f..686232adf 100644 --- a/docs/Rules/MA0112.md +++ b/docs/Rules/MA0112.md @@ -1,4 +1,7 @@ # MA0112 - Use 'Count \> 0' instead of 'Any()' + +Source: [OptimizeLinqUsageAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/OptimizeLinqUsageAnalyzer.cs) + For performance reasons, use the `Count` property instead of `Any()` diff --git a/docs/Rules/MA0113.md b/docs/Rules/MA0113.md index 80bda615b..02e9bce04 100644 --- a/docs/Rules/MA0113.md +++ b/docs/Rules/MA0113.md @@ -1,4 +1,7 @@ # MA0113 - Use DateTime.UnixEpoch + +Sources: [UseDateTimeUnixEpochAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseDateTimeUnixEpochAnalyzer.cs), [UseDateTimeUnixEpochFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UseDateTimeUnixEpochFixer.cs) + Use `System.DateTime.UnixEpoch` instead of re-implementing the logic. diff --git a/docs/Rules/MA0114.md b/docs/Rules/MA0114.md index 9ee2ae4ce..185e799c9 100644 --- a/docs/Rules/MA0114.md +++ b/docs/Rules/MA0114.md @@ -1,4 +1,7 @@ # MA0114 - Use DateTimeOffset.UnixEpoch + +Sources: [UseDateTimeUnixEpochAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseDateTimeUnixEpochAnalyzer.cs), [UseDateTimeUnixEpochFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UseDateTimeUnixEpochFixer.cs) + Use `System.DateTimeOffset.UnixEpoch` instead of re-implementing the logic. diff --git a/docs/Rules/MA0115.md b/docs/Rules/MA0115.md index ebc2b0e7f..a6a5c12ac 100644 --- a/docs/Rules/MA0115.md +++ b/docs/Rules/MA0115.md @@ -1,4 +1,7 @@ # MA0115 - Unknown component parameter + +Source: [DoNotUseUnknownParameterForRazorComponentAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotUseUnknownParameterForRazorComponentAnalyzer.cs) + Detect usage of invalid parameter in Razor components. diff --git a/docs/Rules/MA0116.md b/docs/Rules/MA0116.md index 6ff05b0a5..8217590ee 100644 --- a/docs/Rules/MA0116.md +++ b/docs/Rules/MA0116.md @@ -1,4 +1,7 @@ # MA0116 - Parameters with \[SupplyParameterFromQuery\] attributes should also be marked as \[Parameter\] + +Sources: [ParameterAttributeForRazorComponentAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/ParameterAttributeForRazorComponentAnalyzer.cs), [ParameterAttributeForRazorComponentFixer.AddParameterAttribute.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/ParameterAttributeForRazorComponentFixer.AddParameterAttribute.cs) + Detect when `[SupplyParameterFromQuery]` attributes are used without the `[Parameter]` attributes. diff --git a/docs/Rules/MA0117.md b/docs/Rules/MA0117.md index 8cd35dd6c..6ca31e539 100644 --- a/docs/Rules/MA0117.md +++ b/docs/Rules/MA0117.md @@ -1,4 +1,7 @@ # MA0117 - Parameters with \[EditorRequired\] attributes should also be marked as \[Parameter\] + +Sources: [ParameterAttributeForRazorComponentAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/ParameterAttributeForRazorComponentAnalyzer.cs), [ParameterAttributeForRazorComponentFixer.AddParameterAttribute.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/ParameterAttributeForRazorComponentFixer.AddParameterAttribute.cs) + Detect when `[EditorRequired]` attributes are used without the `[Parameter]` attribute. diff --git a/docs/Rules/MA0118.md b/docs/Rules/MA0118.md index 80c56adb1..cd64deeab 100644 --- a/docs/Rules/MA0118.md +++ b/docs/Rules/MA0118.md @@ -1,4 +1,7 @@ # MA0118 - \[JSInvokable\] methods must be public + +Source: [JSInvokableMethodsMustBePublicAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/JSInvokableMethodsMustBePublicAnalyzer.cs) + Methods marked as `[JSInvokable]` must be public. diff --git a/docs/Rules/MA0119.md b/docs/Rules/MA0119.md index e120fecf4..d2de49d2a 100644 --- a/docs/Rules/MA0119.md +++ b/docs/Rules/MA0119.md @@ -1,4 +1,7 @@ # MA0119 - JSRuntime must not be used in OnInitialized or OnInitializedAsync + +Source: [JSInteropMustNotBeUsedInOnInitializedAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/JSInteropMustNotBeUsedInOnInitializedAnalyzer.cs) + In a Blazor component, JSRuntime is not available in `OnInitialize` or `OnInitializedAsync`. The analyzer also reports usages of `ProtectedBrowserStorage` and derived classes as it uses `IJSRuntime` under the hood. diff --git a/docs/Rules/MA0120.md b/docs/Rules/MA0120.md index d974b4f47..d7cca2a9f 100644 --- a/docs/Rules/MA0120.md +++ b/docs/Rules/MA0120.md @@ -1,4 +1,7 @@ # MA0120 - Use InvokeVoidAsync when the returned value is not used + +Sources: [UseJSRuntimeInvokeVoidAsyncWhenReturnValueIsNotUsedAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseJSRuntimeInvokeVoidAsyncWhenReturnValueIsNotUsedAnalyzer.cs), [UseJSRuntimeInvokeVoidAsyncWhenReturnValueIsNotUsedFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UseJSRuntimeInvokeVoidAsyncWhenReturnValueIsNotUsedFixer.cs) + Simplify the usage of `IJSRuntime` or `IJSInProcessRuntime` when the returned value is not used. diff --git a/docs/Rules/MA0121.md b/docs/Rules/MA0121.md index 63a6f6101..dea271a7c 100644 --- a/docs/Rules/MA0121.md +++ b/docs/Rules/MA0121.md @@ -1,4 +1,7 @@ # MA0121 - Do not overwrite parameter value + +Source: [DoNotOverwriteRazorComponentParameterValue.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotOverwriteRazorComponentParameterValue.cs) + Providing initial values for component parameters is supported, but don't create a component that writes to its own parameters after the component is rendered for the first time. For more information, see the Overwritten parameters section of this article. diff --git a/docs/Rules/MA0122.md b/docs/Rules/MA0122.md index 9191503ef..ba588c7c9 100644 --- a/docs/Rules/MA0122.md +++ b/docs/Rules/MA0122.md @@ -1,4 +1,7 @@ # MA0122 - Parameters with \[SupplyParameterFromQuery\] attributes are only valid in routable components (@page) + +Source: [ParameterAttributeForRazorComponentAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/ParameterAttributeForRazorComponentAnalyzer.cs) + Component parameters can only receive query parameter values in routable components with an `@page` directive. diff --git a/docs/Rules/MA0123.md b/docs/Rules/MA0123.md index 1a59fde78..fdb8b7c07 100644 --- a/docs/Rules/MA0123.md +++ b/docs/Rules/MA0123.md @@ -1,3 +1,6 @@ # MA0123 - Sequence number must be a constant + +Source: [SequenceNumberMustBeAConstantAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/SequenceNumberMustBeAConstantAnalyzer.cs) + The Blazor difference algorithm relies on the sequence numbers corresponding to distinct lines of code, not distinct call invocations. When creating a component with RenderTreeBuilder methods, hardcode the arguments for sequence numbers. Using a calculation or counter to generate the sequence number can lead to poor performance. For more information, see the [Sequence numbers relate to code line numbers and not execution order section]((https://learn.microsoft.com/en-us/aspnet/core/blazor/advanced-scenarios?view=aspnetcore-6.0&WT.mc_id=DT-MVP-5003978#sequence-numbers-relate-to-code-line-numbers-and-not-execution-order)). diff --git a/docs/Rules/MA0124.md b/docs/Rules/MA0124.md index d41ee3ae3..dcc5551b0 100644 --- a/docs/Rules/MA0124.md +++ b/docs/Rules/MA0124.md @@ -1,4 +1,7 @@ # MA0124 - Log parameter type is not valid + +Source: [LoggerParameterTypeAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/LoggerParameterTypeAnalyzer.cs) + This rule ensures the parameters for Microsoft.Extensions.Logging's logger are of the expected types. You can configure the expected types using a configuration file. diff --git a/docs/Rules/MA0125.md b/docs/Rules/MA0125.md index 1bb25e30b..4af226e38 100644 --- a/docs/Rules/MA0125.md +++ b/docs/Rules/MA0125.md @@ -1,4 +1,7 @@ # MA0125 - The list of log parameter types contains an invalid type + +Source: [LoggerParameterTypeAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/LoggerParameterTypeAnalyzer.cs) + The configuration file for the rule [MA0124](./MA0124.md) contains a type that is not found in the current compilation. diff --git a/docs/Rules/MA0126.md b/docs/Rules/MA0126.md index 1271ef98b..099ba65b4 100644 --- a/docs/Rules/MA0126.md +++ b/docs/Rules/MA0126.md @@ -1,4 +1,7 @@ # MA0126 - The list of log parameter types contains a duplicate + +Source: [LoggerParameterTypeAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/LoggerParameterTypeAnalyzer.cs) + The configuration file for the rule [MA0124](./MA0124.md) contains duplicated names. diff --git a/docs/Rules/MA0127.md b/docs/Rules/MA0127.md index 8a2adabe5..d0bbd67ae 100644 --- a/docs/Rules/MA0127.md +++ b/docs/Rules/MA0127.md @@ -1,4 +1,7 @@ # MA0127 - Use String.Equals instead of is pattern + +Source: [UseStringEqualsInsteadOfIsPatternAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseStringEqualsInsteadOfIsPatternAnalyzer.cs) + Note that this rule is disabled by default and must be enabled manually using an `.editorconfig` file. diff --git a/docs/Rules/MA0128.md b/docs/Rules/MA0128.md index 7cbd24a93..62cca9185 100644 --- a/docs/Rules/MA0128.md +++ b/docs/Rules/MA0128.md @@ -1,4 +1,7 @@ # MA0128 - Use 'is' operator instead of SequenceEqual + +Sources: [UseIsPatternInsteadOfSequenceEqualAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseIsPatternInsteadOfSequenceEqualAnalyzer.cs), [UseIsPatternInsteadOfSequenceEqualFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UseIsPatternInsteadOfSequenceEqualFixer.cs) + In C# 11, the `MemoryExtensions.SequenceEqual` and `MemoryExtensions.Equals` can be simplified when the value is a constant string. diff --git a/docs/Rules/MA0129.md b/docs/Rules/MA0129.md index 9e20ca4f1..623c8df37 100644 --- a/docs/Rules/MA0129.md +++ b/docs/Rules/MA0129.md @@ -1,4 +1,7 @@ # MA0129 - Await task in using statement + +Source: [TaskInUsingAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/TaskInUsingAnalyzer.cs) + A `Task` doesn't need to be disposed. When used in a `using` statement, most of the time, developers forgot to await it. diff --git a/docs/Rules/MA0130.md b/docs/Rules/MA0130.md index cf875db3e..b9fe64dc7 100644 --- a/docs/Rules/MA0130.md +++ b/docs/Rules/MA0130.md @@ -1,4 +1,7 @@ # MA0130 - GetType() should not be used on System.Type instances + +Source: [ObjectGetTypeOnTypeInstanceAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/ObjectGetTypeOnTypeInstanceAnalyzer.cs) + `GetType()` allows to get the current type. Most of the time, when using on an instance of type `System.Type`, it's an error diff --git a/docs/Rules/MA0131.md b/docs/Rules/MA0131.md index 2b762a63f..72f42e6ae 100644 --- a/docs/Rules/MA0131.md +++ b/docs/Rules/MA0131.md @@ -1,4 +1,7 @@ # MA0131 - ArgumentNullException.ThrowIfNull should not be used with non-nullable types + +Source: [ThrowIfNullWithNonNullableInstanceAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/ThrowIfNullWithNonNullableInstanceAnalyzer.cs) + `ArgumentNullException.ThrowIfNull` should not be used with non-nullable value, such as `int` or `bool`. diff --git a/docs/Rules/MA0132.md b/docs/Rules/MA0132.md index 06da8f7ca..c59651e65 100644 --- a/docs/Rules/MA0132.md +++ b/docs/Rules/MA0132.md @@ -1,4 +1,7 @@ # MA0132 - Do not convert implicitly to DateTimeOffset + +Source: [DoNotImplicitlyConvertDateTimeToDateTimeOffsetAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotImplicitlyConvertDateTimeToDateTimeOffsetAnalyzer.cs) + Implicit conversions from `DateTime` to `DateTimeOffset` are dangerous. The result depends on `DateTime.Kind` which is often `Unspecified`, and so, fallback to a local time. This may not be desired. Also, this may indicate that you are mixing `DateTime` and `DateTimeOffset` in your application, which may be unintentional. diff --git a/docs/Rules/MA0133.md b/docs/Rules/MA0133.md index 8b69c5ccf..ab633717e 100644 --- a/docs/Rules/MA0133.md +++ b/docs/Rules/MA0133.md @@ -1,4 +1,7 @@ # MA0133 - Use DateTimeOffset instead of relying on the implicit conversion + +Source: [DoNotImplicitlyConvertDateTimeToDateTimeOffsetAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotImplicitlyConvertDateTimeToDateTimeOffsetAnalyzer.cs) + Replace `DateTime.UtcNow` or `DateTime.Now` with `DateTimeOffset.UtcNow` or `DateTimeOffset.Now` to avoid an implicit conversion. diff --git a/docs/Rules/MA0134.md b/docs/Rules/MA0134.md index a65745928..963369c19 100644 --- a/docs/Rules/MA0134.md +++ b/docs/Rules/MA0134.md @@ -1,4 +1,7 @@ # MA0134 - Observe result of async calls + +Source: [AwaitAwaitableMethodInSyncMethodAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/AwaitAwaitableMethodInSyncMethodAnalyzer.cs) + The result of awaitable method should be observed by using `await`, `Result`, `Wait`, or other methods. diff --git a/docs/Rules/MA0135.md b/docs/Rules/MA0135.md index 1897001fa..52f5ff586 100644 --- a/docs/Rules/MA0135.md +++ b/docs/Rules/MA0135.md @@ -1,4 +1,7 @@ # MA0135 - The log parameter has no configured type + +Source: [LoggerParameterTypeAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/LoggerParameterTypeAnalyzer.cs) + This rule ensures the parameters for Microsoft.Extensions.Logging's logger can be found in the configuration file (`LoggerParameterTypes.txt` or `LoggerParameterTypes.*.txt`). diff --git a/docs/Rules/MA0136.md b/docs/Rules/MA0136.md index d8597ed4f..ab6558d35 100644 --- a/docs/Rules/MA0136.md +++ b/docs/Rules/MA0136.md @@ -1,4 +1,7 @@ # MA0136 - Raw String contains an implicit end of line character + +Source: [StringShouldNotContainsNonDeterministicEndOfLineAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/StringShouldNotContainsNonDeterministicEndOfLineAnalyzer.cs) + When using a raw string `"""test"""` that contains a new line, the end of line may depend on the user's configuration (`\n` or `\r\n`). Also, some tools such as git can change the end of line from `\n` to `\r\n` or `\r\n` to `\n`. diff --git a/docs/Rules/MA0137.md b/docs/Rules/MA0137.md index da5597f39..e3e706c55 100644 --- a/docs/Rules/MA0137.md +++ b/docs/Rules/MA0137.md @@ -1,4 +1,7 @@ # MA0137 - Use 'Async' suffix when a method returns an awaitable type + +Source: [MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzer.cs) + Methods that return awaitable types such as `Task` or `ValueTask` should have an Async suffix. diff --git a/docs/Rules/MA0138.md b/docs/Rules/MA0138.md index f6139426f..337a2d579 100644 --- a/docs/Rules/MA0138.md +++ b/docs/Rules/MA0138.md @@ -1,4 +1,7 @@ # MA0138 - Do not use 'Async' suffix when a method does not return an awaitable type + +Source: [MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzer.cs) + Methods that does not return an awaitable type such as `Task` or `ValueTask` should not have an 'Async' suffix. diff --git a/docs/Rules/MA0139.md b/docs/Rules/MA0139.md index 3c22083ff..cd39db8f6 100644 --- a/docs/Rules/MA0139.md +++ b/docs/Rules/MA0139.md @@ -1,4 +1,7 @@ # MA0139 - Log parameter type is not valid + +Source: [LoggerParameterTypeAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/LoggerParameterTypeAnalyzer.cs) + This rule ensures the parameters for Serilog's logger are of the expected types. You can configure the expected types using a configuration file. diff --git a/docs/Rules/MA0140.md b/docs/Rules/MA0140.md index 243dae65a..c08d56736 100644 --- a/docs/Rules/MA0140.md +++ b/docs/Rules/MA0140.md @@ -1,4 +1,7 @@ # MA0140 - Both if and else branch have identical code + +Source: [IfElseBranchesAreIdenticalAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/IfElseBranchesAreIdenticalAnalyzer.cs) + ````c# // non-compliant as both branches have the same code diff --git a/docs/Rules/MA0141.md b/docs/Rules/MA0141.md index 1dd9cf58b..0aa65b78d 100644 --- a/docs/Rules/MA0141.md +++ b/docs/Rules/MA0141.md @@ -1,4 +1,7 @@ # MA0141 - Use pattern matching instead of inequality operators for null check + +Sources: [UsePatternMatchingForEqualityComparisonsAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UsePatternMatchingForEqualityComparisonsAnalyzer.cs), [UsePatternMatchingForEqualityComparisonsFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UsePatternMatchingForEqualityComparisonsFixer.cs) + ````c# value != null; // not compliant diff --git a/docs/Rules/MA0142.md b/docs/Rules/MA0142.md index bcd16bed6..6fe040d95 100644 --- a/docs/Rules/MA0142.md +++ b/docs/Rules/MA0142.md @@ -1,4 +1,7 @@ # MA0142 - Use pattern matching instead of equality operators for null check + +Sources: [UsePatternMatchingForEqualityComparisonsAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UsePatternMatchingForEqualityComparisonsAnalyzer.cs), [UsePatternMatchingForEqualityComparisonsFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UsePatternMatchingForEqualityComparisonsFixer.cs) + ````c# value == null; // not compliant diff --git a/docs/Rules/MA0143.md b/docs/Rules/MA0143.md index 8816d5924..ca1e636c2 100644 --- a/docs/Rules/MA0143.md +++ b/docs/Rules/MA0143.md @@ -1,4 +1,7 @@ # MA0143 - Primary constructor parameters should be readonly + +Source: [PrimaryConstructorParameterShouldBeReadOnlyAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/PrimaryConstructorParameterShouldBeReadOnlyAnalyzer.cs) + The rule reports all assignments to primary constructor parameters diff --git a/docs/Rules/MA0144.md b/docs/Rules/MA0144.md index 94c421dec..0e206dced 100644 --- a/docs/Rules/MA0144.md +++ b/docs/Rules/MA0144.md @@ -1,4 +1,7 @@ # MA0144 - Use System.OperatingSystem to check the current OS + +Source: [UseOperatingSystemInsteadOfRuntimeInformationAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseOperatingSystemInsteadOfRuntimeInformationAnalyzer.cs) + Use `System.OperatingSystem` to check the current OS instead of `RuntimeInformation`. diff --git a/docs/Rules/MA0145.md b/docs/Rules/MA0145.md index 3bf5a6185..5cf3ec666 100644 --- a/docs/Rules/MA0145.md +++ b/docs/Rules/MA0145.md @@ -1,4 +1,7 @@ # MA0145 - Signature for \[UnsafeAccessorAttribute\] method is not valid + +Source: [ValidateUnsafeAccessorAttributeUsageAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/ValidateUnsafeAccessorAttributeUsageAnalyzer.cs) + Report some cases where a method decorated with `[UnsafeAccessorAttribute]` is not valid. diff --git a/docs/Rules/MA0146.md b/docs/Rules/MA0146.md index f7d425ee2..f4a8f0b75 100644 --- a/docs/Rules/MA0146.md +++ b/docs/Rules/MA0146.md @@ -1,4 +1,7 @@ # MA0146 - Name must be set explicitly on local functions + +Source: [ValidateUnsafeAccessorAttributeUsageAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/ValidateUnsafeAccessorAttributeUsageAnalyzer.cs) + Local function names are mangle by the compiler, so the `Name` named constructor parameter is required diff --git a/docs/Rules/MA0147.md b/docs/Rules/MA0147.md index 3d23081f7..ba25b40d0 100644 --- a/docs/Rules/MA0147.md +++ b/docs/Rules/MA0147.md @@ -1,4 +1,7 @@ # MA0147 - Avoid async void method for delegate + +Source: [DoNotUseAsyncDelegateForSyncDelegateAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotUseAsyncDelegateForSyncDelegateAnalyzer.cs) + ````c# Foo(() => {}); // ok diff --git a/docs/Rules/MA0148.md b/docs/Rules/MA0148.md index 9e0067179..d449de807 100644 --- a/docs/Rules/MA0148.md +++ b/docs/Rules/MA0148.md @@ -1,4 +1,7 @@ # MA0148 - Use pattern matching instead of equality operators for discrete value + +Sources: [UsePatternMatchingForEqualityComparisonsAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UsePatternMatchingForEqualityComparisonsAnalyzer.cs), [UsePatternMatchingForEqualityComparisonsFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UsePatternMatchingForEqualityComparisonsFixer.cs) + ````c# value == 1; // not compliant diff --git a/docs/Rules/MA0149.md b/docs/Rules/MA0149.md index c451d4d9a..986295a38 100644 --- a/docs/Rules/MA0149.md +++ b/docs/Rules/MA0149.md @@ -1,4 +1,7 @@ # MA0149 - Use pattern matching instead of inequality operators for discrete value + +Sources: [UsePatternMatchingForEqualityComparisonsAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UsePatternMatchingForEqualityComparisonsAnalyzer.cs), [UsePatternMatchingForEqualityComparisonsFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UsePatternMatchingForEqualityComparisonsFixer.cs) + ````c# value != 1; // not compliant diff --git a/docs/Rules/MA0150.md b/docs/Rules/MA0150.md index dc166430a..c60040a18 100644 --- a/docs/Rules/MA0150.md +++ b/docs/Rules/MA0150.md @@ -1,4 +1,7 @@ # MA0150 - Do not call the default object.ToString explicitly + +Source: [DoNotUseToStringIfObjectAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotUseToStringIfObjectAnalyzer.cs) + Report when `ToString` is called on an instance of a type that doesn't override `ToString`: diff --git a/docs/Rules/MA0151.md b/docs/Rules/MA0151.md index 029b5c3fd..435670f40 100644 --- a/docs/Rules/MA0151.md +++ b/docs/Rules/MA0151.md @@ -1,4 +1,7 @@ # MA0151 - DebuggerDisplay must contain valid members + +Source: [DebuggerDisplayAttributeShouldContainValidExpressionsAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DebuggerDisplayAttributeShouldContainValidExpressionsAnalyzer.cs) + Validate the content diff --git a/docs/Rules/MA0152.md b/docs/Rules/MA0152.md index b09526183..9d0ebd58d 100644 --- a/docs/Rules/MA0152.md +++ b/docs/Rules/MA0152.md @@ -1,4 +1,7 @@ # MA0152 - Use Unwrap instead of using await twice + +Source: [UseTaskUnwrapAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseTaskUnwrapAnalyzer.cs) + Prefer using [`Unwrap`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskextensions.unwrap?view=net-8.0&WT.mc_id=DT-MVP-5003978) instead of using `await` twice diff --git a/docs/Rules/MA0153.md b/docs/Rules/MA0153.md index bfab8d8e9..dae274b1b 100644 --- a/docs/Rules/MA0153.md +++ b/docs/Rules/MA0153.md @@ -1,4 +1,7 @@ # MA0153 - Do not log symbols decorated with DataClassificationAttribute directly + +Source: [DoNotLogClassifiedDataAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotLogClassifiedDataAnalyzer.cs) + Detects when a log parameter is decorated with an attribute that inherits from `Microsoft.Extensions.Compliance.Classification.DataClassificationAttribute`. Most of the time, these values should not be used with `[LogProperties]` to redact values. diff --git a/docs/Rules/MA0154.md b/docs/Rules/MA0154.md index 9451387ca..960342132 100644 --- a/docs/Rules/MA0154.md +++ b/docs/Rules/MA0154.md @@ -1,4 +1,7 @@ # MA0154 - Use langword in XML comment + +Sources: [UseLangwordInXmlCommentAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseLangwordInXmlCommentAnalyzer.cs), [UseLangwordInXmlCommentFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UseLangwordInXmlCommentFixer.cs) + Use `` instead of `keyword` or `keyword` in XML comments. diff --git a/docs/Rules/MA0155.md b/docs/Rules/MA0155.md index 1018354e0..4148094c8 100755 --- a/docs/Rules/MA0155.md +++ b/docs/Rules/MA0155.md @@ -1,4 +1,7 @@ # MA0155 - Do not use async void methods + +Source: [DoNotUseAsyncVoidAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/DoNotUseAsyncVoidAnalyzer.cs) + ```c# // not-compliant diff --git a/docs/Rules/MA0156.md b/docs/Rules/MA0156.md index 4e214cdf3..b03c8ded0 100644 --- a/docs/Rules/MA0156.md +++ b/docs/Rules/MA0156.md @@ -1,4 +1,7 @@ # MA0156 - Use 'Async' suffix when a method returns IAsyncEnumerable\ + +Source: [MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzer.cs) + Methods that return `IAsyncEnumerable` should have the Async suffix. diff --git a/docs/Rules/MA0157.md b/docs/Rules/MA0157.md index 5ac58c858..5c957a1b7 100644 --- a/docs/Rules/MA0157.md +++ b/docs/Rules/MA0157.md @@ -1,4 +1,7 @@ # MA0157 - Do not use 'Async' suffix when a method returns IAsyncEnumerable\ + +Source: [MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzer.cs) + Methods that do not return `IAsyncEnumerable` should not have the Async suffix. diff --git a/docs/Rules/MA0158.md b/docs/Rules/MA0158.md index 92419346a..a0e4f2310 100644 --- a/docs/Rules/MA0158.md +++ b/docs/Rules/MA0158.md @@ -1,4 +1,7 @@ # MA0158 - Use System.Threading.Lock + +Source: [UseSystemThreadingLockInsteadOfObjectAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseSystemThreadingLockInsteadOfObjectAnalyzer.cs) + Starting with .NET 9 and C# 13, you can use `System.Threading.Lock`. When a field or a local variable is only used inside `lock`, this rule will suggest using `System.Threading.Lock` instead of `object`. diff --git a/docs/Rules/MA0159.md b/docs/Rules/MA0159.md index da6012c2e..dfb77c312 100644 --- a/docs/Rules/MA0159.md +++ b/docs/Rules/MA0159.md @@ -1,4 +1,7 @@ # MA0159 - Use 'Order' instead of 'OrderBy' + +Sources: [OptimizeLinqUsageAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/OptimizeLinqUsageAnalyzer.cs), [OptimizeLinqUsageFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeLinqUsageFixer.cs) + Use `Order` instead of `OrderBy(x => x)` to order a collection. diff --git a/docs/Rules/MA0160.md b/docs/Rules/MA0160.md index 1e53353f2..54fe893d9 100644 --- a/docs/Rules/MA0160.md +++ b/docs/Rules/MA0160.md @@ -1,4 +1,7 @@ # MA0160 - Use ContainsKey instead of TryGetValue + +Source: [UseContainsKeyInsteadOfTryGetValueAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseContainsKeyInsteadOfTryGetValueAnalyzer.cs) + ````c# Dictionary dict; diff --git a/docs/Rules/MA0161.md b/docs/Rules/MA0161.md index 139a5b89f..a898dc83e 100644 --- a/docs/Rules/MA0161.md +++ b/docs/Rules/MA0161.md @@ -1,4 +1,7 @@ # MA0161 - UseShellExecute must be explicitly set + +Source: [ProcessStartAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/ProcessStartAnalyzer.cs) + Detects when `Process.Start` is called without specifying the value of `UseShellExecute`. diff --git a/docs/Rules/MA0162.md b/docs/Rules/MA0162.md index f6bf215a7..cd70298cb 100644 --- a/docs/Rules/MA0162.md +++ b/docs/Rules/MA0162.md @@ -1,4 +1,7 @@ # MA0162 - Use Process.Start overload with ProcessStartInfo + +Source: [ProcessStartAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/ProcessStartAnalyzer.cs) + Detects when `Process.Start` is called without the `ProcessStartInfo` parameter. diff --git a/docs/Rules/MA0163.md b/docs/Rules/MA0163.md index fdadb2660..93195ea94 100644 --- a/docs/Rules/MA0163.md +++ b/docs/Rules/MA0163.md @@ -1,4 +1,7 @@ # MA0163 - UseShellExecute must be false when redirecting standard input or output + +Source: [ProcessStartAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/ProcessStartAnalyzer.cs) + Detects when `Process.Start` is called without specifying the value of `UseShellExecute`. diff --git a/docs/Rules/MA0164.md b/docs/Rules/MA0164.md index b06cddb50..1c28a08e0 100644 --- a/docs/Rules/MA0164.md +++ b/docs/Rules/MA0164.md @@ -1,4 +1,7 @@ # MA0164 - Use parentheses to make not pattern clearer + +Sources: [NotPatternShouldBeParenthesizedAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/NotPatternShouldBeParenthesizedAnalyzer.cs), [NotPatternShouldBeParenthesizedCodeFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/NotPatternShouldBeParenthesizedCodeFixer.cs) + `not` patterns are often wrongly used in combination with the `or` operator. This rule suggests using parentheses to make evaluation order clearer. diff --git a/docs/Rules/MA0165.md b/docs/Rules/MA0165.md index 77c0e4c26..22e6adb27 100644 --- a/docs/Rules/MA0165.md +++ b/docs/Rules/MA0165.md @@ -1,4 +1,7 @@ # MA0165 - Make interpolated string + +Sources: [MakeInterpolatedStringAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/MakeInterpolatedStringAnalyzer.cs), [MakeInterpolatedStringFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/MakeInterpolatedStringFixer.cs) + This rule is intended to provide a codefix for adding `$` in front of a string or a verbatim string. diff --git a/docs/Rules/MA0166.md b/docs/Rules/MA0166.md index 6a515daf0..84dc8726b 100644 --- a/docs/Rules/MA0166.md +++ b/docs/Rules/MA0166.md @@ -1,4 +1,7 @@ # MA0166 - Forward the TimeProvider to methods that take one + +Sources: [UseAnOverloadThatHasTimeProviderAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasTimeProviderAnalyzer.cs), [UseAnOverloadThatHasTimeProviderFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UseAnOverloadThatHasTimeProviderFixer.cs) + You should pass a `System.TimeProvider` when calling a method if there is an overload of the method that supports it. diff --git a/docs/Rules/MA0167.md b/docs/Rules/MA0167.md index 0520bd66d..8cf0d4361 100644 --- a/docs/Rules/MA0167.md +++ b/docs/Rules/MA0167.md @@ -1,4 +1,7 @@ # MA0167 - Use an overload with a TimeProvider argument + +Source: [UseAnOverloadThatHasTimeProviderAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseAnOverloadThatHasTimeProviderAnalyzer.cs) + You should pass a `System.TimeProvider` when calling a method if there is an overload of the method that supports it. diff --git a/docs/Rules/MA0168.md b/docs/Rules/MA0168.md index 3045e00ca..d7650bfac 100644 --- a/docs/Rules/MA0168.md +++ b/docs/Rules/MA0168.md @@ -1,4 +1,7 @@ # MA0168 - Use readonly struct for in or ref readonly parameter + +Source: [UseReadOnlyStructForRefReadOnlyParametersAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseReadOnlyStructForRefReadOnlyParametersAnalyzer.cs) + ```c# void A(in Foo p) { } // not-compliant as Foo is not readonly diff --git a/docs/Rules/MA0169.md b/docs/Rules/MA0169.md index 365bb4953..33abc7d18 100644 --- a/docs/Rules/MA0169.md +++ b/docs/Rules/MA0169.md @@ -1,4 +1,7 @@ # MA0169 - Use Equals method instead of operator + +Source: [UseEqualsMethodInsteadOfOperatorAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseEqualsMethodInsteadOfOperatorAnalyzer.cs) + When a type overrides the `Equals` method, but does not define the equality operators, using `==` or `!=` will do a reference comparison. This can lead to unexpected behavior, as the `Equals` method may be overridden to provide a value comparison. This rule is to ensure that the `Equals` method is used. diff --git a/docs/Rules/MA0170.md b/docs/Rules/MA0170.md index 37fa16333..535367c8a 100644 --- a/docs/Rules/MA0170.md +++ b/docs/Rules/MA0170.md @@ -1,4 +1,7 @@ # MA0170 - Type cannot be used as an attribute argument + +Source: [TypeCannotBeUsedInAnAttributeParameterAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/TypeCannotBeUsedInAnAttributeParameterAnalyzer.cs) + Report any constructor parameters, fields, or properties that are not supported by the C# language as attribute arguments as defined in https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/attributes?WT.mc_id=DT-MVP-5003978#2224-attribute-parameter-types. diff --git a/docs/Rules/MA0171.md b/docs/Rules/MA0171.md index a6ca21a0b..7708cdd66 100644 --- a/docs/Rules/MA0171.md +++ b/docs/Rules/MA0171.md @@ -1,4 +1,7 @@ # MA0171 - Use pattern matching instead of HasValue for Nullable\ check + +Sources: [UsePatternMatchingInsteadOfHasValueAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UsePatternMatchingInsteadOfHasValueAnalyzer.cs), [UsePatternMatchingInsteadOfHasvalueFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UsePatternMatchingInsteadOfHasvalueFixer.cs) + Use pattern matching instead of the `HasValue` property to check for non-nullable value types or nullable value types. diff --git a/docs/Rules/MA0172.md b/docs/Rules/MA0172.md index 24c9bd0a6..da764159c 100644 --- a/docs/Rules/MA0172.md +++ b/docs/Rules/MA0172.md @@ -1,4 +1,7 @@ # MA0172 - Both sides of the logical operation are identical + +Source: [BothSideOfTheConditionAreIdenticalAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/BothSideOfTheConditionAreIdenticalAnalyzer.cs) + This rule triggers when both sides of a logical operation (such as `&&`, `||`, `&`, `|`, `==`, `!=`, or pattern matching operations) are identical. This usually indicates a mistake or redundant code. diff --git a/docs/Rules/MA0173.md b/docs/Rules/MA0173.md index e305ac10e..834baad5f 100644 --- a/docs/Rules/MA0173.md +++ b/docs/Rules/MA0173.md @@ -1,4 +1,7 @@ # MA0173 - Use LazyInitializer.EnsureInitialize + +Source: [UseLazyInitializerEnsureInitializeAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseLazyInitializerEnsureInitializeAnalyzer.cs) + ```c# Interlocked.CompareExchange(ref _field, new Sample(), null); // non-compliant diff --git a/docs/Rules/MA0174.md b/docs/Rules/MA0174.md index b705d736e..d8ca8384c 100644 --- a/docs/Rules/MA0174.md +++ b/docs/Rules/MA0174.md @@ -1,4 +1,7 @@ # MA0174 - Record should use explicit 'class' keyword + +Source: [RecordClassDeclarationShouldBeExplicitAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/RecordClassDeclarationShouldBeExplicitAnalyzer.cs) + This rule suggests adding the explicit `class` keyword to record declarations that don't specify it. diff --git a/docs/Rules/MA0175.md b/docs/Rules/MA0175.md index a32f60c53..9d32c321b 100644 --- a/docs/Rules/MA0175.md +++ b/docs/Rules/MA0175.md @@ -1,4 +1,7 @@ # MA0175 - Record should not use explicit 'class' keyword + +Source: [RecordClassDeclarationShouldBeImplicitAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/RecordClassDeclarationShouldBeImplicitAnalyzer.cs) + This rule suggests adding the explicit `class` keyword to record declarations that don't specify it. diff --git a/docs/Rules/MA0176.md b/docs/Rules/MA0176.md index 45db76ada..9143f752c 100644 --- a/docs/Rules/MA0176.md +++ b/docs/Rules/MA0176.md @@ -1,4 +1,7 @@ # MA0176 - Optimize guid creation + +Sources: [OptimizeGuidCreationAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/OptimizeGuidCreationAnalyzer.cs), [OptimizeGuidCreationFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/OptimizeGuidCreationFixer.cs) + Use the constructor overload of `Guid` that takes the individual components of a GUID instead of using the string-based constructor or `Guid.Parse` method. diff --git a/docs/Rules/MA0177.md b/docs/Rules/MA0177.md index 215df7ea8..00950b9cf 100644 --- a/docs/Rules/MA0177.md +++ b/docs/Rules/MA0177.md @@ -1,4 +1,7 @@ # MA0177 - Use single-line XML comment syntax when possible + +Sources: [UseInlineXmlCommentSyntaxWhenPossibleAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseInlineXmlCommentSyntaxWhenPossibleAnalyzer.cs), [UseInlineXmlCommentSyntaxWhenPossibleFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UseInlineXmlCommentSyntaxWhenPossibleFixer.cs) + This rule reports XML documentation comments that span multiple lines but contain only single-line content. Such comments can be more concisely written on a single line for better readability. diff --git a/src/DocumentationGenerator/Program.cs b/src/DocumentationGenerator/Program.cs index 19453c287..084adf1e0 100644 --- a/src/DocumentationGenerator/Program.cs +++ b/src/DocumentationGenerator/Program.cs @@ -4,7 +4,6 @@ #pragma warning disable MA0009 using System.Text.Encodings.Web; using System.Text.RegularExpressions; -using System.Text.Unicode; using Meziantou.Framework; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; @@ -69,21 +68,80 @@ WriteFileIfChanged(path, sb.ToString()); } -// Update title in rule pages +// Update title in rule pages and add links to source code { - foreach (var diagnostic in diagnosticAnalyzers.SelectMany(diagnosticAnalyzer => diagnosticAnalyzer.SupportedDiagnostics).DistinctBy(diag => diag.Id).OrderBy(diag => diag.Id, StringComparer.Ordinal)) + foreach (var diagnosticAnalyzer in diagnosticAnalyzers) { - var title = $"# {diagnostic.Id} - {EscapeMarkdown(diagnostic.Title.ToString(CultureInfo.InvariantCulture))}"; - var detailPath = outputFolder / "docs" / "Rules" / (diagnostic.Id + ".md"); - if (File.Exists(detailPath)) + foreach (var diagnostic in diagnosticAnalyzer.SupportedDiagnostics) { - var lines = await File.ReadAllLinesAsync(detailPath); - lines[0] = title; - WriteFileIfChanged(detailPath, string.Join('\n', lines) + "\n"); - } - else - { - WriteFileIfChanged(detailPath, title); + var title = $"# {diagnostic.Id} - {EscapeMarkdown(diagnostic.Title.ToString(CultureInfo.InvariantCulture))}"; + var detailPath = outputFolder / "docs" / "Rules" / (diagnostic.Id + ".md"); + if (File.Exists(detailPath)) + { + var lines = (await File.ReadAllLinesAsync(detailPath)).ToList(); + lines[0] = title; + + if (!lines.Any(line => line.Contains("", StringComparison.Ordinal))) + { + lines.Insert(1, ""); + lines.Insert(1, ""); + } + + var newContent = string.Join('\n', lines) + "\n"; + + var sourceLinks = new List(); + string GetFilePath(string name) + { + try + { + var files = Directory.GetFiles(outputFolder / "src", name + ".cs", SearchOption.AllDirectories); + if (files.Length == 0) + { + files = Directory.GetFiles(outputFolder / "src", name + "." + diagnostic.Id + ".cs", SearchOption.AllDirectories); + } + if (files.Length == 0) + { + files = Directory.GetFiles(outputFolder / "src", name + ".*.cs", SearchOption.AllDirectories); + } + + if (files.Length == 0) + throw new InvalidOperationException($"Cannot find source file for {name}"); + + if (files.Length > 1) + throw new InvalidOperationException($"Cannot find source file for {name}"); + + var sourceFile = FullPath.FromPath(files.Single()); + var relativePath = sourceFile.MakePathRelativeTo(outputFolder); + return "https://github.com/meziantou/Meziantou.Analyzer/blob/main/" + relativePath.Replace('\\', '/'); + } + catch (Exception ex) + { + throw new InvalidOperationException($"Cannot find source file for {name}", ex); + } + } + void AddLink(string name) + { + var url = GetFilePath(name); + var text = Path.GetFileName(url); + sourceLinks.Add($"[{text}]({url})"); + } + + AddLink(diagnosticAnalyzer.GetType().Name); + + var fixers = codeFixProviders.Where(fixer => fixer.FixableDiagnosticIds.Contains(diagnostic.Id, StringComparer.Ordinal)).ToArray(); + foreach (var fixer in fixers) + { + AddLink(fixer.GetType().Name); + } + + newContent = Regex.Replace(newContent, "(?<=\\r?\\n).*(?=)", (sourceLinks.Count == 1 ? "Source: " : "Sources: ") + string.Join(", ", sourceLinks) + "\n", RegexOptions.Singleline); + + WriteFileIfChanged(detailPath, newContent); + } + else + { + WriteFileIfChanged(detailPath, title); + } } } } From 9a3a8dc90c60d87ae4bc6d34da5036a213c88b39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Sun, 2 Nov 2025 13:41:38 -0500 Subject: [PATCH 32/38] Add more logs when writing files --- src/DocumentationGenerator/Program.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/DocumentationGenerator/Program.cs b/src/DocumentationGenerator/Program.cs index 084adf1e0..c108a9c20 100644 --- a/src/DocumentationGenerator/Program.cs +++ b/src/DocumentationGenerator/Program.cs @@ -196,6 +196,7 @@ void WriteFileIfChanged(FullPath path, string content) path.CreateParentDirectory(); File.WriteAllText(path, content, encoding); fileWritten++; + Console.WriteLine($"Created file: {path}"); return; } @@ -204,6 +205,7 @@ void WriteFileIfChanged(FullPath path, string content) { File.WriteAllText(path, content, encoding); fileWritten++; + Console.WriteLine($"Updated file: {path}"); } } From 1a5d2e3f7f5ca87d71d7bd74b39e8446d5cda269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Sun, 2 Nov 2025 13:51:21 -0500 Subject: [PATCH 33/38] Add all analyzer links to the doc --- src/DocumentationGenerator/Program.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/DocumentationGenerator/Program.cs b/src/DocumentationGenerator/Program.cs index c108a9c20..08c6c4b5a 100644 --- a/src/DocumentationGenerator/Program.cs +++ b/src/DocumentationGenerator/Program.cs @@ -70,10 +70,16 @@ // Update title in rule pages and add links to source code { + var rules = new HashSet(StringComparer.Ordinal); foreach (var diagnosticAnalyzer in diagnosticAnalyzers) { foreach (var diagnostic in diagnosticAnalyzer.SupportedDiagnostics) { + if (!rules.Add(rulesTable)) + { + continue; + } + var title = $"# {diagnostic.Id} - {EscapeMarkdown(diagnostic.Title.ToString(CultureInfo.InvariantCulture))}"; var detailPath = outputFolder / "docs" / "Rules" / (diagnostic.Id + ".md"); if (File.Exists(detailPath)) @@ -126,7 +132,13 @@ void AddLink(string name) sourceLinks.Add($"[{text}]({url})"); } - AddLink(diagnosticAnalyzer.GetType().Name); + foreach (var analyzer in diagnosticAnalyzers) + { + if (analyzer.SupportedDiagnostics.Any(d => d.Id == diagnostic.Id)) + { + AddLink(analyzer.GetType().Name); + } + } var fixers = codeFixProviders.Where(fixer => fixer.FixableDiagnosticIds.Contains(diagnostic.Id, StringComparer.Ordinal)).ToArray(); foreach (var fixer in fixers) @@ -134,6 +146,7 @@ void AddLink(string name) AddLink(fixer.GetType().Name); } + sourceLinks.Sort(StringComparer.Ordinal); newContent = Regex.Replace(newContent, "(?<=\\r?\\n).*(?=)", (sourceLinks.Count == 1 ? "Source: " : "Sources: ") + string.Join(", ", sourceLinks) + "\n", RegexOptions.Singleline); WriteFileIfChanged(detailPath, newContent); From e319b39198bc0cd5eadf68edd8a487ff85129d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Sun, 2 Nov 2025 13:57:53 -0500 Subject: [PATCH 34/38] Fix link generation --- docs/Rules/MA0009.md | 2 +- docs/Rules/MA0023.md | 2 +- src/DocumentationGenerator/Program.cs | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/Rules/MA0009.md b/docs/Rules/MA0009.md index 6c3a7dd0d..9c5f86b5d 100644 --- a/docs/Rules/MA0009.md +++ b/docs/Rules/MA0009.md @@ -1,6 +1,6 @@ # MA0009 - Add regex evaluation timeout -Source: [RegexMethodUsageAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/RegexMethodUsageAnalyzer.cs) +Sources: [GeneratedRegexAttributeUsageAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/GeneratedRegexAttributeUsageAnalyzer.cs), [RegexMethodUsageAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/RegexMethodUsageAnalyzer.cs) ````csharp diff --git a/docs/Rules/MA0023.md b/docs/Rules/MA0023.md index faa77b75d..4c45bcf5c 100644 --- a/docs/Rules/MA0023.md +++ b/docs/Rules/MA0023.md @@ -1,6 +1,6 @@ # MA0023 - Add RegexOptions.ExplicitCapture -Source: [RegexMethodUsageAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/RegexMethodUsageAnalyzer.cs) +Sources: [GeneratedRegexAttributeUsageAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/GeneratedRegexAttributeUsageAnalyzer.cs), [RegexMethodUsageAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/RegexMethodUsageAnalyzer.cs) Using named groups clarifies what is to be captured. It also makes the regex more performant, as unnamed groups will not be captured needlessly. diff --git a/src/DocumentationGenerator/Program.cs b/src/DocumentationGenerator/Program.cs index 08c6c4b5a..0d4adbb53 100644 --- a/src/DocumentationGenerator/Program.cs +++ b/src/DocumentationGenerator/Program.cs @@ -75,10 +75,8 @@ { foreach (var diagnostic in diagnosticAnalyzer.SupportedDiagnostics) { - if (!rules.Add(rulesTable)) - { + if (!rules.Add(diagnostic.Id)) continue; - } var title = $"# {diagnostic.Id} - {EscapeMarkdown(diagnostic.Title.ToString(CultureInfo.InvariantCulture))}"; var detailPath = outputFolder / "docs" / "Rules" / (diagnostic.Id + ".md"); @@ -213,7 +211,7 @@ void WriteFileIfChanged(FullPath path, string content) return; } - var existingContent = File.ReadAllText(path).ReplaceLineEndings(); + var existingContent = File.ReadAllText(path).ReplaceLineEndings("\n"); if (existingContent != content) { File.WriteAllText(path, content, encoding); From f84083d9f8b22a7f4d7570c5197d751934c9eae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Sun, 2 Nov 2025 19:26:39 -0500 Subject: [PATCH 35/38] Update Roslyn dependencies and clean up PackageReferences (#916) --- Directory.Build.targets | 15 ++++------ .../Meziantou.Analyzer.CodeFixers.csproj | 1 - .../Meziantou.Analyzer.csproj | 1 - .../Helpers/ProjectBuilder.cs | 28 +++++++++++++++---- 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/Directory.Build.targets b/Directory.Build.targets index 4ca671d38..45198f25b 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -8,7 +8,6 @@ - @@ -22,7 +21,6 @@ - @@ -36,7 +34,6 @@ - @@ -64,7 +61,6 @@ - @@ -78,14 +74,13 @@ - - - - + + + - $(DefineConstants);ROSLYN4_8;ROSLYN_4_2_OR_GREATER;ROSLYN_4_4_OR_GREATER;ROSLYN_4_5_OR_GREATER;ROSLYN_4_6_OR_GREATER;ROSLYN_4_8_OR_GREATER;ROSLYN_4_10_OR_GREATER - $(DefineConstants);CSHARP9_OR_GREATER;CSHARP10_OR_GREATER;CSHARP11_OR_GREATER;CSHARP12_OR_GREATER;CSHARP13_OR_GREATER + $(DefineConstants);ROSLYN5_0;ROSLYN_4_2_OR_GREATER;ROSLYN_4_4_OR_GREATER;ROSLYN_4_5_OR_GREATER;ROSLYN_4_6_OR_GREATER;ROSLYN_4_8_OR_GREATER;ROSLYN_4_10_OR_GREATER;ROSLYN_5_0_OR_GREATER + $(DefineConstants);CSHARP9_OR_GREATER;CSHARP10_OR_GREATER;CSHARP11_OR_GREATER;CSHARP12_OR_GREATER;CSHARP13_OR_GREATER;CSHARP14_OR_GREATER $(NoWarn);CS0618 diff --git a/src/Meziantou.Analyzer.CodeFixers/Meziantou.Analyzer.CodeFixers.csproj b/src/Meziantou.Analyzer.CodeFixers/Meziantou.Analyzer.CodeFixers.csproj index 9befcd77f..b604dc551 100644 --- a/src/Meziantou.Analyzer.CodeFixers/Meziantou.Analyzer.CodeFixers.csproj +++ b/src/Meziantou.Analyzer.CodeFixers/Meziantou.Analyzer.CodeFixers.csproj @@ -29,7 +29,6 @@ - diff --git a/src/Meziantou.Analyzer/Meziantou.Analyzer.csproj b/src/Meziantou.Analyzer/Meziantou.Analyzer.csproj index 6df946162..19c2c7e4d 100644 --- a/src/Meziantou.Analyzer/Meziantou.Analyzer.csproj +++ b/src/Meziantou.Analyzer/Meziantou.Analyzer.csproj @@ -20,7 +20,6 @@ - diff --git a/tests/Meziantou.Analyzer.Test/Helpers/ProjectBuilder.cs b/tests/Meziantou.Analyzer.Test/Helpers/ProjectBuilder.cs index aafb16eb8..e0b9c53b0 100755 --- a/tests/Meziantou.Analyzer.Test/Helpers/ProjectBuilder.cs +++ b/tests/Meziantou.Analyzer.Test/Helpers/ProjectBuilder.cs @@ -39,9 +39,9 @@ public sealed partial class ProjectBuilder public string? DefaultAnalyzerId { get; set; } public string? DefaultAnalyzerMessage { get; set; } - private static async Task GetNuGetReferences(string packageName, string version, params string[] paths) + private static async Task GetNuGetReferences(string packageName, string version, string[] includedPaths) { - var bytes = Encoding.UTF8.GetBytes(packageName + '@' + version + ':' + string.Join(',', paths)); + var bytes = Encoding.UTF8.GetBytes(packageName + '@' + version + ':' + string.Join(',', includedPaths)); var hash = SHA256.HashData(bytes); var key = Convert.ToBase64String(hash).Replace('/', '_'); var task = NuGetPackagesCache.GetOrAdd(key, _ => new Lazy>(Download)); @@ -61,7 +61,7 @@ async Task Download() await using var zip = new ZipArchive(stream, ZipArchiveMode.Read); var hasEntry = false; - foreach (var entry in zip.Entries.Where(file => paths.Any(path => file.FullName.StartsWith(path, StringComparison.Ordinal)))) + foreach (var entry in zip.Entries.Where(file => includedPaths.Any(path => file.FullName.StartsWith(path, StringComparison.Ordinal)))) { await entry.ExtractToFileAsync(Path.Combine(tempFolder, entry.Name), overwrite: true); hasEntry = true; @@ -69,7 +69,7 @@ async Task Download() if (!hasEntry) { - throw new InvalidOperationException("The NuGet package " + packageName + "@" + version + " does not contain any file matching the paths " + string.Join(", ", paths)); + throw new InvalidOperationException("The NuGet package " + packageName + "@" + version + " does not contain any file matching the paths " + string.Join(", ", includedPaths)); } try @@ -114,7 +114,7 @@ async Task Download() public ProjectBuilder AddNuGetReference(string packageName, string version, string pathPrefix) { - foreach (var reference in GetNuGetReferences(packageName, version, pathPrefix).Result) + foreach (var reference in GetNuGetReferences(packageName, version, [pathPrefix]).Result) { References.Add(MetadataReference.CreateFromFile(reference)); } @@ -149,22 +149,38 @@ public ProjectBuilder WithAnalyzerFromNuGet(string packageName, string version, return this; } +#if ROSLYN5_0 + public ProjectBuilder WithMicrosoftCodeAnalysisNetAnalyzers(params string[] ruleIds) => + WithAnalyzerFromNuGet( + "Microsoft.CodeAnalysis.NetAnalyzers", + "10.0.100-rc.2.25502.107", + paths: ["analyzers/dotnet/cs/", "analyzers/dotnet/Microsoft."], + ruleIds); +#else public ProjectBuilder WithMicrosoftCodeAnalysisNetAnalyzers(params string[] ruleIds) => WithAnalyzerFromNuGet( "Microsoft.CodeAnalysis.NetAnalyzers", "9.0.0", paths: ["analyzers/dotnet/cs/Microsoft.CodeAnalysis"], ruleIds); +#endif public ProjectBuilder WithMicrosoftCodeAnalysisCSharpCodeStyleAnalyzers(params string[] ruleIds) { AddNuGetReference("Microsoft.Bcl.AsyncInterfaces", "9.0.7", "lib/netstandard2.1/"); - +#if ROSLYN5_0 + return WithAnalyzerFromNuGet( + "Microsoft.CodeAnalysis.CSharp.CodeStyle", + "5.0.0-2.final", + paths: ["analyzers/dotnet/cs/", "analyzers/dotnet/Microsoft.CodeAnalysis"], + ruleIds); +#else return WithAnalyzerFromNuGet( "Microsoft.CodeAnalysis.CSharp.CodeStyle", "4.14.0", paths: ["analyzers/dotnet/cs/"], ruleIds); +#endif } public ProjectBuilder AddMSTestApi() => AddNuGetReference("MSTest.TestFramework", "2.1.1", "lib/netstandard1.0/"); From c09974c250c1c078d7f3dc7845cc888924015389 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 2 Nov 2025 19:55:58 -0500 Subject: [PATCH 36/38] Update MA0053 to mention both class and record types (#915) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --------- Co-authored-by: Gérald Barré --- README.md | 2 +- docs/README.md | 6 +- docs/Rules/MA0053.md | 10 +-- .../configuration/default.editorconfig | 2 +- .../configuration/none.editorconfig | 2 +- .../Rules/ClassMustBeSealedAnalyzer.cs | 7 +-- .../Rules/ClassMustBeSealedAnalyzerTests.cs | 62 +++++++++++++++++++ 7 files changed, 76 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 2da19c83c..c6c2de17a 100755 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ If you are already using other analyzers, you can check [which rules are duplica |[MA0050](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0050.md)|Design|Validate arguments correctly in iterator methods|ℹ️|✔️|✔️| |[MA0051](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0051.md)|Design|Method is too long|⚠️|✔️|❌| |[MA0052](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0052.md)|Performance|Replace constant Enum.ToString with nameof|ℹ️|✔️|✔️| -|[MA0053](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0053.md)|Design|Make class sealed|ℹ️|✔️|✔️| +|[MA0053](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0053.md)|Design|Make class or record sealed|ℹ️|✔️|✔️| |[MA0054](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0054.md)|Design|Embed the caught exception as innerException|⚠️|✔️|❌| |[MA0055](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0055.md)|Design|Do not use finalizer|⚠️|✔️|❌| |[MA0056](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0056.md)|Design|Do not call overridable members in constructor|⚠️|✔️|❌| diff --git a/docs/README.md b/docs/README.md index b908fc630..a44bc69da 100755 --- a/docs/README.md +++ b/docs/README.md @@ -52,7 +52,7 @@ |[MA0050](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0050.md)|Design|Validate arguments correctly in iterator methods|ℹ️|✔️|✔️| |[MA0051](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0051.md)|Design|Method is too long|⚠️|✔️|❌| |[MA0052](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0052.md)|Performance|Replace constant Enum.ToString with nameof|ℹ️|✔️|✔️| -|[MA0053](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0053.md)|Design|Make class sealed|ℹ️|✔️|✔️| +|[MA0053](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0053.md)|Design|Make class or record sealed|ℹ️|✔️|✔️| |[MA0054](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0054.md)|Design|Embed the caught exception as innerException|⚠️|✔️|❌| |[MA0055](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0055.md)|Design|Do not use finalizer|⚠️|✔️|❌| |[MA0056](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0056.md)|Design|Do not call overridable members in constructor|⚠️|✔️|❌| @@ -342,7 +342,7 @@ dotnet_diagnostic.MA0051.severity = warning # MA0052: Replace constant Enum.ToString with nameof dotnet_diagnostic.MA0052.severity = suggestion -# MA0053: Make class sealed +# MA0053: Make class or record sealed dotnet_diagnostic.MA0053.severity = suggestion # MA0054: Embed the caught exception as innerException @@ -874,7 +874,7 @@ dotnet_diagnostic.MA0051.severity = none # MA0052: Replace constant Enum.ToString with nameof dotnet_diagnostic.MA0052.severity = none -# MA0053: Make class sealed +# MA0053: Make class or record sealed dotnet_diagnostic.MA0053.severity = none # MA0054: Embed the caught exception as innerException diff --git a/docs/Rules/MA0053.md b/docs/Rules/MA0053.md index 5a8498595..16b5b7762 100644 --- a/docs/Rules/MA0053.md +++ b/docs/Rules/MA0053.md @@ -1,9 +1,9 @@ -# MA0053 - Make class sealed +# MA0053 - Make class or record sealed Sources: [ClassMustBeSealedAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/ClassMustBeSealedAnalyzer.cs), [ClassMustBeSealedFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/ClassMustBeSealedFixer.cs) -Classes should be sealed when there is no inheritor. +Classes and records should be sealed when there is no inheritor. - [Why Are So Many Of The Framework Classes Sealed?](https://blogs.msdn.microsoft.com/ericlippert/2004/01/22/why-are-so-many-of-the-framework-classes-sealed/) - [Performance benefits of sealed class in .NET](https://www.meziantou.net/performance-benefits-of-sealed-class.htm) @@ -27,15 +27,15 @@ public sealed class Bar : Foo # Configuration A Roslyn analyzer can only know the current project context, not the full solution. -Therefore it cannot know if a public class is used in another project hereby making it possibly inaccurate to report this diagnostic for `public` classes. -You can still enable this rule for `public` classes using the `.editorconfig`: +Therefore it cannot know if a public class or record is used in another project hereby making it possibly inaccurate to report this diagnostic for `public` types. +You can still enable this rule for `public` classes and records using the `.editorconfig`: ```` # .editorconfig file MA0053.public_class_should_be_sealed = true ```` -Classes with `virtual` members cannot be sealed. By default, these classes are not reported. You can enable this rule for classes with `virtual` members using the `.editorconfig`: +Classes and records with `virtual` members cannot be sealed. By default, these types are not reported. You can enable this rule for classes and records with `virtual` members using the `.editorconfig`: ```` # .editorconfig file diff --git a/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig b/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig index 75332eb90..e7a44d2b4 100644 --- a/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig +++ b/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig @@ -155,7 +155,7 @@ dotnet_diagnostic.MA0051.severity = warning # MA0052: Replace constant Enum.ToString with nameof dotnet_diagnostic.MA0052.severity = suggestion -# MA0053: Make class sealed +# MA0053: Make class or record sealed dotnet_diagnostic.MA0053.severity = suggestion # MA0054: Embed the caught exception as innerException diff --git a/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig b/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig index 437df1174..29afc8b8c 100644 --- a/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig +++ b/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig @@ -155,7 +155,7 @@ dotnet_diagnostic.MA0051.severity = none # MA0052: Replace constant Enum.ToString with nameof dotnet_diagnostic.MA0052.severity = none -# MA0053: Make class sealed +# MA0053: Make class or record sealed dotnet_diagnostic.MA0053.severity = none # MA0054: Embed the caught exception as innerException diff --git a/src/Meziantou.Analyzer/Rules/ClassMustBeSealedAnalyzer.cs b/src/Meziantou.Analyzer/Rules/ClassMustBeSealedAnalyzer.cs index db3c9aded..944e92c8c 100644 --- a/src/Meziantou.Analyzer/Rules/ClassMustBeSealedAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/ClassMustBeSealedAnalyzer.cs @@ -11,8 +11,8 @@ public sealed class ClassMustBeSealedAnalyzer : DiagnosticAnalyzer { private static readonly DiagnosticDescriptor Rule = new( RuleIdentifiers.ClassMustBeSealed, - title: "Make class sealed", - messageFormat: "Make class sealed", + title: "Make class or record sealed", + messageFormat: "Make class or record sealed", RuleCategories.Design, DiagnosticSeverity.Info, isEnabledByDefault: true, @@ -107,14 +107,13 @@ private bool IsPotentialSealed(AnalyzerOptions options, INamedTypeSymbol symbol, if (symbol.IsTopLevelStatement(cancellationToken)) return false; - if (symbol.GetMembers().Any(member => member.IsVirtual) && !SealedClassWithVirtualMember(options, symbol)) + if (symbol.GetMembers().Any(member => member.IsVirtual && member.CanBeReferencedByName && !member.IsImplicitlyDeclared) && !SealedClassWithVirtualMember(options, symbol)) return false; var canBeInheritedOutsideOfAssembly = symbol.IsVisibleOutsideOfAssembly() && symbol.GetMembers().OfType().Any(member => member.MethodKind is MethodKind.Constructor && member.IsVisibleOutsideOfAssembly()); if (canBeInheritedOutsideOfAssembly && !PublicClassShouldBeSealed(options, symbol)) return false; - return true; } diff --git a/tests/Meziantou.Analyzer.Test/Rules/ClassMustBeSealedAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/ClassMustBeSealedAnalyzerTests.cs index bbb0f1bfc..96335e67d 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/ClassMustBeSealedAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/ClassMustBeSealedAnalyzerTests.cs @@ -254,6 +254,68 @@ internal sealed record Sample(); .ValidateAsync(); } + [Fact] + public async Task Record_Inherited_Diagnostic() + { + await CreateProjectBuilder() + .WithSourceCode(""" + record Base(); + + record [||]Derived() : Base(); + """) + .ShouldFixCodeWith(""" + record Base(); + + sealed record Derived() : Base(); + """) + .ValidateAsync(); + } + + [Fact] + public async Task Record_ImplementInterface_Diagnostic() + { + await CreateProjectBuilder() + .WithSourceCode(""" + interface ITest + { + } + + record [||]Test() : ITest; + """) + .ShouldFixCodeWith(""" + interface ITest + { + } + + sealed record Test() : ITest; + """) + .ValidateAsync(); + } + + [Fact] + public async Task Record_Public_NotReported() + { + await CreateProjectBuilder() + .WithSourceCode(""" + public record Sample(); + """) + .ValidateAsync(); + } + + [Fact] + public async Task Record_Public_WithEditorConfig_Diagnostic() + { + await CreateProjectBuilder() + .AddAnalyzerConfiguration("MA0053.public_class_should_be_sealed", "true") + .WithSourceCode(""" + public record [||]Sample(); + """) + .ShouldFixCodeWith(""" + public sealed record Sample(); + """) + .ValidateAsync(); + } + [Theory] [InlineData("private")] [InlineData("internal")] From f2aa48f45dd1dcefed5c1caa7167474dc52d38e4 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 3 Nov 2025 21:01:13 -0500 Subject: [PATCH 37/38] Fix MA0095 to not report for CRTP-inherited IEquatable (#918) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gérald Barré --- docs/Rules/MA0095.md | 38 ++++- global.json | 6 +- ...ityShouldBeCorrectlyImplementedAnalyzer.cs | 130 +++++++++++------ ...CorrectlyImplementedAnalyzerMA0095Tests.cs | 133 ++++++++++++++++++ 4 files changed, 254 insertions(+), 53 deletions(-) create mode 100644 tests/Meziantou.Analyzer.Test/Rules/EqualityShouldBeCorrectlyImplementedAnalyzerMA0095Tests.cs diff --git a/docs/Rules/MA0095.md b/docs/Rules/MA0095.md index 52497acb4..933bbfa47 100644 --- a/docs/Rules/MA0095.md +++ b/docs/Rules/MA0095.md @@ -3,15 +3,43 @@ Source: [EqualityShouldBeCorrectlyImplementedAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/EqualityShouldBeCorrectlyImplementedAnalyzer.cs) +When a type directly implements `IEquatable`, it should also override `Equals(object)` to ensure consistent equality behavior across different contexts. + +## Non-compliant Code + +````c# +// non-compliant +public sealed class Test : IEquatable +{ + public bool Equals(Test? other) => throw null; +} +```` + ````c# -class Test : IEquatable // non-compliant +public class Base : IEquatable { - public bool Equals(Test other) => throw null; + public bool Equals(Base? other) => throw null; + public override bool Equals(object? obj) => throw null; + public override int GetHashCode() => 0; } -class Test : IEquatable // ok +// non-compliant +public sealed class Derived : Base, IEquatable +{ + public bool Equals(Derived? other) => throw null; + // Missing override of Equals(object) +} +```` + +## Compliant Code + +Direct implementation with `Equals(object)` override: + +````c# +public sealed class Test : IEquatable { - public override bool Equals(object other) => throw null; - public bool Equals(Test other) => throw null; + public override bool Equals(object? obj) => obj is Test other && Equals(other); + public bool Equals(Test? other) => throw null; + public override int GetHashCode() => 0; } ```` diff --git a/global.json b/global.json index 7d0d05b7d..0aa9fcd25 100644 --- a/global.json +++ b/global.json @@ -4,8 +4,8 @@ "rollForward": "latestPatch" }, "msbuild-sdks": { - "Meziantou.NET.Sdk": "1.0.38", - "Meziantou.NET.Sdk.Test": "1.0.38", - "Meziantou.NET.Sdk.Web": "1.0.38" + "Meziantou.NET.Sdk": "1.0.40", + "Meziantou.NET.Sdk.Test": "1.0.40", + "Meziantou.NET.Sdk.Web": "1.0.40" } } diff --git a/src/Meziantou.Analyzer/Rules/EqualityShouldBeCorrectlyImplementedAnalyzer.cs b/src/Meziantou.Analyzer/Rules/EqualityShouldBeCorrectlyImplementedAnalyzer.cs index bc7cc5b37..815274965 100644 --- a/src/Meziantou.Analyzer/Rules/EqualityShouldBeCorrectlyImplementedAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/EqualityShouldBeCorrectlyImplementedAnalyzer.cs @@ -79,6 +79,9 @@ public override void Initialize(AnalysisContext context) private sealed class AnalyzerContext(Compilation compilation) { + public IMethodSymbol? ObjectEqualsSymbol { get; set; } = compilation.GetSpecialType(SpecialType.System_Object).GetMembers("Equals").FirstOrDefault() as IMethodSymbol; + public IMethodSymbol? ValueTypeEqualsSymbol { get; set; } = compilation.GetSpecialType(SpecialType.System_ValueType).GetMembers("Equals").FirstOrDefault() as IMethodSymbol; + public INamedTypeSymbol? IComparableSymbol { get; set; } = compilation.GetBestTypeByMetadataName("System.IComparable"); public INamedTypeSymbol? IComparableOfTSymbol { get; set; } = compilation.GetBestTypeByMetadataName("System.IComparable`1"); public INamedTypeSymbol? IEquatableOfTSymbol { get; set; } = compilation.GetBestTypeByMetadataName("System.IEquatable`1"); @@ -96,6 +99,7 @@ public void AnalyzeSymbol(SymbolAnalysisContext context) var implementIComparable = false; var implementIComparableOfT = false; var implementIEquatableOfT = false; + var directlyImplementIEquatableOfT = false; foreach (var implementedInterface in symbol.AllInterfaces) { if (implementedInterface.IsEqualTo(IComparableSymbol)) @@ -112,6 +116,16 @@ public void AnalyzeSymbol(SymbolAnalysisContext context) } } + // Check if the type directly implements IEquatable (not inherited from base class) + foreach (var implementedInterface in symbol.Interfaces) + { + if (IEquatableOfTSymbol is not null && implementedInterface.IsEqualTo(IEquatableOfTSymbol.Construct(symbol))) + { + directlyImplementIEquatableOfT = true; + break; + } + } + // IComparable without IComparable if (implementIComparable && !implementIComparableOfT) { @@ -125,7 +139,9 @@ public void AnalyzeSymbol(SymbolAnalysisContext context) } // IEquatable without Equals(object) - if (implementIEquatableOfT && !HasMethod(symbol, IsEqualsMethod)) + // Only report if directly implemented (not inherited via CRTP - Curiously Recurring Template Pattern) + // Check the entire type hierarchy for an Equals(object) override + if (directlyImplementIEquatableOfT && !symbol.GetMembers().OfType().Any(IsEqualsMethodOverride)) { context.ReportDiagnostic(OverrideEqualsObjectRule, symbol); } @@ -148,22 +164,32 @@ public void AnalyzeSymbol(SymbolAnalysisContext context) context.ReportDiagnostic(AddComparisonRule, symbol); } } - } - private static bool HasMethod(INamedTypeSymbol parentType, Func predicate) - { - foreach (var member in parentType.GetMembers().OfType()) + private static bool HasMethod(INamedTypeSymbol parentType, Func predicate) { - if (predicate(member)) - return true; + foreach (var member in parentType.GetMembers().OfType()) + { + if (predicate(member)) + return true; + } + + return false; } - return false; - } + private static bool HasMethodInHierarchy(INamedTypeSymbol type, Func predicate) + { + foreach (var member in type.GetAllMembers().OfType()) + { + if (predicate(member)) + return true; + } - private static bool HasComparisonOperator(INamedTypeSymbol parentType) - { - var operatorNames = new List(6) + return false; + } + + private static bool HasComparisonOperator(INamedTypeSymbol parentType) + { + var operatorNames = new List(6) { "op_LessThan", "op_LessThanOrEqual", @@ -173,44 +199,58 @@ private static bool HasComparisonOperator(INamedTypeSymbol parentType) "op_Inequality", }; - foreach (var member in parentType.GetAllMembers().OfType()) - { - if (member.MethodKind is MethodKind.UserDefinedOperator) + foreach (var member in parentType.GetAllMembers().OfType()) { - operatorNames.Remove(member.Name); + if (member.MethodKind is MethodKind.UserDefinedOperator) + { + operatorNames.Remove(member.Name); + } } + + return operatorNames.Count == 0; } - return operatorNames.Count == 0; - } + private static bool IsEqualsMethod(IMethodSymbol symbol) + { + return symbol.Name == nameof(object.Equals) && + symbol.ReturnType.IsBoolean() && + symbol.Parameters.Length == 1 && + symbol.Parameters[0].Type.IsObject() && + symbol.DeclaredAccessibility == Accessibility.Public && + !symbol.IsStatic; + } - private static bool IsEqualsMethod(IMethodSymbol symbol) - { - return symbol.Name == nameof(object.Equals) && - symbol.ReturnType.IsBoolean() && - symbol.Parameters.Length == 1 && - symbol.Parameters[0].Type.IsObject() && - symbol.DeclaredAccessibility == Accessibility.Public && - !symbol.IsStatic; - } + private bool IsEqualsMethodOverride(IMethodSymbol symbol) + { + // Check if it's an Equals(object) method AND it's overridden (not the base System.Object method) + return symbol.Name == nameof(object.Equals) && + symbol.ReturnType.IsBoolean() && + symbol.Parameters.Length == 1 && + symbol.Parameters[0].Type.IsObject() && + symbol.DeclaredAccessibility == Accessibility.Public && + !symbol.IsStatic && + !symbol.IsEqualTo(ObjectEqualsSymbol) && + !symbol.IsEqualTo(ValueTypeEqualsSymbol); + } - private static bool IsCompareToMethod(IMethodSymbol symbol) - { - return symbol.Name == nameof(IComparable.CompareTo) && - symbol.ReturnType.IsInt32() && - symbol.Parameters.Length == 1 && - symbol.Parameters[0].Type.IsObject() && - symbol.DeclaredAccessibility == Accessibility.Public && - !symbol.IsStatic; - } + private static bool IsCompareToMethod(IMethodSymbol symbol) + { + return symbol.Name == nameof(IComparable.CompareTo) && + symbol.ReturnType.IsInt32() && + symbol.Parameters.Length == 1 && + symbol.Parameters[0].Type.IsObject() && + symbol.DeclaredAccessibility == Accessibility.Public && + !symbol.IsStatic; + } - private static bool IsCompareToOfTMethod(IMethodSymbol symbol) - { - return symbol.Name == nameof(IComparable.CompareTo) && - symbol.ReturnType.IsInt32() && - symbol.Parameters.Length == 1 && - symbol.Parameters[0].Type.IsEqualTo(symbol.ContainingType) && - symbol.DeclaredAccessibility == Accessibility.Public && - !symbol.IsStatic; + private static bool IsCompareToOfTMethod(IMethodSymbol symbol) + { + return symbol.Name == nameof(IComparable.CompareTo) && + symbol.ReturnType.IsInt32() && + symbol.Parameters.Length == 1 && + symbol.Parameters[0].Type.IsEqualTo(symbol.ContainingType) && + symbol.DeclaredAccessibility == Accessibility.Public && + !symbol.IsStatic; + } } -} +} \ No newline at end of file diff --git a/tests/Meziantou.Analyzer.Test/Rules/EqualityShouldBeCorrectlyImplementedAnalyzerMA0095Tests.cs b/tests/Meziantou.Analyzer.Test/Rules/EqualityShouldBeCorrectlyImplementedAnalyzerMA0095Tests.cs new file mode 100644 index 000000000..f104dd457 --- /dev/null +++ b/tests/Meziantou.Analyzer.Test/Rules/EqualityShouldBeCorrectlyImplementedAnalyzerMA0095Tests.cs @@ -0,0 +1,133 @@ +using Meziantou.Analyzer.Rules; +using TestHelper; + +namespace Meziantou.Analyzer.Test.Rules; + +public sealed class EqualityShouldBeCorrectlyImplementedAnalyzerMA0095Tests +{ + private static ProjectBuilder CreateProjectBuilder() + { + return new ProjectBuilder() + .WithAnalyzer() + .WithCodeFixProvider(); + } + + [Fact] + public async Task DirectImplementation_WithoutEqualsObject_ShouldTrigger() + { + var originalCode = """ +using System; + +public sealed class [|TriggersMA0095AndCA1067|] : IEquatable +{ + public bool Equals(TriggersMA0095AndCA1067? other) => true; +} +"""; + + await CreateProjectBuilder() + .WithSourceCode(originalCode) + .ValidateAsync(); + } + + [Fact] + public async Task DirectImplementation_WithEqualsObject_ShouldNotTrigger() + { + var originalCode = """ +using System; + +public sealed class Test : IEquatable +{ + public bool Equals(Test? other) => true; + public override bool Equals(object? obj) => true; + public override int GetHashCode() => 0; +} +"""; + + await CreateProjectBuilder() + .WithSourceCode(originalCode) + .ValidateAsync(); + } + +#if ROSLYN_4_8_OR_GREATER + [Fact] + public async Task CRTP_WithoutEqualsObject_ShouldNotTrigger() + { + var originalCode = """ +using System; + +public abstract class Crtp : IEquatable where T : Crtp +{ + public bool Equals(T? other) => true; +} + +public sealed class TriggersMA0095Only : Crtp; +"""; + + await CreateProjectBuilder() + .WithSourceCode(originalCode) + .ValidateAsync(); + } + + [Fact] + public async Task CRTP_WithEqualsObjectInBase_ShouldNotTrigger() + { + var originalCode = """ +using System; + +public abstract class Crtp : IEquatable where T : Crtp +{ + public bool Equals(T? other) => true; + public override bool Equals(object? obj) => true; + public override int GetHashCode() => 0; +} + +public sealed class DerivedClass : Crtp; +"""; + + await CreateProjectBuilder() + .WithSourceCode(originalCode) + .ValidateAsync(); + } +#endif + + [Fact] + public async Task InheritedIEquatable_WithDirectImplementationToo_ShouldTrigger() + { + var originalCode = """ +using System; + +public abstract class Base : IEquatable +{ + public bool Equals(Base? other) => true; + public override bool Equals(object? obj) => true; + public override int GetHashCode() => 0; +} + +public sealed class [|Derived|] : Base, IEquatable +{ + public bool Equals(Derived? other) => true; +} +"""; + + await CreateProjectBuilder() + .WithSourceCode(originalCode) + .ValidateAsync(); + } + + [Fact] + public async Task Struct_DirectImplementation_WithoutEqualsObject_ShouldTrigger() + { + var originalCode = """ +using System; + +public struct [|TestStruct|] : IEquatable +{ + public bool Equals(TestStruct other) => true; +} +"""; + + await CreateProjectBuilder() + .WithSourceCode(originalCode) + .ValidateAsync(); + } +} From 4c972612fda0ded07f42957a86ad36576c6ae995 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 4 Nov 2025 00:30:16 -0500 Subject: [PATCH 38/38] MA0011: Skip diagnostic for culture-invariant interpolated strings (#919) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gérald Barré --- .github/workflows/ci.yml | 4 +- Directory.Build.targets | 15 +- .../Meziantou.Analyzer.Pack.csproj | 3 + .../CultureSensitiveFormattingContext.cs | 50 +++++ .../Rules/UseIFormatProviderAnalyzer.cs | 2 +- .../Rules/UseIFormatProviderAnalyzerTests.cs | 194 ++++++++++++++++++ 6 files changed, 265 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4891cb8cd..d7c1e8c64 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,12 +67,14 @@ jobs: - run: dotnet build src/Meziantou.Analyzer/Meziantou.Analyzer.csproj --configuration Release /p:RoslynVersion=roslyn4.4 /p:Version=${{ needs.compute_package_version.outputs.package_version }} /bl:Meziantou.Analyzer.roslyn4.4.binlog - run: dotnet build src/Meziantou.Analyzer/Meziantou.Analyzer.csproj --configuration Release /p:RoslynVersion=roslyn4.6 /p:Version=${{ needs.compute_package_version.outputs.package_version }} /bl:Meziantou.Analyzer.roslyn4.6.binlog - run: dotnet build src/Meziantou.Analyzer/Meziantou.Analyzer.csproj --configuration Release /p:RoslynVersion=roslyn4.8 /p:Version=${{ needs.compute_package_version.outputs.package_version }} /bl:Meziantou.Analyzer.roslyn4.8.binlog + - run: dotnet build src/Meziantou.Analyzer/Meziantou.Analyzer.csproj --configuration Release /p:RoslynVersion=roslyn4.14 /p:Version=${{ needs.compute_package_version.outputs.package_version }} /bl:Meziantou.Analyzer.roslyn4.14.binlog - run: dotnet build src/Meziantou.Analyzer.CodeFixers/Meziantou.Analyzer.CodeFixers.csproj --configuration Release /p:RoslynVersion=roslyn3.8 /p:Version=${{ needs.compute_package_version.outputs.package_version }} /bl:Meziantou.Analyzer.CodeFixers.roslyn3.8.binlog - run: dotnet build src/Meziantou.Analyzer.CodeFixers/Meziantou.Analyzer.CodeFixers.csproj --configuration Release /p:RoslynVersion=roslyn4.2 /p:Version=${{ needs.compute_package_version.outputs.package_version }} /bl:Meziantou.Analyzer.CodeFixers.roslyn4.2.binlog - run: dotnet build src/Meziantou.Analyzer.CodeFixers/Meziantou.Analyzer.CodeFixers.csproj --configuration Release /p:RoslynVersion=roslyn4.4 /p:Version=${{ needs.compute_package_version.outputs.package_version }} /bl:Meziantou.Analyzer.CodeFixers.roslyn4.4.binlog - run: dotnet build src/Meziantou.Analyzer.CodeFixers/Meziantou.Analyzer.CodeFixers.csproj --configuration Release /p:RoslynVersion=roslyn4.6 /p:Version=${{ needs.compute_package_version.outputs.package_version }} /bl:Meziantou.Analyzer.CodeFixers.roslyn4.6.binlog - run: dotnet build src/Meziantou.Analyzer.CodeFixers/Meziantou.Analyzer.CodeFixers.csproj --configuration Release /p:RoslynVersion=roslyn4.8 /p:Version=${{ needs.compute_package_version.outputs.package_version }} /bl:Meziantou.Analyzer.CodeFixers.roslyn4.8.binlog + - run: dotnet build src/Meziantou.Analyzer.CodeFixers/Meziantou.Analyzer.CodeFixers.csproj --configuration Release /p:RoslynVersion=roslyn4.14 /p:Version=${{ needs.compute_package_version.outputs.package_version }} /bl:Meziantou.Analyzer.CodeFixers.roslyn4.14.binlog - run: dotnet restore src/Meziantou.Analyzer.Pack/Meziantou.Analyzer.Pack.csproj -bl:Meziantou.Analyzer.Pack.restore.binlog - run: dotnet pack src/Meziantou.Analyzer.Pack/Meziantou.Analyzer.Pack.csproj --configuration Release --no-build /p:Version=${{ needs.compute_package_version.outputs.package_version }} /bl:Meziantou.Analyzer.Pack.pack.binlog @@ -127,7 +129,7 @@ jobs: matrix: runs-on: [ ubuntu-latest ] configuration: [ Release ] - roslyn-version: [ 'roslyn3.8', 'roslyn4.2', 'roslyn4.4', 'roslyn4.6', 'roslyn4.8', 'default' ] + roslyn-version: [ 'roslyn3.8', 'roslyn4.2', 'roslyn4.4', 'roslyn4.6', 'roslyn4.8', 'roslyn4.14', 'default' ] fail-fast: false steps: - uses: actions/checkout@v4 diff --git a/Directory.Build.targets b/Directory.Build.targets index 45198f25b..3561832b1 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -72,6 +72,19 @@ + + + + + + + + $(DefineConstants);ROSLYN_4_8;ROSLYN_4_2_OR_GREATER;ROSLYN_4_4_OR_GREATER;ROSLYN_4_6_OR_GREATER;ROSLYN_4_8_OR_GREATER;ROSLYN_4_14_OR_GREATER + $(DefineConstants);CSHARP9_OR_GREATER;CSHARP10_OR_GREATER;CSHARP11_OR_GREATER;CSHARP12_OR_GREATER;CSHARP13_OR_GREATER + $(NoWarn);nullable + + + @@ -79,7 +92,7 @@ - $(DefineConstants);ROSLYN5_0;ROSLYN_4_2_OR_GREATER;ROSLYN_4_4_OR_GREATER;ROSLYN_4_5_OR_GREATER;ROSLYN_4_6_OR_GREATER;ROSLYN_4_8_OR_GREATER;ROSLYN_4_10_OR_GREATER;ROSLYN_5_0_OR_GREATER + $(DefineConstants);ROSLYN5_0;ROSLYN_4_2_OR_GREATER;ROSLYN_4_4_OR_GREATER;ROSLYN_4_5_OR_GREATER;ROSLYN_4_6_OR_GREATER;ROSLYN_4_8_OR_GREATER;ROSLYN_4_10_OR_GREATER;ROSLYN_4_14_OR_GREATER;ROSLYN_5_0_OR_GREATER $(DefineConstants);CSHARP9_OR_GREATER;CSHARP10_OR_GREATER;CSHARP11_OR_GREATER;CSHARP12_OR_GREATER;CSHARP13_OR_GREATER;CSHARP14_OR_GREATER $(NoWarn);CS0618 diff --git a/src/Meziantou.Analyzer.Pack/Meziantou.Analyzer.Pack.csproj b/src/Meziantou.Analyzer.Pack/Meziantou.Analyzer.Pack.csproj index 3bdaf071e..115183275 100644 --- a/src/Meziantou.Analyzer.Pack/Meziantou.Analyzer.Pack.csproj +++ b/src/Meziantou.Analyzer.Pack/Meziantou.Analyzer.Pack.csproj @@ -37,5 +37,8 @@ + + + \ No newline at end of file diff --git a/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs b/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs index cdf10d059..8a8d6f1a3 100755 --- a/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs +++ b/src/Meziantou.Analyzer/Internals/CultureSensitiveFormattingContext.cs @@ -26,6 +26,8 @@ internal sealed class CultureSensitiveFormattingContext(Compilation compilation) public INamedTypeSymbol? SystemWindowsFontStretchSymbol { get; } = compilation.GetBestTypeByMetadataName("System.Windows.FontStretch"); public INamedTypeSymbol? SystemWindowsMediaBrushSymbol { get; } = compilation.GetBestTypeByMetadataName("System.Windows.Media.Brush"); public INamedTypeSymbol? NuGetVersioningSemanticVersionSymbol { get; } = compilation.GetBestTypeByMetadataName("NuGet.Versioning.SemanticVersion"); + public INamedTypeSymbol? FormattableStringSymbol { get; } = compilation.GetBestTypeByMetadataName("System.FormattableString"); + public INamedTypeSymbol? InterpolatedStringHandlerAttributeSymbol { get; } = compilation.GetBestTypeByMetadataName("System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute"); private static HashSet CreateExcludedMethods(Compilation compilation) { @@ -127,12 +129,22 @@ public bool IsCultureSensitiveOperation(IOperation operation, CultureSensitiveOp return initializer.ElementValues.Any(arg => IsCultureSensitiveOperation(arg.UnwrapImplicitConversionOperations(), options)); } +#if ROSLYN_4_14_OR_GREATER + else if (invocation.TargetMethod.Parameters.Length == 2 && invocation.Arguments[1].Value is ICollectionExpressionOperation collectionExpression) + { + return collectionExpression.Elements.Any(arg => IsCultureSensitiveOperation(arg.UnwrapImplicitConversionOperations(), options)); + } +#endif else { return invocation.Arguments.Skip(1).Any(arg => IsCultureSensitiveOperation(arg.Value.UnwrapImplicitConversionOperations(), options)); } } + // Check if all interpolated string arguments are culture-invariant + if (HasOnlyCultureInvariantInterpolatedStringArguments(invocation, options)) + return false; + if ((options & CultureSensitiveOptions.UseInvocationReturnType) == CultureSensitiveOptions.UseInvocationReturnType) return IsCultureSensitiveType(invocation.Type, options); @@ -175,6 +187,9 @@ public bool IsCultureSensitiveOperation(IOperation operation, CultureSensitiveOp } #endif + if (operation is IConversionOperation interpolatedConversion && interpolatedConversion.Type.IsEqualTo(FormattableStringSymbol)) + return IsCultureSensitiveOperation(interpolatedConversion.Operand, options); + if (operation is IInterpolatedStringOperation interpolatedString) { if (interpolatedString.Parts.Length == 0) @@ -482,4 +497,39 @@ private static bool IsConstantPositiveNumber(IOperation operation) return false; } + + private bool HasOnlyCultureInvariantInterpolatedStringArguments(IInvocationOperation invocation, CultureSensitiveOptions options) + { + var hasInterpolatedStringParam = false; + + foreach (var argument in invocation.Arguments) + { + var argumentType = argument.Value.Type; + if (argumentType is null) + continue; + + if (IsInterpolatedStringType(argumentType)) + { + hasInterpolatedStringParam = true; + + // If any interpolated string argument is culture-sensitive, return false + if (IsCultureSensitiveOperation(argument.Value, options)) + return false; + } + } + + // Return true only if we found interpolated string parameters and all were culture-invariant + return hasInterpolatedStringParam; + } + + private bool IsInterpolatedStringType(ITypeSymbol typeSymbol) + { + if (typeSymbol.IsEqualTo(FormattableStringSymbol)) + return true; + + if (InterpolatedStringHandlerAttributeSymbol is not null && typeSymbol.HasAttribute(InterpolatedStringHandlerAttributeSymbol)) + return true; + + return false; + } } diff --git a/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs index e17245c94..493d5e1c3 100644 --- a/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs +++ b/src/Meziantou.Analyzer/Rules/UseIFormatProviderAnalyzer.cs @@ -1,4 +1,4 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using Meziantou.Analyzer.Configurations; using Meziantou.Analyzer.Internals; using Microsoft.CodeAnalysis; diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseIFormatProviderAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseIFormatProviderAnalyzerTests.cs index 752da8f39..a8900552d 100755 --- a/tests/Meziantou.Analyzer.Test/Rules/UseIFormatProviderAnalyzerTests.cs +++ b/tests/Meziantou.Analyzer.Test/Rules/UseIFormatProviderAnalyzerTests.cs @@ -11,6 +11,7 @@ private static ProjectBuilder CreateProjectBuilder() return new ProjectBuilder() .WithAnalyzer() .WithOutputKind(Microsoft.CodeAnalysis.OutputKind.ConsoleApplication) + .WithTargetFramework(TargetFramework.NetLatest) .AddMeziantouAttributes(); } @@ -314,6 +315,199 @@ class Location public override string ToString() => throw null; public string ToString(System.IFormatProvider formatProvider) => throw null; } +"""; + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + +#if CSHARP10_OR_GREATER + [Fact] + public async Task InterpolatedStringHandler_CultureSensitiveFormat_ShouldReport() + { + var sourceCode = """ +using System; +using System.Runtime.CompilerServices; + +[||]A.Print($"{DateTime.Now:D}"); + +class A +{ + public static void Print(ref DefaultInterpolatedStringHandler interpolatedStringHandler) => throw null; + public static void Print(IFormatProvider provider, ref DefaultInterpolatedStringHandler interpolatedStringHandler) => throw null; +} +"""; + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task InterpolatedStringHandler_CultureInvariantFormat_ShouldNotReport() + { + var sourceCode = """ +using System; +using System.Runtime.CompilerServices; + +A.Print($"{DateTime.Now:o}"); + +class A +{ + public static void Print(ref DefaultInterpolatedStringHandler interpolatedStringHandler) => throw null; + public static void Print(IFormatProvider provider, ref DefaultInterpolatedStringHandler interpolatedStringHandler) => throw null; +} +"""; + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task InterpolatedStringHandler_NoFormattableTypes_ShouldNotReport() + { + var sourceCode = """ +using System; +using System.Runtime.CompilerServices; + +A.Print($"XXX"); + +class A +{ + public static void Print(ref DefaultInterpolatedStringHandler interpolatedStringHandler) => throw null; + public static void Print(IFormatProvider provider, ref DefaultInterpolatedStringHandler interpolatedStringHandler) => throw null; +} +"""; + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task InterpolatedStringHandler_MixedFormats_ShouldReport() + { + var sourceCode = """ +using System; +using System.Runtime.CompilerServices; + +[||]A.Print($"{DateTime.Now:o} | {DateTime.Now:D}"); + +class A +{ + public static void Print(ref DefaultInterpolatedStringHandler interpolatedStringHandler) => throw null; + public static void Print(IFormatProvider provider, ref DefaultInterpolatedStringHandler interpolatedStringHandler) => throw null; +} +"""; + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task InterpolatedStringHandler_CustomTypeWithAttribute_CultureInvariantFormat_ShouldNotReport() + { + var sourceCode = """ +using System; +using System.Runtime.CompilerServices; +using Meziantou.Analyzer.Annotations; + +A.Print($"{new Bar():o}"); + +class A +{ + public static void Print(ref DefaultInterpolatedStringHandler interpolatedStringHandler) => throw null; + public static void Print(IFormatProvider provider, ref DefaultInterpolatedStringHandler interpolatedStringHandler) => throw null; +} + +[CultureInsensitiveType(format: "o")] +sealed class Bar : IFormattable +{ + public string ToString(string? format, IFormatProvider? formatProvider) => string.Empty; +} +"""; + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task InterpolatedStringHandler_CustomTypeWithAttribute_CultureSensitiveFormat_ShouldReport() + { + var sourceCode = """ +using System; +using System.Runtime.CompilerServices; +using Meziantou.Analyzer.Annotations; + +[||]A.Print($"{new Bar():D}"); + +class A +{ + public static void Print(ref DefaultInterpolatedStringHandler interpolatedStringHandler) => throw null; + public static void Print(IFormatProvider provider, ref DefaultInterpolatedStringHandler interpolatedStringHandler) => throw null; +} + +[CultureInsensitiveType(format: "o")] +sealed class Bar : IFormattable +{ + public string ToString(string? format, IFormatProvider? formatProvider) => string.Empty; +} +"""; + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task InterpolatedStringHandler_NoOverload_ShouldNotReport() + { + var sourceCode = """ +using System; +using System.Runtime.CompilerServices; + +A.Print($"{DateTime.Now:D}"); + +class A +{ + public static void Print(ref DefaultInterpolatedStringHandler interpolatedStringHandler) => throw null; +} +"""; + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } +#endif + + [Fact] + public async Task FormattableString_CultureSensitiveFormat_ShouldReport() + { + var sourceCode = """ +using System; + +[||]A.Sample($"{DateTime.Now:D}"); + +class A +{ + public static void Sample(FormattableString value) => throw null; + public static void Sample(IFormatProvider format, FormattableString value) => throw null; +} +"""; + await CreateProjectBuilder() + .WithSourceCode(sourceCode) + .ValidateAsync(); + } + + [Fact] + public async Task FormattableString_CultureInvariantFormat_ShouldNotReport() + { + var sourceCode = """ +using System; + +A.Sample($"{DateTime.Now:o}"); + +class A +{ + public static void Sample(FormattableString value) => throw null; + public static void Sample(IFormatProvider format, FormattableString value) => throw null; +} """; await CreateProjectBuilder() .WithSourceCode(sourceCode)