Skip to content

Commit b5ea5d3

Browse files
authored
Remove ECS0200 suppression and update WellKnownTypes to not trigger ECS0600 (#236)
Effective C# rules 0200 and 0600 have violations in `src/Common/WellKnownTypeNames.cs`. This change updates the members in `WellKnownTypes` type and their usages Related to rjmurillo/EffectiveCSharp.Analyzers#73, see item for discussion - Updated constant declarations to static read-only fields for improved flexibility as referenced in [ECS0200](https://github.com/rjmurillo/EffectiveCSharp.Analyzers/blob/main/docs/rules/ECS0200.md) and removed suppression. - Updated naming conventions for `WellKnownTypes` to avoid triggering [ECS0600](https://github.com/rjmurillo/EffectiveCSharp.Analyzers/blob/main/docs/rules/ECS0600.md) - Updated control flow in `src/Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs` to move from switch, which requires compile time constants, to a more traditional if/else - Updated names in the former `WellKnownTypeNames` type (now `WellKnownMoqNames`) with improved names and added documentation
1 parent bbb213d commit b5ea5d3

7 files changed

Lines changed: 96 additions & 40 deletions

src/Analyzers/AsShouldBeUsedOnlyForInterfaceAnalyzer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ private static void RegisterCompilationStartAction(CompilationStartAnalysisConte
4444
// Look for the Mock.As() method and provide it to Analyze to avoid looking it up multiple times.
4545
#pragma warning disable ECS0900 // Minimize boxing and unboxing
4646
ImmutableArray<IMethodSymbol> asMethods = mockTypes
47-
.SelectMany(mockType => mockType.GetMembers(WellKnownTypeNames.As))
47+
.SelectMany(mockType => mockType.GetMembers(WellKnownMoqNames.AsMethodName))
4848
.OfType<IMethodSymbol>()
4949
.Where(method => method.IsGenericMethod)
5050
.ToImmutableArray();

src/Analyzers/ConstructorArgumentsShouldMatchAnalyzer.cs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ private static bool IsExpressionMockBehavior(SyntaxNodeAnalysisContext context,
7878
if (expression is MemberAccessExpressionSyntax memberAccessExpressionSyntax)
7979
{
8080
if (memberAccessExpressionSyntax.Expression is IdentifierNameSyntax identifierNameSyntax
81-
&& string.Equals(identifierNameSyntax.Identifier.ValueText, WellKnownTypeNames.MockBehavior, StringComparison.Ordinal))
81+
&& string.Equals(identifierNameSyntax.Identifier.ValueText, WellKnownMoqNames.MockBehaviorTypeName, StringComparison.Ordinal))
8282
{
8383
return true;
8484
}
@@ -107,14 +107,14 @@ private static bool IsExpressionMockBehavior(SyntaxNodeAnalysisContext context,
107107
}
108108

109109
if (typeSymbol != null
110-
&& string.Equals(typeSymbol.Name, WellKnownTypeNames.MockBehavior, StringComparison.Ordinal))
110+
&& string.Equals(typeSymbol.Name, WellKnownMoqNames.MockBehaviorTypeName, StringComparison.Ordinal))
111111
{
112112
return true;
113113
}
114114
}
115115

116116
// Crude fallback to check if the expression is a Moq.MockBehavior enum
117-
if (expression.ToString().StartsWith(WellKnownTypeNames.MoqBehavior, StringComparison.Ordinal))
117+
if (expression.ToString().StartsWith(WellKnownMoqNames.FullyQualifiedMoqBehaviorTypeName, StringComparison.Ordinal))
118118
{
119119
return true;
120120
}
@@ -205,18 +205,18 @@ private static void AnalyzeInstanceCall(SyntaxNodeAnalysisContext context)
205205
return;
206206
}
207207

208-
switch (genericNameSyntax.Identifier.Value)
208+
if (genericNameSyntax.Identifier.Value is not string genericNameSyntaxIdentifierValue)
209209
{
210-
case WellKnownTypeNames.Create:
211-
AnalyzeInvocation(context, invocationExpressionSyntax, WellKnownTypeNames.MockFactory, true, true);
212-
break;
213-
214-
case WellKnownTypeNames.Of:
215-
AnalyzeInvocation(context, invocationExpressionSyntax, WellKnownTypeNames.MockName, false, true);
216-
break;
210+
return;
211+
}
217212

218-
default:
219-
return;
213+
if (string.Equals(genericNameSyntaxIdentifierValue, WellKnownMoqNames.CreateMethodName, StringComparison.Ordinal))
214+
{
215+
AnalyzeInvocation(context, invocationExpressionSyntax, WellKnownMoqNames.MockFactoryTypeName, true, true);
216+
}
217+
else if (string.Equals(genericNameSyntaxIdentifierValue, WellKnownMoqNames.OfMethodName, StringComparison.Ordinal))
218+
{
219+
AnalyzeInvocation(context, invocationExpressionSyntax, WellKnownMoqNames.MockTypeName, false, true);
220220
}
221221
}
222222

@@ -252,7 +252,7 @@ private static void AnalyzeInvocation(
252252

253253
// We are calling MockRepository.Create<T> or Mock.Of<T>, determine which
254254
ArgumentListSyntax? argumentList = null;
255-
if (WellKnownTypeNames.Of.Equals(method.Name, StringComparison.Ordinal))
255+
if (WellKnownMoqNames.OfMethodName.Equals(method.Name, StringComparison.Ordinal))
256256
{
257257
// Mock.Of<T> can specify condition for construction and MockBehavior, but
258258
// cannot specify constructor parameters
@@ -286,7 +286,7 @@ private static void AnalyzeNewObject(SyntaxNodeAnalysisContext context)
286286
// Quick check
287287
if (!string.Equals(
288288
genericNameSyntax.Identifier.ValueText,
289-
WellKnownTypeNames.MockName,
289+
WellKnownMoqNames.MockTypeName,
290290
StringComparison.Ordinal))
291291
{
292292
return;
@@ -297,7 +297,7 @@ private static void AnalyzeNewObject(SyntaxNodeAnalysisContext context)
297297

298298
if (symbolInfo.Symbol is not IMethodSymbol mockConstructorMethod
299299
|| mockConstructorMethod.MethodKind != MethodKind.Constructor
300-
|| !string.Equals(mockConstructorMethod.ContainingType.ConstructedFrom.ContainingSymbol.Name, WellKnownTypeNames.Moq, StringComparison.Ordinal))
300+
|| !string.Equals(mockConstructorMethod.ContainingType.ConstructedFrom.ContainingSymbol.Name, WellKnownMoqNames.MoqSymbolName, StringComparison.Ordinal))
301301
{
302302
return;
303303
}

src/Analyzers/SetExplicitMockBehaviorAnalyzer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ private static void RegisterCompilationStartAction(CompilationStartAnalysisConte
4242
}
4343

4444
// Look for the MockBehavior type and provide it to Analyze to avoid looking it up multiple times.
45-
INamedTypeSymbol? mockBehaviorSymbol = context.Compilation.GetTypeByMetadataName(WellKnownTypeNames.MoqBehavior);
45+
INamedTypeSymbol? mockBehaviorSymbol = context.Compilation.GetTypeByMetadataName(WellKnownMoqNames.FullyQualifiedMoqBehaviorTypeName);
4646
if (mockBehaviorSymbol is null)
4747
{
4848
return;
@@ -51,7 +51,7 @@ private static void RegisterCompilationStartAction(CompilationStartAnalysisConte
5151
// Look for the Mock.Of() method and provide it to Analyze to avoid looking it up multiple times.
5252
#pragma warning disable ECS0900 // Minimize boxing and unboxing
5353
ImmutableArray<IMethodSymbol> ofMethods = mockTypes
54-
.SelectMany(mockType => mockType.GetMembers(WellKnownTypeNames.Of))
54+
.SelectMany(mockType => mockType.GetMembers(WellKnownMoqNames.OfMethodName))
5555
.OfType<IMethodSymbol>()
5656
.Where(method => method.IsGenericMethod)
5757
.ToImmutableArray();

src/Common/CompilationExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,6 @@ public static ImmutableArray<INamedTypeSymbol> GetTypesByMetadataNames(this Comp
3434
/// </returns>
3535
public static ImmutableArray<INamedTypeSymbol> GetMoqMock(this Compilation compilation)
3636
{
37-
return compilation.GetTypesByMetadataNames([WellKnownTypeNames.MoqMock, WellKnownTypeNames.MoqMock1, WellKnownTypeNames.MoqRepository]);
37+
return compilation.GetTypesByMetadataNames([WellKnownMoqNames.FullyQualifiedMoqMockTypeName, WellKnownMoqNames.FullyQualifiedMoqMock1TypeName, WellKnownMoqNames.FullyQualifiedMoqRepositoryTypeName]);
3838
}
3939
}

src/Common/MoqMethodDescriptorBase.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
/// </remarks>
1212
internal abstract class MoqMethodDescriptorBase
1313
{
14-
private static readonly string ContainingNamespace = WellKnownTypeNames.Moq;
15-
private static readonly string ContainingType = WellKnownTypeNames.MockName;
14+
private static readonly string ContainingNamespace = WellKnownMoqNames.MoqNamespace;
15+
private static readonly string ContainingType = WellKnownMoqNames.MockTypeName;
1616

1717
public abstract bool IsMatch(SemanticModel semanticModel, MemberAccessExpressionSyntax memberAccessSyntax, CancellationToken cancellationToken);
1818

src/Common/WellKnownMoqNames.cs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
namespace Moq.Analyzers.Common;
2+
3+
/// <summary>
4+
/// Provides well-known names and fully qualified names for commonly used Moq types, namespaces, and members.
5+
/// </summary>
6+
internal static class WellKnownMoqNames
7+
{
8+
/// <summary>
9+
/// Represents the namespace for the Moq library.
10+
/// </summary>
11+
internal static readonly string MoqNamespace = "Moq";
12+
13+
/// <summary>
14+
/// Symbol name for Moq.
15+
/// </summary>
16+
internal static readonly string MoqSymbolName = "Moq";
17+
18+
/// <summary>
19+
/// The name of the 'Moq.Mock' type.
20+
/// </summary>
21+
internal static readonly string MockTypeName = "Mock";
22+
23+
/// <summary>
24+
/// The name of the 'Moq.MockBehavior' type.
25+
/// This type specifies the behavior of the mock (Strict, Loose, etc.).
26+
/// </summary>
27+
internal static readonly string MockBehaviorTypeName = "MockBehavior";
28+
29+
/// <summary>
30+
/// The name of the 'Moq.MockFactory' type.
31+
/// This factory is used for creating multiple mock objects with a shared configuration.
32+
/// </summary>
33+
internal static readonly string MockFactoryTypeName = "MockFactory";
34+
35+
/// <summary>
36+
/// Fully qualified name for the 'Moq.Mock' type.
37+
/// </summary>
38+
internal static readonly string FullyQualifiedMoqMockTypeName = $"{MoqNamespace}.{MockTypeName}";
39+
40+
/// <summary>
41+
/// Fully qualified name for the generic version of 'Moq.Mock{T}'.
42+
/// Represents mocks for specific types.
43+
/// </summary>
44+
internal static readonly string FullyQualifiedMoqMock1TypeName = $"{FullyQualifiedMoqMockTypeName}`1";
45+
46+
/// <summary>
47+
/// Fully qualified name for the 'Moq.MockBehavior' type.
48+
/// </summary>
49+
internal static readonly string FullyQualifiedMoqBehaviorTypeName = $"{MoqNamespace}.{MockBehaviorTypeName}";
50+
51+
/// <summary>
52+
/// Fully qualified name for the 'Moq.MockRepository' type.
53+
/// This type acts as a container for multiple mocks and shared mock configurations.
54+
/// </summary>
55+
internal static readonly string FullyQualifiedMoqRepositoryTypeName = $"{MoqNamespace}.MockRepository";
56+
57+
/// <summary>
58+
/// Represents the method name for the `As` method in Moq.
59+
/// This method is used to cast mocks to interfaces.
60+
/// </summary>
61+
internal static readonly string AsMethodName = "As";
62+
63+
/// <summary>
64+
/// Represents the method name for the `Create` method in Moq.
65+
/// This method is used to create instances of mocks.
66+
/// </summary>
67+
internal static readonly string CreateMethodName = "Create";
68+
69+
/// <summary>
70+
/// Represents the method name for the `Of` method in Moq.
71+
/// This method is used to create a mock from a type without directly specifying the constructor.
72+
/// </summary>
73+
internal static readonly string OfMethodName = "Of";
74+
}

src/Common/WellKnownTypeNames.cs

Lines changed: 0 additions & 18 deletions
This file was deleted.

0 commit comments

Comments
 (0)