Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/Sentry.Compiler.Extensions/AnalyzerReleases.Shipped.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
; Shipped analyzer releases
; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
; Unshipped analyzer release
; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md

### New Rules

Rule ID | Category | Severity | Notes
--------|----------|----------|-------
SENTRY1001 | Support | Warning | TraceConnectedMetricsAnalyzer
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
using System;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;

namespace Sentry.Compiler.Extensions.Analyzers;

/// <summary>
/// Guide consumers 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 static readonly string Title = "Unsupported numeric type of Metric";
private static readonly string MessageFormat = "{0} is unsupported type for Sentry Metrics. The only supported types are byte, short, int, long, float, and double.";
private static readonly 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.Support,
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.IsAbstract || method.IsVirtual || method.IsStatic || !method.ReturnsVoid || 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.SentryTraceMetrics";
}
else if (method.Name is "SetBeforeSendMetric")
{
fullyQualifiedMetadataName = "Sentry.SentryOptions+ExperimentalSentryOptions";
}
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);
}
}
6 changes: 6 additions & 0 deletions src/Sentry.Compiler.Extensions/DiagnosticCategories.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Sentry.Compiler.Extensions;

internal static class DiagnosticCategories
{
internal const string Support = nameof(Support);
}
6 changes: 6 additions & 0 deletions src/Sentry.Compiler.Extensions/DiagnosticIds.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Sentry.Compiler.Extensions;

internal static class DiagnosticIds
{
internal const string Sentry1001 = "SENTRY1001";
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,25 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.1" PrivateAssets="all"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.1" PrivateAssets="all" />
</ItemGroup>

<!--
We use Simon Cropp's Polyfill source-only package to access APIs in lower targets.
https://github.com/SimonCropp/Polyfill
-->
<ItemGroup>
<PackageReference Include="Polyfill" Version="1.32.0" PrivateAssets="all" />
</ItemGroup>

<ItemGroup>
<Using Remove="System.Text.Json" />
<Using Remove="System.Text.Json.Serialization" />
</ItemGroup>

<ItemGroup>
<AdditionalFiles Remove="AnalyzerReleases.Shipped.md" />
<AdditionalFiles Remove="AnalyzerReleases.Unshipped.md" />
</ItemGroup>

</Project>
12 changes: 12 additions & 0 deletions src/Sentry.Compiler.Extensions/SymbolDisplayFormats.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Microsoft.CodeAnalysis;

namespace Sentry.Compiler.Extensions;

internal static class SymbolDisplayFormats
{
internal static SymbolDisplayFormat FullNameFormat { get; } = new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters
);
}
Loading
Loading