Skip to content

Commit

Permalink
General improvements (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
viceroypenguin committed Mar 22, 2024
1 parent eb50ed5 commit 5b0b5a5
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 70 deletions.
11 changes: 4 additions & 7 deletions src/Immediate.Apis.Analyzers/CustomizeEndpointUsageAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context)

if (!namedTypeSymbol
.GetAttributes()
.Any(x => Utility.ValidAttributes.Contains(x.AttributeClass?.ToString()))
)
.Any(x => x.AttributeClass.IsMapMethodAttribute()))
{
return;
}
Expand All @@ -63,16 +62,14 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context)
return;
}

if (
customizeEndpointMethod is
if (customizeEndpointMethod is
{
DeclaredAccessibility: Accessibility.Internal,
IsStatic: true,
ReturnsVoid: true,
Parameters: [{ } param]
Parameters: [{ Type: { } paramType }],
}
&& param.Type.ToString() == "Microsoft.AspNetCore.Builder.IEndpointConventionBuilder"
)
&& paramType.IsIEndpointConventionBuilder())
{
return;
}
Expand Down
91 changes: 91 additions & 0 deletions src/Immediate.Apis.Analyzers/ITypeSymbolExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using Microsoft.CodeAnalysis;

namespace Immediate.Apis.Analyzers;

internal static class ITypeSymbolExtensions
{
public static bool IsImmediateApisShared(this INamespaceSymbol symbol) =>
symbol is
{
Name: "Shared",
ContainingNamespace:
{
Name: "Apis",
ContainingNamespace:
{
Name: "Immediate",
ContainingNamespace.IsGlobalNamespace: true,
},
},
};

public static bool IsMapMethodAttribute(this ITypeSymbol? typeSymbol, string method) =>
typeSymbol?.Name == $"Map{method}Attribute"
&& typeSymbol.ContainingNamespace.IsImmediateApisShared();

private static readonly string[] s_methods = ["Get", "Post", "Put", "Patch", "Delete"];
public static bool IsMapMethodAttribute(this ITypeSymbol? typeSymbol) =>
s_methods.Any(typeSymbol.IsMapMethodAttribute);

public static bool IsMicrosoftAspNetCoreAuthorization(this INamespaceSymbol symbol) =>
symbol is
{
Name: "Authorization",
ContainingNamespace:
{
Name: "AspNetCore",
ContainingNamespace:
{
Name: "Microsoft",
ContainingNamespace.IsGlobalNamespace: true,
},
},
};

public static bool IsAllowAnonymous(this ITypeSymbol? typeSymbol) =>
typeSymbol?.Name is "AllowAnonymousAttribute"
&& typeSymbol.ContainingNamespace.IsMicrosoftAspNetCoreAuthorization();

public static bool IsAuthorize(this ITypeSymbol? typeSymbol) =>
typeSymbol?.Name is "AuthorizeAttribute"
&& typeSymbol.ContainingNamespace.IsMicrosoftAspNetCoreAuthorization();

public static bool IsHandlerAttribute(this ITypeSymbol? typeSymbol) =>
typeSymbol is
{
Name: "HandlerAttribute",
ContainingNamespace:
{
Name: "Shared",
ContainingNamespace:
{
Name: "Handlers",
ContainingNamespace:
{
Name: "Immediate",
ContainingNamespace.IsGlobalNamespace: true,
},
},
},
};

public static bool IsIEndpointConventionBuilder(this ITypeSymbol? typeSymbol) =>
// IErrorTypeSymbol is until we can reference AspNetCore framework
typeSymbol is IErrorTypeSymbol or
{
Name: "IEndpointConventionBuilder",
ContainingNamespace:
{
Name: "Builder",
ContainingNamespace:
{
Name: "AspNetCore",
ContainingNamespace:
{
Name: "Microsoft",
ContainingNamespace.IsGlobalNamespace: true,
},
},
},
};
}
35 changes: 14 additions & 21 deletions src/Immediate.Apis.Analyzers/InvalidAuthorizeAttributeAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,36 +55,29 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context)
if (context.Symbol is not INamedTypeSymbol namedTypeSymbol)
return;

var attributeNames = namedTypeSymbol
.GetAttributes()
.Select(a => a.AttributeClass?.ToString() ?? "")
.ToList();
var attributes = namedTypeSymbol.GetAttributes();

if (!attributeNames.Any(x => Utility.ValidAttributes.Contains(x)))
{
if (!attributes.Any(a => a.AttributeClass.IsMapMethodAttribute()))
return;
}

token.ThrowIfCancellationRequested();

var allowAnonymous = attributeNames.Contains("Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute");
var allowAnonymous = attributes.Any(a => a.AttributeClass.IsAllowAnonymous());

var authorizeIndex = attributeNames.IndexOf("Microsoft.AspNetCore.Authorization.AuthorizeAttribute");
var authorize = authorizeIndex >= 0;
var authorizeAttribute = attributes.FirstOrDefault(a => a.AttributeClass.IsAuthorize());

if (allowAnonymous && authorize)
if (authorizeAttribute is not null)
{
context.ReportDiagnostic(
Diagnostic.Create(
UsedBothAuthorizeAndAnonymous,
namedTypeSymbol.Locations[0]
)
);
}
if (allowAnonymous)
{
context.ReportDiagnostic(
Diagnostic.Create(
UsedBothAuthorizeAndAnonymous,
namedTypeSymbol.Locations[0]
)
);
}

if (authorize)
{
var authorizeAttribute = namedTypeSymbol.GetAttributes()[authorizeIndex];
if (authorizeAttribute.NamedArguments.Length > 0)
{
foreach (var argument in authorizeAttribute.NamedArguments)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context)

if (!namedTypeSymbol
.GetAttributes()
.Any(x => Utility.ValidAttributes.Contains(x.AttributeClass?.ToString()))
)
.Any(x => x.AttributeClass.IsMapMethodAttribute()))
{
return;
}
Expand All @@ -55,8 +54,7 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context)

if (!namedTypeSymbol
.GetAttributes()
.Any(x => x.AttributeClass?.ToString() == "Immediate.Handlers.Shared.HandlerAttribute")
)
.Any(x => x.AttributeClass.IsHandlerAttribute()))
{
context.ReportDiagnostic(
Diagnostic.Create(
Expand Down
12 changes: 0 additions & 12 deletions src/Immediate.Apis.Analyzers/Utility.cs

This file was deleted.

68 changes: 68 additions & 0 deletions src/Immediate.Apis.Generators/ITypeSymbolExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using Microsoft.CodeAnalysis;

namespace Immediate.Apis.Generators;

internal static class ITypeSymbolExtensions
{
public static bool IsImmediateApisShared(this INamespaceSymbol symbol) =>
symbol is
{
Name: "Shared",
ContainingNamespace:
{
Name: "Apis",
ContainingNamespace:
{
Name: "Immediate",
ContainingNamespace.IsGlobalNamespace: true,
},
},
};

public static bool IsMapMethodAttribute(this ITypeSymbol? typeSymbol, string method) =>
typeSymbol?.Name == $"Map{method}Attribute"
&& typeSymbol.ContainingNamespace.IsImmediateApisShared();

public static bool IsMicrosoftAspNetCoreAuthorization(this INamespaceSymbol symbol) =>
symbol is
{
Name: "Authorization",
ContainingNamespace:
{
Name: "AspNetCore",
ContainingNamespace:
{
Name: "Microsoft",
ContainingNamespace.IsGlobalNamespace: true,
},
},
};

public static bool IsAllowAnonymous(this ITypeSymbol? typeSymbol) =>
typeSymbol?.Name is "AllowAnonymousAttribute"
&& typeSymbol.ContainingNamespace.IsMicrosoftAspNetCoreAuthorization();

public static bool IsAuthorize(this ITypeSymbol? typeSymbol) =>
typeSymbol?.Name is "AuthorizeAttribute"
&& typeSymbol.ContainingNamespace.IsMicrosoftAspNetCoreAuthorization();

public static bool IsIEndpointConventionBuilder(this ITypeSymbol? typeSymbol) =>
// IErrorTypeSymbol is until we can reference AspNetCore framework
typeSymbol is IErrorTypeSymbol or
{
Name: "IEndpointConventionBuilder",
ContainingNamespace:
{
Name: "Builder",
ContainingNamespace:
{
Name: "AspNetCore",
ContainingNamespace:
{
Name: "Microsoft",
ContainingNamespace.IsGlobalNamespace: true,
},
},
},
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,4 @@ private sealed record Method

public required bool UseCustomization { get; init; }
}

private static readonly string[] s_methodAttributes =
[
"Immediate.Apis.Shared.MapGetAttribute",
"Immediate.Apis.Shared.MapPostAttribute",
"Immediate.Apis.Shared.MapPutAttribute",
"Immediate.Apis.Shared.MapPatchAttribute",
"Immediate.Apis.Shared.MapDeleteAttribute",
];
}
31 changes: 14 additions & 17 deletions src/Immediate.Apis.Generators/ImmediateApisGenerator.Transform.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;

namespace Immediate.Apis.Generators;
Expand All @@ -13,37 +14,32 @@ CancellationToken token

var symbol = (INamedTypeSymbol)context.TargetSymbol;
var attributes = symbol.GetAttributes();
var attributeNames = attributes
.Select(a => a.AttributeClass?.ToString() ?? "")
.ToList();

token.ThrowIfCancellationRequested();

if (GetMethodAttributeIndex(attributeNames) is not { } methodIndex)
if (GetMethodAttribute(attributes) is not { } attribute)
return null;

if (GetValidHandleMethod(symbol) is not { } handleMethod)
return null;

token.ThrowIfCancellationRequested();

var attribute = attributes[methodIndex];
var httpMethod = attribute.AttributeClass!.Name[..^9];

if (attribute.ConstructorArguments.FirstOrDefault().Value is not string route)
return null;

token.ThrowIfCancellationRequested();

var allowAnonymous = attributeNames.Contains("Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute");
var allowAnonymous = attributes.Any(a => a.AttributeClass.IsAllowAnonymous());

var authorizeIndex = attributeNames.IndexOf("Microsoft.AspNetCore.Authorization.AuthorizeAttribute");
var authorize = authorizeIndex >= 0;
var authorizeAttribute = attributes.FirstOrDefault(a => a.AttributeClass.IsAuthorize());
var authorize = authorizeAttribute != null;
var authorizePolicy = string.Empty;

if (authorize)
if (authorizeAttribute != null)
{
var authorizeAttribute = attributes[authorizeIndex];
if (authorizeAttribute.ConstructorArguments.Length > 0)
{
authorizePolicy = (string)authorizeAttribute.ConstructorArguments[0].Value!;
Expand Down Expand Up @@ -93,13 +89,14 @@ CancellationToken token
};
}

private static int? GetMethodAttributeIndex(List<string> attributeNames)
private static readonly string[] s_methods = ["Get", "Post", "Put", "Patch", "Delete"];
private static AttributeData? GetMethodAttribute(ImmutableArray<AttributeData> attributes)
{
foreach (var name in s_methodAttributes)
foreach (var name in s_methods)
{
var index = attributeNames.IndexOf(name);
if (index >= 0)
return index;
var attribute = attributes.FirstOrDefault(a => a.AttributeClass.IsMapMethodAttribute(name));
if (attribute != null)
return attribute;
}

return null;
Expand Down Expand Up @@ -135,8 +132,8 @@ m is
IsStatic: true,
DeclaredAccessibility: Accessibility.Internal,
ReturnsVoid: true,
Parameters: [{ } param]
Parameters: [{ Type: { } paramType }],
}
&& param.Type.ToString() == "Microsoft.AspNetCore.Builder.IEndpointConventionBuilder"
&& paramType.IsIEndpointConventionBuilder()
);
}

0 comments on commit 5b0b5a5

Please sign in to comment.