-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathSetExplicitMockBehaviorAnalyzer.cs
More file actions
59 lines (51 loc) · 2.82 KB
/
Copy pathSetExplicitMockBehaviorAnalyzer.cs
File metadata and controls
59 lines (51 loc) · 2.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
using Microsoft.CodeAnalysis.Operations;
namespace Moq.Analyzers;
/// <summary>
/// Mock should explicitly specify a behavior and not rely on the default.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class SetExplicitMockBehaviorAnalyzer : MockBehaviorDiagnosticAnalyzerBase
{
private static readonly LocalizableString Title = "Moq: Explicitly choose a mock behavior";
private static readonly LocalizableString Message = "Explicitly choose a mocking behavior for {0} instead of relying on the default (Loose) behavior";
private static readonly LocalizableString Description = "Explicitly choose a mocking behavior instead of relying on the default (Loose) behavior.";
private static readonly DiagnosticDescriptor Rule = new(
DiagnosticIds.SetExplicitMockBehavior,
Title,
Message,
DiagnosticCategory.BestPractice,
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: Description,
helpLinkUri: $"https://github.com/rjmurillo/moq.analyzers/blob/{ThisAssembly.GitCommitId}/docs/rules/{DiagnosticIds.SetExplicitMockBehavior}.md");
/// <inheritdoc />
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(Rule);
/// <inheritdoc />
private protected override DiagnosticDescriptor DiagnosticRule => Rule;
/// <inheritdoc />
private protected override void AnalyzeMockBehaviorArgument(
OperationAnalysisContext context,
IMethodSymbol target,
IParameterSymbol mockParameter,
IArgumentOperation? mockArgument,
MoqKnownSymbols knownSymbols)
{
System.Diagnostics.Debug.Assert(knownSymbols.MockBehavior is not null, "Base registration requires the MockBehavior symbol.");
// Is the behavior set via a default value?
if (mockArgument?.ArgumentKind == ArgumentKind.DefaultValue
&& MockBehaviorConstantValues.ConstantValueEquals(mockArgument.Value.WalkDownConversion().ConstantValue, knownSymbols.MockBehaviorDefault))
{
TryReportMockBehaviorDiagnostic(context, mockParameter, Rule, DiagnosticEditProperties.EditType.Insert, target);
return;
}
// NOTE: This logic can't handle indirection (e.g. var x = MockBehavior.Default; new Mock(x);). We can't use the constant value either,
// as Loose and Default share the same enum value: `1`. Being more accurate I believe requires data flow analysis.
//
// The operation specifies a MockBehavior; is it MockBehavior.Default?
if (mockArgument is not null
&& ContainsFieldReference(mockArgument, knownSymbols.MockBehaviorDefault))
{
TryReportMockBehaviorDiagnostic(context, mockParameter, Rule, DiagnosticEditProperties.EditType.Replace, target);
}
}
}