-
Notifications
You must be signed in to change notification settings - Fork 463
/
DisposeMethodsShouldCallBaseClassDispose.cs
117 lines (103 loc) · 5.8 KB
/
DisposeMethodsShouldCallBaseClassDispose.cs
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.
using System.Collections.Immutable;
using System.Diagnostics;
using Analyzer.Utilities;
using Analyzer.Utilities.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
namespace Microsoft.NetCore.Analyzers.Runtime
{
using static MicrosoftNetCoreAnalyzersResources;
/// <summary>
/// CA2215: <inheritdoc cref="DisposeMethodsShouldCallBaseClassDisposeTitle"/>
///
/// A type that implements System.IDisposable inherits from a type that also implements IDisposable.
/// The Dispose method of the inheriting type does not call the Dispose method of the parent type.
/// To fix a violation of this rule, call base.Dispose in your Dispose method.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public sealed class DisposeMethodsShouldCallBaseClassDispose : DiagnosticAnalyzer
{
internal const string RuleId = "CA2215";
internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create(
RuleId,
CreateLocalizableResourceString(nameof(DisposeMethodsShouldCallBaseClassDisposeTitle)),
CreateLocalizableResourceString(nameof(DisposeMethodsShouldCallBaseClassDisposeMessage)),
DiagnosticCategory.Usage,
RuleLevel.IdeHidden_BulkConfigurable,
description: CreateLocalizableResourceString(nameof(DisposeMethodsShouldCallBaseClassDisposeDescription)),
isPortedFxCopRule: true,
isDataflowRule: false);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(Rule);
public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.RegisterCompilationStartAction(compilationContext =>
{
if (!DisposeAnalysisHelper.TryGetOrCreate(compilationContext.Compilation, out DisposeAnalysisHelper? disposeAnalysisHelper))
{
return;
}
compilationContext.RegisterOperationBlockStartAction(operationBlockStartContext =>
{
if (operationBlockStartContext.OwningSymbol is not IMethodSymbol containingMethod ||
containingMethod.OverriddenMethod == null ||
containingMethod.OverriddenMethod.IsAbstract)
{
return;
}
var disposeMethodKind = disposeAnalysisHelper.GetDisposeMethodKind(containingMethod);
switch (disposeMethodKind)
{
case DisposeMethodKind.Dispose:
case DisposeMethodKind.DisposeBool:
case DisposeMethodKind.DisposeAsync:
case DisposeMethodKind.DisposeCoreAsync:
break;
case DisposeMethodKind.Close:
// FxCop compat: Ignore Close methods due to high false positive rate.
return;
default:
return;
}
var invokesBaseDispose = false;
operationBlockStartContext.RegisterOperationAction(operationContext =>
{
if (invokesBaseDispose)
{
return;
}
var invocation = (IInvocationOperation)operationContext.Operation;
if (Equals(invocation.TargetMethod, containingMethod.OverriddenMethod) &&
invocation.Instance is IInstanceReferenceOperation instanceReference &&
instanceReference.ReferenceKind == InstanceReferenceKind.ContainingTypeInstance)
{
Debug.Assert(disposeAnalysisHelper.GetDisposeMethodKind(invocation.TargetMethod) == disposeMethodKind);
invokesBaseDispose = true;
}
}, OperationKind.Invocation);
operationBlockStartContext.RegisterOperationBlockEndAction(operationEndContext =>
{
if (!invokesBaseDispose)
{
// Ensure that method '{0}' calls '{1}' in all possible control flow paths.
var arg1 = containingMethod.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
var baseKeyword = containingMethod.Language == LanguageNames.CSharp ? "base" : "MyBase";
var disposeMethodParam = (disposeMethodKind is DisposeMethodKind.DisposeBool or DisposeMethodKind.DisposeCoreAsync) ?
containingMethod.Language == LanguageNames.CSharp ? "bool" : "Boolean" :
string.Empty;
var disposeMethodName = disposeMethodKind == DisposeMethodKind.DisposeBool ?
"Dispose" :
disposeMethodKind.ToString();
var arg2 = $"{baseKeyword}.{disposeMethodName}({disposeMethodParam})";
var diagnostic = containingMethod.CreateDiagnostic(Rule, arg1, arg2);
operationEndContext.ReportDiagnostic(diagnostic);
}
});
});
});
}
}
}