From 06ea6bc3cd678297c8ff601ed3c33bf847b662e0 Mon Sep 17 00:00:00 2001 From: Richard Murillo Date: Wed, 23 Oct 2024 09:37:52 -0700 Subject: [PATCH 1/5] Remove ECS0200 suppression and update WellKnownTypes to not trigger ECS0600 --- ...ConstructorArgumentsShouldMatchAnalyzer.cs | 26 +++++++++++-------- src/Common/MoqMethodDescriptorBase.cs | 2 +- src/Common/WellKnownTypeNames.cs | 24 ++++++++--------- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs b/src/Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs index 41107a6d3..ebd69ed86 100644 --- a/src/Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs +++ b/src/Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs @@ -205,18 +205,22 @@ private static void AnalyzeInstanceCall(SyntaxNodeAnalysisContext context) return; } - switch (genericNameSyntax.Identifier.Value) + if (genericNameSyntax.Identifier.Value is not string genericNameSyntaxIdentifierValue) { - case WellKnownTypeNames.Create: - AnalyzeInvocation(context, invocationExpressionSyntax, WellKnownTypeNames.MockFactory, true, true); - break; - - case WellKnownTypeNames.Of: - AnalyzeInvocation(context, invocationExpressionSyntax, WellKnownTypeNames.MockName, false, true); - break; + return; + } - default: - return; + if (string.Equals(genericNameSyntaxIdentifierValue, WellKnownTypeNames.Create, StringComparison.Ordinal)) + { + AnalyzeInvocation(context, invocationExpressionSyntax, WellKnownTypeNames.MockFactory, true, true); + } + else if (string.Equals(genericNameSyntaxIdentifierValue, WellKnownTypeNames.Of, StringComparison.Ordinal)) + { + AnalyzeInvocation(context, invocationExpressionSyntax, WellKnownTypeNames.Mock, false, true); + } + else + { + return; } } @@ -286,7 +290,7 @@ private static void AnalyzeNewObject(SyntaxNodeAnalysisContext context) // Quick check if (!string.Equals( genericNameSyntax.Identifier.ValueText, - WellKnownTypeNames.MockName, + WellKnownTypeNames.Mock, StringComparison.Ordinal)) { return; diff --git a/src/Common/MoqMethodDescriptorBase.cs b/src/Common/MoqMethodDescriptorBase.cs index 263922641..b779d8b70 100644 --- a/src/Common/MoqMethodDescriptorBase.cs +++ b/src/Common/MoqMethodDescriptorBase.cs @@ -12,7 +12,7 @@ internal abstract class MoqMethodDescriptorBase { private static readonly string ContainingNamespace = WellKnownTypeNames.Moq; - private static readonly string ContainingType = WellKnownTypeNames.MockName; + private static readonly string ContainingType = WellKnownTypeNames.Mock; public abstract bool IsMatch(SemanticModel semanticModel, MemberAccessExpressionSyntax memberAccessSyntax, CancellationToken cancellationToken); diff --git a/src/Common/WellKnownTypeNames.cs b/src/Common/WellKnownTypeNames.cs index e47bc65d7..185cbd29a 100644 --- a/src/Common/WellKnownTypeNames.cs +++ b/src/Common/WellKnownTypeNames.cs @@ -1,18 +1,16 @@ namespace Moq.Analyzers.Common; -#pragma warning disable ECS0200 // Consider using readonly instead of const for flexibility - internal static class WellKnownTypeNames { - internal const string Moq = nameof(Moq); - internal const string MockName = "Mock"; - internal const string MockBehavior = nameof(MockBehavior); - internal const string MockFactory = nameof(MockFactory); - internal const string MoqMock = $"{Moq}.{MockName}"; - internal const string MoqMock1 = $"{MoqMock}`1"; - internal const string MoqBehavior = $"{Moq}.{MockBehavior}"; - internal const string MoqRepository = $"{Moq}.MockRepository"; - internal const string As = nameof(As); - internal const string Create = nameof(Create); - internal const string Of = nameof(Of); + internal static readonly string Moq = nameof(Moq); + internal static readonly string Mock = nameof(Mock); + internal static readonly string MockBehavior = nameof(MockBehavior); + internal static readonly string MockFactory = nameof(MockFactory); + internal static readonly string MoqMock = $"{Moq}.{Mock}"; + internal static readonly string MoqMock1 = $"{MoqMock}`1"; + internal static readonly string MoqBehavior = $"{Moq}.{MockBehavior}"; + internal static readonly string MoqRepository = $"{Moq}.MockRepository"; + internal static readonly string As = nameof(As); + internal static readonly string Create = nameof(Create); + internal static readonly string Of = nameof(Of); } From 424f04d16ca0454e6b43a070d9af76825cec505d Mon Sep 17 00:00:00 2001 From: Richard Murillo Date: Wed, 23 Oct 2024 09:48:27 -0700 Subject: [PATCH 2/5] Remove redundant else --- src/Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs b/src/Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs index ebd69ed86..56ca95fba 100644 --- a/src/Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs +++ b/src/Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs @@ -218,10 +218,6 @@ private static void AnalyzeInstanceCall(SyntaxNodeAnalysisContext context) { AnalyzeInvocation(context, invocationExpressionSyntax, WellKnownTypeNames.Mock, false, true); } - else - { - return; - } } private static void AnalyzeInvocation( From a4e7819b8b24adeb5e0eec7537b974f04c85b89f Mon Sep 17 00:00:00 2001 From: Richard Murillo Date: Wed, 23 Oct 2024 11:05:18 -0700 Subject: [PATCH 3/5] Update WellKnownTypeNames based on PR feedback See https://github.com/rjmurillo/moq.analyzers/pull/236/files#r1813176017 --- src/Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs | 2 +- src/Common/MoqMethodDescriptorBase.cs | 2 +- src/Common/WellKnownTypeNames.cs | 9 +++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs b/src/Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs index 56ca95fba..6054544ea 100644 --- a/src/Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs +++ b/src/Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs @@ -297,7 +297,7 @@ private static void AnalyzeNewObject(SyntaxNodeAnalysisContext context) if (symbolInfo.Symbol is not IMethodSymbol mockConstructorMethod || mockConstructorMethod.MethodKind != MethodKind.Constructor - || !string.Equals(mockConstructorMethod.ContainingType.ConstructedFrom.ContainingSymbol.Name, WellKnownTypeNames.Moq, StringComparison.Ordinal)) + || !string.Equals(mockConstructorMethod.ContainingType.ConstructedFrom.ContainingSymbol.Name, WellKnownTypeNames.MoqSymbolName, StringComparison.Ordinal)) { return; } diff --git a/src/Common/MoqMethodDescriptorBase.cs b/src/Common/MoqMethodDescriptorBase.cs index b779d8b70..48627e39b 100644 --- a/src/Common/MoqMethodDescriptorBase.cs +++ b/src/Common/MoqMethodDescriptorBase.cs @@ -11,7 +11,7 @@ /// internal abstract class MoqMethodDescriptorBase { - private static readonly string ContainingNamespace = WellKnownTypeNames.Moq; + private static readonly string ContainingNamespace = WellKnownTypeNames.MoqNamespace; private static readonly string ContainingType = WellKnownTypeNames.Mock; public abstract bool IsMatch(SemanticModel semanticModel, MemberAccessExpressionSyntax memberAccessSyntax, CancellationToken cancellationToken); diff --git a/src/Common/WellKnownTypeNames.cs b/src/Common/WellKnownTypeNames.cs index 185cbd29a..ab5d29aa2 100644 --- a/src/Common/WellKnownTypeNames.cs +++ b/src/Common/WellKnownTypeNames.cs @@ -2,14 +2,15 @@ internal static class WellKnownTypeNames { - internal static readonly string Moq = nameof(Moq); + internal static readonly string MoqNamespace = "Moq"; + internal static readonly string MoqSymbolName = "Moq"; internal static readonly string Mock = nameof(Mock); internal static readonly string MockBehavior = nameof(MockBehavior); internal static readonly string MockFactory = nameof(MockFactory); - internal static readonly string MoqMock = $"{Moq}.{Mock}"; + internal static readonly string MoqMock = $"{MoqNamespace}.{Mock}"; internal static readonly string MoqMock1 = $"{MoqMock}`1"; - internal static readonly string MoqBehavior = $"{Moq}.{MockBehavior}"; - internal static readonly string MoqRepository = $"{Moq}.MockRepository"; + internal static readonly string MoqBehavior = $"{MoqNamespace}.{MockBehavior}"; + internal static readonly string MoqRepository = $"{MoqNamespace}.MockRepository"; internal static readonly string As = nameof(As); internal static readonly string Create = nameof(Create); internal static readonly string Of = nameof(Of); From afcafb601d2a44a641e453d168254f7748ef3e1a Mon Sep 17 00:00:00 2001 From: Richard Murillo Date: Wed, 23 Oct 2024 13:05:57 -0700 Subject: [PATCH 4/5] Update well known type names to be more perscriptive --- .../AsShouldBeUsedOnlyForInterfaceAnalyzer.cs | 2 +- ...ConstructorArgumentsShouldMatchAnalyzer.cs | 20 +++++++++---------- .../SetExplicitMockBehaviorAnalyzer.cs | 4 ++-- src/Common/CompilationExtensions.cs | 2 +- src/Common/MoqMethodDescriptorBase.cs | 4 ++-- src/Common/WellKnownMoqNames.cs | 20 +++++++++++++++++++ src/Common/WellKnownTypeNames.cs | 17 ---------------- 7 files changed, 36 insertions(+), 33 deletions(-) create mode 100644 src/Common/WellKnownMoqNames.cs delete mode 100644 src/Common/WellKnownTypeNames.cs diff --git a/src/Analyzers/AsShouldBeUsedOnlyForInterfaceAnalyzer.cs b/src/Analyzers/AsShouldBeUsedOnlyForInterfaceAnalyzer.cs index 099df996e..5ec398e9e 100644 --- a/src/Analyzers/AsShouldBeUsedOnlyForInterfaceAnalyzer.cs +++ b/src/Analyzers/AsShouldBeUsedOnlyForInterfaceAnalyzer.cs @@ -44,7 +44,7 @@ private static void RegisterCompilationStartAction(CompilationStartAnalysisConte // Look for the Mock.As() method and provide it to Analyze to avoid looking it up multiple times. #pragma warning disable ECS0900 // Minimize boxing and unboxing ImmutableArray asMethods = mockTypes - .SelectMany(mockType => mockType.GetMembers(WellKnownTypeNames.As)) + .SelectMany(mockType => mockType.GetMembers(WellKnownMoqNames.AsMethodName)) .OfType() .Where(method => method.IsGenericMethod) .ToImmutableArray(); diff --git a/src/Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs b/src/Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs index 6054544ea..02f1383a8 100644 --- a/src/Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs +++ b/src/Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs @@ -78,7 +78,7 @@ private static bool IsExpressionMockBehavior(SyntaxNodeAnalysisContext context, if (expression is MemberAccessExpressionSyntax memberAccessExpressionSyntax) { if (memberAccessExpressionSyntax.Expression is IdentifierNameSyntax identifierNameSyntax - && string.Equals(identifierNameSyntax.Identifier.ValueText, WellKnownTypeNames.MockBehavior, StringComparison.Ordinal)) + && string.Equals(identifierNameSyntax.Identifier.ValueText, WellKnownMoqNames.MockBehaviorTypeName, StringComparison.Ordinal)) { return true; } @@ -107,14 +107,14 @@ private static bool IsExpressionMockBehavior(SyntaxNodeAnalysisContext context, } if (typeSymbol != null - && string.Equals(typeSymbol.Name, WellKnownTypeNames.MockBehavior, StringComparison.Ordinal)) + && string.Equals(typeSymbol.Name, WellKnownMoqNames.MockBehaviorTypeName, StringComparison.Ordinal)) { return true; } } // Crude fallback to check if the expression is a Moq.MockBehavior enum - if (expression.ToString().StartsWith(WellKnownTypeNames.MoqBehavior, StringComparison.Ordinal)) + if (expression.ToString().StartsWith(WellKnownMoqNames.FullyQualifiedMoqBehaviorTypeName, StringComparison.Ordinal)) { return true; } @@ -210,13 +210,13 @@ private static void AnalyzeInstanceCall(SyntaxNodeAnalysisContext context) return; } - if (string.Equals(genericNameSyntaxIdentifierValue, WellKnownTypeNames.Create, StringComparison.Ordinal)) + if (string.Equals(genericNameSyntaxIdentifierValue, WellKnownMoqNames.CreateMethodName, StringComparison.Ordinal)) { - AnalyzeInvocation(context, invocationExpressionSyntax, WellKnownTypeNames.MockFactory, true, true); + AnalyzeInvocation(context, invocationExpressionSyntax, WellKnownMoqNames.MockFactoryTypeName, true, true); } - else if (string.Equals(genericNameSyntaxIdentifierValue, WellKnownTypeNames.Of, StringComparison.Ordinal)) + else if (string.Equals(genericNameSyntaxIdentifierValue, WellKnownMoqNames.OfMethodName, StringComparison.Ordinal)) { - AnalyzeInvocation(context, invocationExpressionSyntax, WellKnownTypeNames.Mock, false, true); + AnalyzeInvocation(context, invocationExpressionSyntax, WellKnownMoqNames.MockTypeName, false, true); } } @@ -252,7 +252,7 @@ private static void AnalyzeInvocation( // We are calling MockRepository.Create or Mock.Of, determine which ArgumentListSyntax? argumentList = null; - if (WellKnownTypeNames.Of.Equals(method.Name, StringComparison.Ordinal)) + if (WellKnownMoqNames.OfMethodName.Equals(method.Name, StringComparison.Ordinal)) { // Mock.Of can specify condition for construction and MockBehavior, but // cannot specify constructor parameters @@ -286,7 +286,7 @@ private static void AnalyzeNewObject(SyntaxNodeAnalysisContext context) // Quick check if (!string.Equals( genericNameSyntax.Identifier.ValueText, - WellKnownTypeNames.Mock, + WellKnownMoqNames.MockTypeName, StringComparison.Ordinal)) { return; @@ -297,7 +297,7 @@ private static void AnalyzeNewObject(SyntaxNodeAnalysisContext context) if (symbolInfo.Symbol is not IMethodSymbol mockConstructorMethod || mockConstructorMethod.MethodKind != MethodKind.Constructor - || !string.Equals(mockConstructorMethod.ContainingType.ConstructedFrom.ContainingSymbol.Name, WellKnownTypeNames.MoqSymbolName, StringComparison.Ordinal)) + || !string.Equals(mockConstructorMethod.ContainingType.ConstructedFrom.ContainingSymbol.Name, WellKnownMoqNames.MoqSymbolName, StringComparison.Ordinal)) { return; } diff --git a/src/Analyzers/SetExplicitMockBehaviorAnalyzer.cs b/src/Analyzers/SetExplicitMockBehaviorAnalyzer.cs index 1ccb05359..5752907c7 100644 --- a/src/Analyzers/SetExplicitMockBehaviorAnalyzer.cs +++ b/src/Analyzers/SetExplicitMockBehaviorAnalyzer.cs @@ -42,7 +42,7 @@ private static void RegisterCompilationStartAction(CompilationStartAnalysisConte } // Look for the MockBehavior type and provide it to Analyze to avoid looking it up multiple times. - INamedTypeSymbol? mockBehaviorSymbol = context.Compilation.GetTypeByMetadataName(WellKnownTypeNames.MoqBehavior); + INamedTypeSymbol? mockBehaviorSymbol = context.Compilation.GetTypeByMetadataName(WellKnownMoqNames.FullyQualifiedMoqBehaviorTypeName); if (mockBehaviorSymbol is null) { return; @@ -51,7 +51,7 @@ private static void RegisterCompilationStartAction(CompilationStartAnalysisConte // Look for the Mock.Of() method and provide it to Analyze to avoid looking it up multiple times. #pragma warning disable ECS0900 // Minimize boxing and unboxing ImmutableArray ofMethods = mockTypes - .SelectMany(mockType => mockType.GetMembers(WellKnownTypeNames.Of)) + .SelectMany(mockType => mockType.GetMembers(WellKnownMoqNames.OfMethodName)) .OfType() .Where(method => method.IsGenericMethod) .ToImmutableArray(); diff --git a/src/Common/CompilationExtensions.cs b/src/Common/CompilationExtensions.cs index a41692db6..ba775b298 100644 --- a/src/Common/CompilationExtensions.cs +++ b/src/Common/CompilationExtensions.cs @@ -34,6 +34,6 @@ public static ImmutableArray GetTypesByMetadataNames(this Comp /// public static ImmutableArray GetMoqMock(this Compilation compilation) { - return compilation.GetTypesByMetadataNames([WellKnownTypeNames.MoqMock, WellKnownTypeNames.MoqMock1, WellKnownTypeNames.MoqRepository]); + return compilation.GetTypesByMetadataNames([WellKnownMoqNames.FullyQualifiedMoqMockTypeName, WellKnownMoqNames.FullyQualifiedMoqMock1TypeName, WellKnownMoqNames.FullyQualifiedMoqRepositoryTypeName]); } } diff --git a/src/Common/MoqMethodDescriptorBase.cs b/src/Common/MoqMethodDescriptorBase.cs index 48627e39b..f531b8b71 100644 --- a/src/Common/MoqMethodDescriptorBase.cs +++ b/src/Common/MoqMethodDescriptorBase.cs @@ -11,8 +11,8 @@ /// internal abstract class MoqMethodDescriptorBase { - private static readonly string ContainingNamespace = WellKnownTypeNames.MoqNamespace; - private static readonly string ContainingType = WellKnownTypeNames.Mock; + private static readonly string ContainingNamespace = WellKnownMoqNames.MoqNamespace; + private static readonly string ContainingType = WellKnownMoqNames.MockTypeName; public abstract bool IsMatch(SemanticModel semanticModel, MemberAccessExpressionSyntax memberAccessSyntax, CancellationToken cancellationToken); diff --git a/src/Common/WellKnownMoqNames.cs b/src/Common/WellKnownMoqNames.cs new file mode 100644 index 000000000..69bb5be96 --- /dev/null +++ b/src/Common/WellKnownMoqNames.cs @@ -0,0 +1,20 @@ +namespace Moq.Analyzers.Common; + +internal static class WellKnownMoqNames +{ + internal static readonly string MoqNamespace = "Moq"; + + internal static readonly string MoqSymbolName = "Moq"; + internal static readonly string MockTypeName = "Mock"; + internal static readonly string MockBehaviorTypeName = "MockBehavior"; + internal static readonly string MockFactoryTypeName = "MockFactory"; + + internal static readonly string FullyQualifiedMoqMockTypeName = $"{MoqNamespace}.{MockTypeName}"; + internal static readonly string FullyQualifiedMoqMock1TypeName = $"{FullyQualifiedMoqMockTypeName}`1"; + internal static readonly string FullyQualifiedMoqBehaviorTypeName = $"{MoqNamespace}.{MockBehaviorTypeName}"; + internal static readonly string FullyQualifiedMoqRepositoryTypeName = $"{MoqNamespace}.MockRepository"; + + internal static readonly string AsMethodName = "As"; + internal static readonly string CreateMethodName = "Create"; + internal static readonly string OfMethodName = "Of"; +} diff --git a/src/Common/WellKnownTypeNames.cs b/src/Common/WellKnownTypeNames.cs deleted file mode 100644 index ab5d29aa2..000000000 --- a/src/Common/WellKnownTypeNames.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Moq.Analyzers.Common; - -internal static class WellKnownTypeNames -{ - internal static readonly string MoqNamespace = "Moq"; - internal static readonly string MoqSymbolName = "Moq"; - internal static readonly string Mock = nameof(Mock); - internal static readonly string MockBehavior = nameof(MockBehavior); - internal static readonly string MockFactory = nameof(MockFactory); - internal static readonly string MoqMock = $"{MoqNamespace}.{Mock}"; - internal static readonly string MoqMock1 = $"{MoqMock}`1"; - internal static readonly string MoqBehavior = $"{MoqNamespace}.{MockBehavior}"; - internal static readonly string MoqRepository = $"{MoqNamespace}.MockRepository"; - internal static readonly string As = nameof(As); - internal static readonly string Create = nameof(Create); - internal static readonly string Of = nameof(Of); -} From 8b1649850047e9e9980a48479f83d634b3134410 Mon Sep 17 00:00:00 2001 From: Richard Murillo Date: Wed, 23 Oct 2024 13:11:31 -0700 Subject: [PATCH 5/5] Update documentation for well known Moq names type --- src/Common/WellKnownMoqNames.cs | 54 +++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/Common/WellKnownMoqNames.cs b/src/Common/WellKnownMoqNames.cs index 69bb5be96..fdedd9387 100644 --- a/src/Common/WellKnownMoqNames.cs +++ b/src/Common/WellKnownMoqNames.cs @@ -1,20 +1,74 @@ namespace Moq.Analyzers.Common; +/// +/// Provides well-known names and fully qualified names for commonly used Moq types, namespaces, and members. +/// internal static class WellKnownMoqNames { + /// + /// Represents the namespace for the Moq library. + /// internal static readonly string MoqNamespace = "Moq"; + /// + /// Symbol name for Moq. + /// internal static readonly string MoqSymbolName = "Moq"; + + /// + /// The name of the 'Moq.Mock' type. + /// internal static readonly string MockTypeName = "Mock"; + + /// + /// The name of the 'Moq.MockBehavior' type. + /// This type specifies the behavior of the mock (Strict, Loose, etc.). + /// internal static readonly string MockBehaviorTypeName = "MockBehavior"; + + /// + /// The name of the 'Moq.MockFactory' type. + /// This factory is used for creating multiple mock objects with a shared configuration. + /// internal static readonly string MockFactoryTypeName = "MockFactory"; + /// + /// Fully qualified name for the 'Moq.Mock' type. + /// internal static readonly string FullyQualifiedMoqMockTypeName = $"{MoqNamespace}.{MockTypeName}"; + + /// + /// Fully qualified name for the generic version of 'Moq.Mock{T}'. + /// Represents mocks for specific types. + /// internal static readonly string FullyQualifiedMoqMock1TypeName = $"{FullyQualifiedMoqMockTypeName}`1"; + + /// + /// Fully qualified name for the 'Moq.MockBehavior' type. + /// internal static readonly string FullyQualifiedMoqBehaviorTypeName = $"{MoqNamespace}.{MockBehaviorTypeName}"; + + /// + /// Fully qualified name for the 'Moq.MockRepository' type. + /// This type acts as a container for multiple mocks and shared mock configurations. + /// internal static readonly string FullyQualifiedMoqRepositoryTypeName = $"{MoqNamespace}.MockRepository"; + /// + /// Represents the method name for the `As` method in Moq. + /// This method is used to cast mocks to interfaces. + /// internal static readonly string AsMethodName = "As"; + + /// + /// Represents the method name for the `Create` method in Moq. + /// This method is used to create instances of mocks. + /// internal static readonly string CreateMethodName = "Create"; + + /// + /// Represents the method name for the `Of` method in Moq. + /// This method is used to create a mock from a type without directly specifying the constructor. + /// internal static readonly string OfMethodName = "Of"; }