Skip to content

Commit d171555

Browse files
committed
add diagnostic for missing lambdafunction attribute
1 parent 7c977a4 commit d171555

7 files changed

Lines changed: 111 additions & 5 deletions

File tree

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/AnalyzerReleases.Unshipped.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ AWSLambda0124 | AWSLambdaCSharpGenerator | Error | Authorizer Type Mismatch
1313
AWSLambda0125 | AWSLambdaCSharpGenerator | Error | Duplicate Authorizer Name
1414
AWSLambda0127 | AWSLambdaCSharpGenerator | Error | Invalid Result TTL
1515
AWSLambda0128 | AWSLambdaCSharpGenerator | Warning | Authorizer Payload Version Mismatch
16+
AWSLambda0129 | AWSLambdaCSharpGenerator | Error | Missing LambdaFunction Attribute

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/DiagnosticDescriptors.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,5 +218,13 @@ public static class DiagnosticDescriptors
218218
category: "AWSLambdaCSharpGenerator",
219219
DiagnosticSeverity.Warning,
220220
isEnabledByDefault: true);
221+
222+
public static readonly DiagnosticDescriptor MissingLambdaFunctionAttribute = new DiagnosticDescriptor(
223+
id: "AWSLambda0129",
224+
title: "Missing LambdaFunction Attribute",
225+
messageFormat: "Method has [{0}] attribute but is missing the required [LambdaFunction] attribute. Add [LambdaFunction] to this method.",
226+
category: "AWSLambdaCSharpGenerator",
227+
DiagnosticSeverity.Error,
228+
isEnabledByDefault: true);
221229
}
222230
}

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Generator.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,17 @@ public void Execute(GeneratorExecutionContext context)
151151
configureMethodSymbol = configureHostBuilderMethodSymbol;
152152
}
153153

154+
// Check for methods that have secondary attributes (HttpApi, RestApi, HttpApiAuthorizer, etc.)
155+
// but are missing the required [LambdaFunction] attribute
156+
foreach (var (method, attributeName) in receiver.MethodsWithMissingLambdaFunction)
157+
{
158+
diagnosticReporter.Report(Diagnostic.Create(
159+
DiagnosticDescriptors.MissingLambdaFunctionAttribute,
160+
Location.Create(method.SyntaxTree, method.Span),
161+
attributeName));
162+
foundFatalError = true;
163+
}
164+
154165
var annotationReport = new AnnotationReport();
155166

156167
var templateHandler = new CloudFormationTemplateHandler(_fileManager, _directoryManager);

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/SyntaxReceiver.cs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,31 @@ namespace Amazon.Lambda.Annotations.SourceGenerator
1111

1212
internal class SyntaxReceiver : ISyntaxContextReceiver
1313
{
14+
/// <summary>
15+
/// Secondary attribute names that require the [LambdaFunction] attribute to also be present.
16+
/// The key is the attribute class name, the value is the user-friendly name for diagnostics.
17+
/// </summary>
18+
private static readonly Dictionary<string, string> _secondaryAttributeNames = new Dictionary<string, string>
19+
{
20+
{ "HttpApiAuthorizerAttribute", "HttpApiAuthorizer" },
21+
{ "RestApiAuthorizerAttribute", "RestApiAuthorizer" },
22+
{ "HttpApiAttribute", "HttpApi" },
23+
{ "RestApiAttribute", "RestApi" },
24+
{ "SQSEventAttribute", "SQSEvent" }
25+
};
26+
1427
public List<MethodDeclarationSyntax> LambdaMethods { get; } = new List<MethodDeclarationSyntax>();
1528

1629
public List<ClassDeclarationSyntax> StartupClasses { get; private set; } = new List<ClassDeclarationSyntax>();
1730

1831
public List<MethodDeclarationSyntax> MethodDeclarations { get; } = new List<MethodDeclarationSyntax>();
1932

33+
/// <summary>
34+
/// Methods that have a secondary Lambda annotation attribute but are missing [LambdaFunction].
35+
/// Each entry is a tuple of the method syntax, its location, and the friendly name of the secondary attribute found.
36+
/// </summary>
37+
public List<(MethodDeclarationSyntax Method, string AttributeName)> MethodsWithMissingLambdaFunction { get; } = new List<(MethodDeclarationSyntax, string)>();
38+
2039
/// <summary>
2140
/// Path to the directory containing the .csproj file
2241
/// </summary>
@@ -57,10 +76,24 @@ public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
5776
var methodSymbol = ModelExtensions.GetDeclaredSymbol(
5877
context.SemanticModel,
5978
methodDeclarationSyntax);
60-
if (methodSymbol.GetAttributes().Any(attr => attr.AttributeClass.Name == nameof(LambdaFunctionAttribute)))
79+
var attributes = methodSymbol.GetAttributes();
80+
if (attributes.Any(attr => attr.AttributeClass.Name == nameof(LambdaFunctionAttribute)))
6181
{
6282
LambdaMethods.Add(methodDeclarationSyntax);
6383
}
84+
else
85+
{
86+
// Check if the method has a secondary attribute without [LambdaFunction]
87+
foreach (var attr in attributes)
88+
{
89+
var attrName = attr.AttributeClass?.Name;
90+
if (attrName != null && _secondaryAttributeNames.TryGetValue(attrName, out var friendlyName))
91+
{
92+
MethodsWithMissingLambdaFunction.Add((methodDeclarationSyntax, friendlyName));
93+
break; // Only report once per method
94+
}
95+
}
96+
}
6497
}
6598

6699
// any class with at least one attribute is a candidate of Startup class

Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/SourceGeneratorTests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1756,6 +1756,36 @@ public async Task CustomAuthorizerAppAuthorizerDefinitionsTest()
17561756
Assert.Equal(expectedTemplateContent, actualTemplateContent);
17571757
}
17581758

1759+
[Fact]
1760+
public async Task VerifyMissingLambdaFunctionWithAuthorizerAttribute()
1761+
{
1762+
var test = new VerifyCS.Test()
1763+
{
1764+
TestState =
1765+
{
1766+
Sources =
1767+
{
1768+
(Path.Combine("TestServerlessApp", "FromScratch", "MissingLambdaFunctionWithAuthorizer.cs"), await File.ReadAllTextAsync(Path.Combine("TestServerlessApp", "FromScratch", "MissingLambdaFunctionWithAuthorizer.cs"))),
1769+
(Path.Combine("Amazon.Lambda.Annotations", "LambdaFunctionAttribute.cs"), await File.ReadAllTextAsync(Path.Combine("Amazon.Lambda.Annotations", "LambdaFunctionAttribute.cs"))),
1770+
(Path.Combine("Amazon.Lambda.Annotations", "LambdaStartupAttribute.cs"), await File.ReadAllTextAsync(Path.Combine("Amazon.Lambda.Annotations", "LambdaStartupAttribute.cs"))),
1771+
(Path.Combine("Amazon.Lambda.Annotations", "APIGateway", "HttpApiAuthorizerAttribute.cs"), await File.ReadAllTextAsync(Path.Combine("Amazon.Lambda.Annotations", "APIGateway", "HttpApiAuthorizerAttribute.cs"))),
1772+
(Path.Combine("Amazon.Lambda.Annotations", "APIGateway", "AuthorizerPayloadFormatVersion.cs"), await File.ReadAllTextAsync(Path.Combine("Amazon.Lambda.Annotations", "APIGateway", "AuthorizerPayloadFormatVersion.cs"))),
1773+
},
1774+
GeneratedSources =
1775+
{
1776+
},
1777+
ExpectedDiagnostics =
1778+
{
1779+
new DiagnosticResult("AWSLambda0129", DiagnosticSeverity.Error)
1780+
.WithSpan($"TestServerlessApp{Path.DirectorySeparatorChar}FromScratch{Path.DirectorySeparatorChar}MissingLambdaFunctionWithAuthorizer.cs", 10, 9, 21, 10)
1781+
.WithMessage("Method has [HttpApiAuthorizer] attribute but is missing the required [LambdaFunction] attribute. Add [LambdaFunction] to this method."),
1782+
},
1783+
},
1784+
};
1785+
1786+
await test.RunAsync();
1787+
}
1788+
17591789
public void Dispose()
17601790
{
17611791
File.Delete(Path.Combine("TestServerlessApp", "serverless.template"));

Libraries/test/TestCustomAuthorizerApp/aws-lambda-tools-defaults.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
"configuration": "Release",
99
"template": "serverless.template",
1010
"template-parameters": "",
11-
"s3-bucket" : "test-custom-authorizer-112e1ec1",
11+
"s3-bucket": "test-custom-authorizer-app",
1212
"s3-prefix": "TestCustomAuthorizerApp/",
13-
"stack-name" : "test-custom-authorizer-112e1ec1",
14-
"function-architecture" : "x86_64"
15-
}
13+
"stack-name": "test-custom-authorizer",
14+
"function-architecture": "x86_64"
15+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using Amazon.Lambda.Annotations;
2+
using Amazon.Lambda.Annotations.APIGateway;
3+
using Amazon.Lambda.APIGatewayEvents;
4+
using Amazon.Lambda.Core;
5+
6+
namespace TestServerlessApp.FromScratch
7+
{
8+
public class MissingLambdaFunctionWithAuthorizer
9+
{
10+
[HttpApiAuthorizer(
11+
Name = "MyAuthorizer",
12+
AuthorizerPayloadFormatVersion = AuthorizerPayloadFormatVersion.V2)]
13+
public APIGatewayCustomAuthorizerV2SimpleResponse Authorize(
14+
APIGatewayCustomAuthorizerV2Request request,
15+
ILambdaContext context)
16+
{
17+
return new APIGatewayCustomAuthorizerV2SimpleResponse
18+
{
19+
IsAuthorized = true
20+
};
21+
}
22+
}
23+
}

0 commit comments

Comments
 (0)