-
-
Notifications
You must be signed in to change notification settings - Fork 230
Expand file tree
/
Copy pathTraceConnectedMetricsAnalyzer.cs
More file actions
113 lines (94 loc) · 3.88 KB
/
TraceConnectedMetricsAnalyzer.cs
File metadata and controls
113 lines (94 loc) · 3.88 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
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
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
namespace Sentry.Compiler.Extensions.Analyzers;
/// <summary>
/// Guide callers to use the public API of <see href="https://develop.sentry.dev/sdk/telemetry/metrics/">Sentry Trace-connected Metrics</see> correctly.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class TraceConnectedMetricsAnalyzer : DiagnosticAnalyzer
{
private const string Title = "Unsupported numeric type of Metric";
private const string MessageFormat = "{0} is unsupported type for Sentry Metrics. The only supported types are byte, short, int, long, float, and double.";
private const string Description = "Integers should be a 64-bit signed integer, while doubles should be a 64-bit floating point number.";
private static readonly DiagnosticDescriptor Rule = new(
id: DiagnosticIds.Sentry1001,
title: Title,
messageFormat: MessageFormat,
category: DiagnosticCategories.Sentry,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: Description,
helpLinkUri: null
);
/// <inheritdoc />
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(Rule);
/// <inheritdoc />
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterOperationAction(Execute, OperationKind.Invocation);
}
private static void Execute(OperationAnalysisContext context)
{
Debug.Assert(context.Operation.Language == LanguageNames.CSharp);
Debug.Assert(context.Operation.Kind is OperationKind.Invocation);
context.CancellationToken.ThrowIfCancellationRequested();
if (context.Operation is not IInvocationOperation invocation)
{
return;
}
var method = invocation.TargetMethod;
if (method.DeclaredAccessibility != Accessibility.Public || method.IsStatic || method.Parameters.Length == 0)
{
return;
}
if (!method.IsGenericMethod || method.Arity != 1 || method.TypeArguments.Length != 1)
{
return;
}
if (method.ContainingAssembly is null || method.ContainingAssembly.Name != "Sentry")
{
return;
}
if (method.ContainingNamespace is null || method.ContainingNamespace.Name != "Sentry")
{
return;
}
string fullyQualifiedMetadataName;
if (method.Name is "EmitCounter" or "EmitGauge" or "EmitDistribution")
{
fullyQualifiedMetadataName = "Sentry.SentryMetricEmitter";
}
else if (method.Name is "TryGetValue")
{
fullyQualifiedMetadataName = "Sentry.SentryMetric";
}
else
{
return;
}
var typeArgument = method.TypeArguments[0];
if (typeArgument.SpecialType is SpecialType.System_Byte or SpecialType.System_Int16 or SpecialType.System_Int32 or SpecialType.System_Int64 or SpecialType.System_Single or SpecialType.System_Double)
{
return;
}
if (typeArgument is ITypeParameterSymbol)
{
return;
}
var sentryType = context.Compilation.GetTypeByMetadataName(fullyQualifiedMetadataName);
if (sentryType is null)
{
return;
}
if (!SymbolEqualityComparer.Default.Equals(method.ContainingType, sentryType))
{
return;
}
var location = invocation.Syntax.GetLocation();
var diagnostic = Diagnostic.Create(Rule, location, typeArgument.ToDisplayString(SymbolDisplayFormats.FullNameFormat));
context.ReportDiagnostic(diagnostic);
}
}