diff --git a/.omnisharp/omnisharp.json b/.omnisharp/omnisharp.json new file mode 100644 index 0000000..28c2827 --- /dev/null +++ b/.omnisharp/omnisharp.json @@ -0,0 +1,9 @@ +{ + "RoslynExtensionsOptions": { + "enableImportCompletion": true, + "enableAnalyzersSupport": true, + "locationPaths": [ + "../src/ImmutableAnalyzer/ImmutableAnalyzer/bin/Release/netstandard2.0/publish/ImmutableAnalyzer.dll" + ] + } +} diff --git a/src/.vscode/launch.json b/src/.vscode/launch.json new file mode 100644 index 0000000..850a95c --- /dev/null +++ b/src/.vscode/launch.json @@ -0,0 +1,26 @@ +{ + "version": "0.2.0", + "configurations": [ + { + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md + "name": ".NET Core Launch (console)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/ImmutableAnalyzer/ImmutableAnalyzer.Tests/bin/Debug/net6.0/ImmutableAnalyzer.Tests.dll", + "args": [], + "cwd": "${workspaceFolder}/ImmutableAnalyzer/ImmutableAnalyzer.Tests", + // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console + "console": "internalConsole", + "stopAtEntry": false + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + } + ] +} \ No newline at end of file diff --git a/src/.vscode/tasks.json b/src/.vscode/tasks.json new file mode 100644 index 0000000..4a7b073 --- /dev/null +++ b/src/.vscode/tasks.json @@ -0,0 +1,41 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/ImmutableAnalyzer.sln", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary;ForceNoAlign" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/ImmutableAnalyzer.sln", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary;ForceNoAlign" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "--project", + "${workspaceFolder}/ImmutableAnalyzer.sln" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer.Tests/Factories/SourceFactory.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer.Tests/Factories/SourceFactory.cs index 49ad3ac..713d90d 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer.Tests/Factories/SourceFactory.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer.Tests/Factories/SourceFactory.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; namespace ImmutableAnalyzer.Tests.Factories; @@ -114,4 +114,4 @@ private static (int Line, int Column) GetLineAndColumn(string source, string tex return (line + 1, column + 1); } -} \ No newline at end of file +} diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer.Tests/PropertyAnalyzersTests/SetAccessorTests/SetAccessorAnalyzerTests.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer.Tests/PropertyAnalyzersTests/SetAccessorTests/SetAccessorAnalyzerTests.cs index 790e1b6..70c701e 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer.Tests/PropertyAnalyzersTests/SetAccessorTests/SetAccessorAnalyzerTests.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer.Tests/PropertyAnalyzersTests/SetAccessorTests/SetAccessorAnalyzerTests.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using ImmutableAnalyzer.PropertyAnalyzers.SetAccessor; using ImmutableAnalyzer.Tests.Factories; using Xunit; @@ -44,4 +44,4 @@ public async Task Immutable_class_property_could_have_no_setter() var source = SourceFactory.ImmutableClassWithGetOnlyPropertyAccessor(); await Verifier.VerifyAnalyzerAsync(source); } -} \ No newline at end of file +} diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer.Tests/PropertyAnalyzersTests/SetAccessorTests/SetAccessorCodeFixTests.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer.Tests/PropertyAnalyzersTests/SetAccessorTests/SetAccessorCodeFixTests.cs index 2ca5656..3d8bcf8 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer.Tests/PropertyAnalyzersTests/SetAccessorTests/SetAccessorCodeFixTests.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer.Tests/PropertyAnalyzersTests/SetAccessorTests/SetAccessorCodeFixTests.cs @@ -5,15 +5,15 @@ using Microsoft.CodeAnalysis.Testing; using Xunit; using FixStrategy = ImmutableAnalyzer.PropertyAnalyzers.SetAccessor.SetAccessorCodeFixProvider.FixStrategy; -using Verifier = Microsoft.CodeAnalysis.CSharp.Testing.XUnit.CodeFixVerifier< - ImmutableAnalyzer.PropertyAnalyzers.SetAccessor.SetAccessorAnalyzer, - ImmutableAnalyzer.PropertyAnalyzers.SetAccessor.SetAccessorCodeFixProvider ->; using SetAccessorCodeFixTest = Microsoft.CodeAnalysis.CSharp.Testing.CSharpCodeFixTest< ImmutableAnalyzer.PropertyAnalyzers.SetAccessor.SetAccessorAnalyzer, ImmutableAnalyzer.PropertyAnalyzers.SetAccessor.SetAccessorCodeFixProvider, Microsoft.CodeAnalysis.Testing.Verifiers.XUnitVerifier >; +using Verifier = Microsoft.CodeAnalysis.CSharp.Testing.XUnit.CodeFixVerifier< + ImmutableAnalyzer.PropertyAnalyzers.SetAccessor.SetAccessorAnalyzer, + ImmutableAnalyzer.PropertyAnalyzers.SetAccessor.SetAccessorCodeFixProvider +>; namespace ImmutableAnalyzer.Tests.PropertyAnalyzersTests.SetAccessorTests; @@ -60,10 +60,10 @@ private static SetAccessorCodeFixTest CreateTest(FixStrategy fixStrategy) var fixedSource = fixStrategy switch { - FixStrategy.Remove => SourceFactory.ImmutableClassWithGetOnlyPropertyAccessor(), - FixStrategy.ToInit => SourceFactory.ImmutableClassWithPropertyAccessor("init", out _, out _), + FixStrategy.Remove => SourceFactory.ImmutableClassWithGetOnlyPropertyAccessor(), + FixStrategy.ToInit => SourceFactory.ImmutableClassWithPropertyAccessor("init", out _, out _), FixStrategy.ToPrivate => SourceFactory.ImmutableClassWithPropertyAccessor("private set", out _, out _), - _ => throw new ArgumentOutOfRangeException(nameof(fixStrategy), fixStrategy, null) + _ => throw new ArgumentOutOfRangeException(nameof(fixStrategy), fixStrategy, null) }; return new SetAccessorCodeFixTest diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer.Tests/RecordAnalyzersTests/ParameterTypeAnalyzerTests.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer.Tests/RecordAnalyzersTests/ParameterTypeAnalyzerTests.cs index febf4ad..78a1729 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer.Tests/RecordAnalyzersTests/ParameterTypeAnalyzerTests.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer.Tests/RecordAnalyzersTests/ParameterTypeAnalyzerTests.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using ImmutableAnalyzer.ParameterAnalyzers; using ImmutableAnalyzer.Tests.Factories; using Xunit; @@ -31,4 +31,4 @@ public async Task Record_with_mutable_property_should_not_be_immutable(string pr var expected = Verifier.Diagnostic().WithLocation(line, column).WithArguments(property); await Verifier.VerifyAnalyzerAsync(source, expected); } -} \ No newline at end of file +} diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer.Tests/TestData.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer.Tests/TestData.cs index eedd7f8..21c67f9 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer.Tests/TestData.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer.Tests/TestData.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using Xunit; -using TypeCheckerConst = ImmutableAnalyzer.Utils.TypeChecking.Const.TypeCheckerConst; +using Types = ImmutableAnalyzer.Utils.TypeChecking.Const.Types; namespace ImmutableAnalyzer.Tests; @@ -13,26 +13,26 @@ public static class TestData { /// /// Represent immutable built-in types. - /// + /// /// public class ImmutableTypes : TheoryData { public ImmutableTypes() { - foreach (var typeName in TypeCheckerConst.ImmutableTypes) + foreach (var typeName in Types.ImmutableTypes) Add(typeName); } } /// /// Represent immutable built-in generic types. - /// + /// /// public class ImmutableGenericsTypes : TheoryData { public ImmutableGenericsTypes() { - foreach (var typeName in TypeCheckerConst.ImmutableGenericTypes) + foreach (var typeName in Types.Generic) Add(CreateGenericTypeStringWithParams(typeName, nameof(Int32))); } diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer/AssemblyInfo.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer/AssemblyInfo.cs index a0250af..11f07ef 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer/AssemblyInfo.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer/AssemblyInfo.cs @@ -1,3 +1,3 @@ -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; -[assembly:InternalsVisibleTo("ImmutableAnalyzer.Tests")] \ No newline at end of file +[assembly: InternalsVisibleTo("ImmutableAnalyzer.Tests")] diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer/Abstractions/MemberAnalyzer.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer/Common/MemberAnalyzer.cs similarity index 84% rename from src/ImmutableAnalyzer/ImmutableAnalyzer/Abstractions/MemberAnalyzer.cs rename to src/ImmutableAnalyzer/ImmutableAnalyzer/Common/MemberAnalyzer.cs index 6aad35d..259b9ec 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer/Abstractions/MemberAnalyzer.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer/Common/MemberAnalyzer.cs @@ -1,13 +1,14 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -namespace ImmutableAnalyzer.Abstractions; +namespace ImmutableAnalyzer.Common; /// /// Base class for member analyzers in immutable class. /// Type of class member to analyze. /// -internal abstract class MemberAnalyzer : TypeDeclarationAnalyzer +internal abstract class MemberAnalyzer + : TypeDeclarationAnalyzer { /// /// Execute analyzer. @@ -18,8 +19,7 @@ internal abstract class MemberAnalyzer : TypeDeclarationAnalyzer< /// /// Execute analyzer. - /// Executes only if class declaration - /// marked by . + /// Executes only if class declaration marked by . /// /// Type declaration node. /// Analysis context. diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer/Abstractions/ParameterAnalyzer.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer/Common/ParameterAnalyzer.cs similarity index 90% rename from src/ImmutableAnalyzer/ImmutableAnalyzer/Abstractions/ParameterAnalyzer.cs rename to src/ImmutableAnalyzer/ImmutableAnalyzer/Common/ParameterAnalyzer.cs index e2c495e..fccc484 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer/Abstractions/ParameterAnalyzer.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer/Common/ParameterAnalyzer.cs @@ -1,7 +1,7 @@ -using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -namespace ImmutableAnalyzer.Abstractions; +namespace ImmutableAnalyzer.Common; /// /// Base class for parameter analyzer in immutable records. diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer/Abstractions/TypeDeclarationAnalyzer.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer/Common/TypeDeclarationAnalyzer.cs similarity index 87% rename from src/ImmutableAnalyzer/ImmutableAnalyzer/Abstractions/TypeDeclarationAnalyzer.cs rename to src/ImmutableAnalyzer/ImmutableAnalyzer/Common/TypeDeclarationAnalyzer.cs index 193c9e2..a01c565 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer/Abstractions/TypeDeclarationAnalyzer.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer/Common/TypeDeclarationAnalyzer.cs @@ -1,9 +1,9 @@ -using ImmutableAnalyzer.Extensions; +using ImmutableAnalyzer.Extensions; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -namespace ImmutableAnalyzer.Abstractions; +namespace ImmutableAnalyzer.Common; /// /// Base class for type declaration analyzer. @@ -40,7 +40,9 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context) if (context.ContainingSymbol is { } symbol && !symbol.HasAttribute()) return; - if (context.Node is TDeclarationSyntax typeDeclarationSyntax) - AnalyzeTypeDeclaration(typeDeclarationSyntax, context); + if (context.Node is not TDeclarationSyntax typeDeclarationSyntax) + return; + + AnalyzeTypeDeclaration(typeDeclarationSyntax, context); } } diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer/Extensions/SymbolExtensions.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer/Extensions/SymbolExtensions.cs index 7fea587..5cb8e2f 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer/Extensions/SymbolExtensions.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer/Extensions/SymbolExtensions.cs @@ -10,15 +10,15 @@ namespace ImmutableAnalyzer.Extensions; internal static class SymbolExtensions { /// - /// Checks if marked by given attribute. + /// Checks if marked by given attribute. /// /// Symbol, exposed by compiler. - /// true - if symbol marked by given attribute, otherwise - false. + /// true - if marked by given attribute, otherwise - false. public static bool HasAttribute(this ISymbol symbol) => symbol.GetAttributes().Any(data => data.AttributeClass?.Name == typeof(T).Name); /// - /// Returns symbol from type and context. + /// Returns symbol from type. /// /// Type syntax expression. /// Semantic model. @@ -30,8 +30,8 @@ public static ITypeSymbol GetTypeSymbolFromSyntaxNode(this SemanticModel semanti return symbol switch { IArrayTypeSymbol arrayTypeSymbol => arrayTypeSymbol.ElementType, // detect arrays e.g. int[] -> int - not null => symbol, // just return type symbol - _ => throw new NotSupportedException("Not supported symbol") + not null => symbol, // just return type symbol + _ => throw new NotSupportedException("Not supported symbol") }; } } diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer/ImmutableAttribute.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer/ImmutableAttribute.cs index 0285297..4add122 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer/ImmutableAttribute.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer/ImmutableAttribute.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace ImmutableAnalyzer; @@ -6,4 +6,4 @@ namespace ImmutableAnalyzer; /// Attribute, that represent immutable class. /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface)] -public class ImmutableAttribute : Attribute { } \ No newline at end of file +public class ImmutableAttribute : Attribute { } diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer/ParameterAnalyzers/ParameterTypeAnalyzer.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer/ParameterAnalyzers/ParameterTypeAnalyzer.cs index 1437339..59aaba7 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer/ParameterAnalyzers/ParameterTypeAnalyzer.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer/ParameterAnalyzers/ParameterTypeAnalyzer.cs @@ -1,5 +1,5 @@ using System.Collections.Immutable; -using ImmutableAnalyzer.Abstractions; +using ImmutableAnalyzer.Common; using ImmutableAnalyzer.Extensions; using ImmutableAnalyzer.Utils.TypeChecking; using Microsoft.CodeAnalysis; @@ -20,20 +20,22 @@ internal class ParameterTypeAnalyzer : ParameterAnalyzer private static readonly TypeChecker Checker = TypeCheckerFactory.GetOrCreate(); /// - public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + public override ImmutableArray SupportedDiagnostics { get; } = + ImmutableArray.Create(Rule); /// /// Diagnostic descriptor. /// - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( - id: "IM0003", - title: "Mutable parameter in record parameter list", - messageFormat: "Immutable record can't have parameter of type '{0}'", - category: "Design", - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: "Record parameter must have immutable type." - ); + private static readonly DiagnosticDescriptor Rule = + new( + id: "IM0003", + title: "Mutable parameter in record parameter list", + messageFormat: "Immutable record can't have parameter of type '{0}'", + category: "Design", + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: "Record parameter must have immutable type." + ); /// protected override void AnalyzeParameter(ParameterSyntax node, SyntaxNodeAnalysisContext ctx) @@ -42,7 +44,8 @@ protected override void AnalyzeParameter(ParameterSyntax node, SyntaxNodeAnalysi return; var diagnostic = Diagnostic.Create( - Rule, node.Type!.GetLocation(), + Rule, + node.Type!.GetLocation(), node.Type.ToFullString().Trim() ); diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/PropertyAnalyzer.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/PropertyAnalyzer.cs index 81aedfb..b8fd86e 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/PropertyAnalyzer.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/PropertyAnalyzer.cs @@ -1,4 +1,4 @@ -using ImmutableAnalyzer.Abstractions; +using ImmutableAnalyzer.Common; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace ImmutableAnalyzer.PropertyAnalyzers; @@ -6,4 +6,4 @@ namespace ImmutableAnalyzer.PropertyAnalyzers; /// /// Base class for property analyzer in immutable type. /// -internal abstract class PropertyAnalyzer : MemberAnalyzer { } \ No newline at end of file +internal abstract class PropertyAnalyzer : MemberAnalyzer { } diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/PropertyType/PropertyTypeAnalyzer.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/PropertyType/PropertyTypeAnalyzer.cs index 3e5d2be..30d933a 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/PropertyType/PropertyTypeAnalyzer.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/PropertyType/PropertyTypeAnalyzer.cs @@ -1,4 +1,4 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using ImmutableAnalyzer.Extensions; using ImmutableAnalyzer.Utils.TypeChecking; using Microsoft.CodeAnalysis; @@ -24,14 +24,14 @@ internal sealed class PropertyTypeAnalyzer : PropertyAnalyzer /// /// Diagnostic descriptor. /// - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( - id: "IM0001", - title: "Mutable property in immutable type", - messageFormat: "Immutable type can't have property of type '{0}'", - category: "Design", - defaultSeverity: DiagnosticSeverity.Error, + private static readonly DiagnosticDescriptor Rule = new( + id: "IM0001", + title: "Mutable property in immutable type", + messageFormat: "Immutable type can't have property of type '{0}'", + category: "Design", + defaultSeverity: DiagnosticSeverity.Error, isEnabledByDefault: true, - description: "Class member must have immutable type." + description: "Class member must have immutable type." ); /// diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/SetAccessor/CodeFixes/ChangeSetAccessorStrategy.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/SetAccessor/CodeFixes/ChangeSetAccessorStrategy.cs index 43ebc63..6fa8f3c 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/SetAccessor/CodeFixes/ChangeSetAccessorStrategy.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/SetAccessor/CodeFixes/ChangeSetAccessorStrategy.cs @@ -1,4 +1,4 @@ -using System.Threading; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -9,13 +9,13 @@ namespace ImmutableAnalyzer.PropertyAnalyzers.SetAccessor.CodeFixes; /// /// Base class for change set accessor strategy. /// -internal abstract class ChangeSetAccessorStrategy +internal abstract class ChangeSetAccessorCodeFix { /// /// Delegate, which used to get new from given. /// /// We must return new accessor instead of modifying given because it's immutable. - protected delegate AccessorDeclarationSyntax? AccessorModifier(AccessorDeclarationSyntax originalNode); + protected delegate void AccessorModifier(AccessorDeclarationSyntax originalNode, DocumentEditor editor); /// /// Gets title for code fix. @@ -30,7 +30,7 @@ internal abstract class ChangeSetAccessorStrategy protected abstract AccessorModifier Modifier { get; } /// - /// Applies to given node. + /// Applies to given . /// /// Original . /// Node to apply changes. @@ -39,13 +39,8 @@ internal abstract class ChangeSetAccessorStrategy public async Task ChangeDocument(Document originalDocument, AccessorDeclarationSyntax node, CancellationToken ct) { var editor = await DocumentEditor.CreateAsync(originalDocument, ct).ConfigureAwait(false); - var newNode = Modifier.Invoke(node); - - if (newNode is null) - editor.RemoveNode(node); - else - editor.ReplaceNode(node, newNode); + Modifier.Invoke(node, editor); return editor.GetChangedDocument(); } -} \ No newline at end of file +} diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/SetAccessor/CodeFixes/RemoveSetAccessor.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/SetAccessor/CodeFixes/RemoveSetAccessor.cs index dd39bbe..3736c5f 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/SetAccessor/CodeFixes/RemoveSetAccessor.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/SetAccessor/CodeFixes/RemoveSetAccessor.cs @@ -1,13 +1,13 @@ -namespace ImmutableAnalyzer.PropertyAnalyzers.SetAccessor.CodeFixes; +namespace ImmutableAnalyzer.PropertyAnalyzers.SetAccessor.CodeFixes; /// /// Removes 'set' accessor. /// -internal class RemoveSetAccessor : ChangeSetAccessorStrategy +internal class RemoveSetAccessor : ChangeSetAccessorCodeFix { /// public override string GetTitle(string format) => "Remove set accessor"; /// - protected override AccessorModifier Modifier { get; } = _ => null; -} \ No newline at end of file + protected override AccessorModifier Modifier { get; } = (node, editor) => editor.RemoveNode(node); +} diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/SetAccessor/CodeFixes/ToInit.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/SetAccessor/CodeFixes/ToInit.cs index 8c9a7a1..dae7afb 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/SetAccessor/CodeFixes/ToInit.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/SetAccessor/CodeFixes/ToInit.cs @@ -5,11 +5,11 @@ namespace ImmutableAnalyzer.PropertyAnalyzers.SetAccessor.CodeFixes; /// /// Changes 'set' accessor to 'init'. /// -internal class ToInit : ChangeSetAccessorStrategy +internal class ToInit : ChangeSetAccessorCodeFix { /// public override string GetTitle(string format) => string.Format(format, "init"); /// - protected override AccessorModifier Modifier { get; } = syntax => syntax.WithKeyword(SyntaxFactory.Token(SyntaxKind.InitKeyword)); + protected override AccessorModifier Modifier { get; } = (node, editor) => editor.ReplaceNode(node, node.WithKeyword(SyntaxFactory.Token(SyntaxKind.InitKeyword))); } diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/SetAccessor/CodeFixes/ToPrivate.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/SetAccessor/CodeFixes/ToPrivate.cs index d3d526a..31ee510 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/SetAccessor/CodeFixes/ToPrivate.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/SetAccessor/CodeFixes/ToPrivate.cs @@ -1,4 +1,4 @@ -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; namespace ImmutableAnalyzer.PropertyAnalyzers.SetAccessor.CodeFixes; @@ -6,12 +6,12 @@ namespace ImmutableAnalyzer.PropertyAnalyzers.SetAccessor.CodeFixes; /// /// Changes access modifier of 'set' accessor to private. /// -internal class ToPrivate : ChangeSetAccessorStrategy +internal class ToPrivate : ChangeSetAccessorCodeFix { /// public override string GetTitle(string format) => string.Format(format, "private set"); /// - protected override AccessorModifier Modifier { get; } = - syntax => syntax.WithModifiers(new SyntaxTokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword))); -} \ No newline at end of file + protected override AccessorModifier Modifier { get; } = (node, editor) => + editor.ReplaceNode(node, node.WithModifiers(new SyntaxTokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword)))); +} diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/SetAccessor/SetAccessorAnalyzer.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/SetAccessor/SetAccessorAnalyzer.cs index 9634037..4d041e6 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/SetAccessor/SetAccessorAnalyzer.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/SetAccessor/SetAccessorAnalyzer.cs @@ -1,4 +1,4 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -24,21 +24,20 @@ internal sealed class SetAccessorAnalyzer : PropertyAnalyzer /// /// Diagnostic descriptor. /// - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( - id: DiagnosticId, - title: "Public setter violates type immutability", - messageFormat: "Member of immutable type can't have '{0}' accessor", - category: "Design", - defaultSeverity: DiagnosticSeverity.Error, + private static readonly DiagnosticDescriptor Rule = new( + id: DiagnosticId, + title: "Public setter violates type immutability", + messageFormat: "Member of immutable type can't have '{0}' accessor", + category: "Design", + defaultSeverity: DiagnosticSeverity.Error, isEnabledByDefault: true, - description: "Setter can't be public, because it's give possibility to change member from the outer." + description: "Setter can't be public, because it's give possibility to change member from the outer." ); /// protected override void AnalyzeMember(PropertyDeclarationSyntax node, SyntaxNodeAnalysisContext ctx) { - var setAccessor = node.AccessorList? - .Accessors + var setAccessor = node.AccessorList?.Accessors .FirstOrDefault(syntax => syntax.IsKind(SyntaxKind.SetAccessorDeclaration)); if (setAccessor is null || !ShouldReport(setAccessor)) @@ -63,4 +62,4 @@ private static bool ShouldReport(AccessorDeclarationSyntax setAccessor) var modifiers = setAccessor.Modifiers; return !(modifiers.Any(SyntaxKind.PrivateKeyword) || modifiers.Any(SyntaxKind.InitKeyword)); } -} \ No newline at end of file +} diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/SetAccessor/SetAccessorCodeFixProvider.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/SetAccessor/SetAccessorCodeFixProvider.cs index 12fe8ba..120d4a4 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/SetAccessor/SetAccessorCodeFixProvider.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer/PropertyAnalyzers/SetAccessor/SetAccessorCodeFixProvider.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Linq; @@ -23,11 +23,11 @@ public class SetAccessorCodeFixProvider : CodeFixProvider public enum FixStrategy { /// Mapped to . - ToInit = 0, + ToInit = 0, /// Mapped to . ToPrivate = 1, /// Mapped to . - Remove = 2, + Remove = 2, } /// @@ -43,8 +43,8 @@ public enum FixStrategy /// /// Key-Value pairs of strategies to codefix. /// - private static readonly IReadOnlyDictionary ChangeSetAccessorStrategies = - new SortedDictionary + private static readonly IReadOnlyDictionary ChangeSetAccessorFixes = + new SortedDictionary { { FixStrategy.ToInit, new ToInit() }, { FixStrategy.ToPrivate, new ToPrivate() }, @@ -67,15 +67,15 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) if (root?.FindNode(diagnostic.Location.SourceSpan) is not AccessorDeclarationSyntax node) return; - foreach (var strategy in ChangeSetAccessorStrategies.Values) + foreach (var strategy in ChangeSetAccessorFixes.Values) { var action = CodeAction.Create( - title: strategy.GetTitle(TitleFormat), + title: strategy.GetTitle(TitleFormat), createChangedDocument: ct => strategy.ChangeDocument(context.Document, node, ct), - equivalenceKey: EquivalencyKey + equivalenceKey: EquivalencyKey ); context.RegisterCodeFix(action, diagnostic); } } -} \ No newline at end of file +} diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/Const/TypeCheckerConst.GenericsTypes.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/Const/Types.Generics.cs similarity index 76% rename from src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/Const/TypeCheckerConst.GenericsTypes.cs rename to src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/Const/Types.Generics.cs index 7b98ad5..7da2d58 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/Const/TypeCheckerConst.GenericsTypes.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/Const/Types.Generics.cs @@ -1,17 +1,17 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Collections.Immutable; namespace ImmutableAnalyzer.Utils.TypeChecking.Const; -internal partial struct TypeCheckerConst +internal readonly partial struct Types { /// /// Set of generic immutable class types. /// - public static readonly ImmutableArray ImmutableGenericTypes = ImmutableArray.Create( + public static readonly ImmutableArray Generic = ImmutableArray.Create( typeof(ImmutableArray<>).Name, typeof(ImmutableDictionary<,>).Name, typeof(ImmutableList<>).Name, typeof(ImmutableHashSet<>).Name, typeof(ImmutableSortedDictionary<,>).Name, typeof(ImmutableSortedSet<>).Name, typeof(ImmutableStack<>).Name, typeof(ImmutableQueue<>).Name, typeof(IReadOnlyList<>).Name, typeof(IReadOnlyCollection<>).Name, typeof(IReadOnlyDictionary<,>).Name ); -} \ No newline at end of file +} diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/Const/TypeCheckerConst.Types.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/Const/Types.cs similarity index 90% rename from src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/Const/TypeCheckerConst.Types.cs rename to src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/Const/Types.cs index 9a28222..2a50b16 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/Const/TypeCheckerConst.Types.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/Const/Types.cs @@ -1,9 +1,9 @@ -using System; +using System; using System.Collections.Immutable; namespace ImmutableAnalyzer.Utils.TypeChecking.Const; -internal partial struct TypeCheckerConst +internal partial struct Types { /// /// Set of valid immutable types. @@ -13,4 +13,4 @@ internal partial struct TypeCheckerConst nameof(Int32), nameof(UInt32), nameof(Int64), nameof(UInt64), nameof(Int16), nameof(UInt16), nameof(String), nameof(DateTime), nameof(Guid), nameof(Enum) ); -} \ No newline at end of file +} diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/DeclaredTypeSymbolChecker.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/DeclaredTypeSymbolChecker.cs index 82d7162..0846d3e 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/DeclaredTypeSymbolChecker.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/DeclaredTypeSymbolChecker.cs @@ -1,30 +1,23 @@ -using ImmutableAnalyzer.Extensions; using Microsoft.CodeAnalysis; +using ImmutableAnalyzer.Extensions; namespace ImmutableAnalyzer.Utils.TypeChecking; /// /// Checks for immutability by declared types. /// -internal class DeclaredTypeSymbolChecker : TypeChecker +internal class DeclaredTypeSymbolChecker(TypeChecker? inner = null) : TypeChecker { - private readonly TypeChecker? _inner; - - public DeclaredTypeSymbolChecker(TypeChecker? inner = null) - { - _inner = inner; - } + private readonly TypeChecker? _inner = inner; /// public override bool IsImmutable(ITypeSymbol typeSymbol) { - var isImmutableFromInner = _inner?.IsImmutable(typeSymbol); - - if (isImmutableFromInner.HasValue && !isImmutableFromInner.Value) + if (_inner?.IsImmutable(typeSymbol) == false) return false; - return ((isImmutableFromInner ?? true) && typeSymbol.HasAttribute()) || - Const.TypeCheckerConst.ImmutableTypes.Contains(typeSymbol.Name) || - Const.TypeCheckerConst.ImmutableGenericTypes.Contains(typeSymbol.MetadataName); + return typeSymbol.HasAttribute() || + Const.Types.ImmutableTypes.Contains(typeSymbol.Name) || + Const.Types.Generic.Contains(typeSymbol.MetadataName); } } diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/RecursiveTypeSymbolChecker.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/RecursiveTypeSymbolChecker.cs index 9a257b0..31eb6ae 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/RecursiveTypeSymbolChecker.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/RecursiveTypeSymbolChecker.cs @@ -6,27 +6,18 @@ namespace ImmutableAnalyzer.Utils.TypeChecking; /// /// Decorator to check recursively. /// -internal class RecursiveTypeSymbolChecker : TypeChecker +internal class RecursiveTypeSymbolChecker(TypeChecker inner) : TypeChecker { - private readonly TypeChecker _inner; - - public RecursiveTypeSymbolChecker(TypeChecker inner) - { - _inner = inner; - } + private readonly TypeChecker _inner = inner; /// public override bool IsImmutable(ITypeSymbol typeSymbol) { - while (true) - { - if (_inner.IsImmutable(typeSymbol)) - return true; - - if (typeSymbol.BaseType is not { } baseType) - return typeSymbol.AllInterfaces.Any(IsImmutable); + if (_inner.IsImmutable(typeSymbol)) + return true; - typeSymbol = baseType; - } + return typeSymbol.BaseType is not null + ? IsImmutable(typeSymbol.BaseType) + : typeSymbol.AllInterfaces.Any(IsImmutable); } } diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/TypeChecker.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/TypeChecker.cs index 572b8f1..8d8ded1 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/TypeChecker.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/TypeChecker.cs @@ -1,4 +1,4 @@ -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; namespace ImmutableAnalyzer.Utils.TypeChecking; @@ -8,9 +8,9 @@ namespace ImmutableAnalyzer.Utils.TypeChecking; internal abstract class TypeChecker { /// - /// Checks given type symbol for immutability. + /// Checks for immutability. /// /// Type symbol. - /// true - if given type symbol is immutable, otherwise - false. + /// true - if is immutable, otherwise - false. public abstract bool IsImmutable(ITypeSymbol typeSymbol); } diff --git a/src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/TypeCheckerFactory.cs b/src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/TypeCheckerFactory.cs index 132928b..4d8d850 100644 --- a/src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/TypeCheckerFactory.cs +++ b/src/ImmutableAnalyzer/ImmutableAnalyzer/Utils/TypeChecking/TypeCheckerFactory.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; namespace ImmutableAnalyzer.Utils.TypeChecking; @@ -26,4 +26,4 @@ private static TypeChecker Create() return new RecursiveTypeSymbolChecker(declaredTypesChecker); } -} \ No newline at end of file +}