Skip to content

Commit 572b8be

Browse files
committed
.
1 parent 7b7135e commit 572b8be

5 files changed

Lines changed: 42 additions & 43 deletions

File tree

src/GraphQL.EntityFramework.Analyzers/DiagnosticDescriptors.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
using Microsoft.CodeAnalysis;
2-
3-
namespace GraphQL.EntityFramework.Analyzers;
4-
51
static class DiagnosticDescriptors
62
{
73
public static readonly DiagnosticDescriptor GQLEF002 = new(

src/GraphQL.EntityFramework.Analyzers/FieldBuilderResolveAnalyzer.cs

Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,10 @@
1-
#nullable enable
2-
3-
using System.Collections.Immutable;
4-
using System.Linq;
5-
using Microsoft.CodeAnalysis;
6-
using Microsoft.CodeAnalysis.CSharp;
7-
using Microsoft.CodeAnalysis.CSharp.Syntax;
8-
using Microsoft.CodeAnalysis.Diagnostics;
9-
101
namespace GraphQL.EntityFramework.Analyzers;
112

123
[DiagnosticAnalyzer(LanguageNames.CSharp)]
134
public class FieldBuilderResolveAnalyzer : DiagnosticAnalyzer
145
{
156
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } =
16-
ImmutableArray.Create(DiagnosticDescriptors.GQLEF002);
7+
[DiagnosticDescriptors.GQLEF002];
178

189
public override void Initialize(AnalysisContext context)
1910
{
@@ -22,7 +13,7 @@ public override void Initialize(AnalysisContext context)
2213
context.RegisterSyntaxNodeAction(AnalyzeInvocation, SyntaxKind.InvocationExpression);
2314
}
2415

25-
void AnalyzeInvocation(SyntaxNodeAnalysisContext context)
16+
static void AnalyzeInvocation(SyntaxNodeAnalysisContext context)
2617
{
2718
var invocation = (InvocationExpressionSyntax)context.Node;
2819

@@ -54,16 +45,15 @@ void AnalyzeInvocation(SyntaxNodeAnalysisContext context)
5445
}
5546
}
5647

57-
bool IsFieldBuilderResolveCall(
48+
static bool IsFieldBuilderResolveCall(
5849
InvocationExpressionSyntax invocation,
5950
SemanticModel semanticModel,
6051
out LambdaExpressionSyntax? lambdaExpression)
6152
{
6253
lambdaExpression = null;
6354

6455
// Check method name is Resolve, ResolveAsync, ResolveList, or ResolveListAsync
65-
var memberAccess = invocation.Expression as MemberAccessExpressionSyntax;
66-
if (memberAccess == null)
56+
if (invocation.Expression is not MemberAccessExpressionSyntax memberAccess)
6757
{
6858
return false;
6959
}
@@ -114,7 +104,7 @@ bool IsFieldBuilderResolveCall(
114104
return false;
115105
}
116106

117-
bool IsProjectionBasedResolve(InvocationExpressionSyntax invocation, SemanticModel semanticModel)
107+
static bool IsProjectionBasedResolve(InvocationExpressionSyntax invocation, SemanticModel semanticModel)
118108
{
119109
var symbolInfo = semanticModel.GetSymbolInfo(invocation);
120110
if (symbolInfo.Symbol is not IMethodSymbol methodSymbol)
@@ -128,7 +118,7 @@ bool IsProjectionBasedResolve(InvocationExpressionSyntax invocation, SemanticMod
128118
methodSymbol.ContainingNamespace?.ToString() == "GraphQL.EntityFramework";
129119
}
130120

131-
bool IsInEfGraphType(SyntaxNode node, SemanticModel semanticModel)
121+
static bool IsInEfGraphType(SyntaxNode node, SemanticModel semanticModel)
132122
{
133123
var classDeclaration = node.FirstAncestorOrSelf<ClassDeclarationSyntax>();
134124
if (classDeclaration == null)
@@ -147,9 +137,7 @@ bool IsInEfGraphType(SyntaxNode node, SemanticModel semanticModel)
147137
while (baseType != null)
148138
{
149139
var typeName = baseType.Name;
150-
if ((typeName == "EfObjectGraphType" ||
151-
typeName == "EfInterfaceGraphType" ||
152-
typeName == "QueryGraphType") &&
140+
if (typeName is "EfObjectGraphType" or "EfInterfaceGraphType" or "QueryGraphType" &&
153141
baseType.ContainingNamespace?.ToString() == "GraphQL.EntityFramework")
154142
{
155143
return true;
@@ -161,7 +149,7 @@ bool IsInEfGraphType(SyntaxNode node, SemanticModel semanticModel)
161149
return false;
162150
}
163151

164-
bool AccessesNavigationProperties(LambdaExpressionSyntax lambda, SemanticModel semanticModel)
152+
static bool AccessesNavigationProperties(LambdaExpressionSyntax lambda, SemanticModel semanticModel)
165153
{
166154
var body = lambda.Body;
167155

@@ -196,7 +184,7 @@ bool AccessesNavigationProperties(LambdaExpressionSyntax lambda, SemanticModel s
196184
return false;
197185
}
198186

199-
bool IsContextSourceAccess(MemberAccessExpressionSyntax memberAccess, out MemberAccessExpressionSyntax? propertyAccess)
187+
static bool IsContextSourceAccess(MemberAccessExpressionSyntax memberAccess, out MemberAccessExpressionSyntax? propertyAccess)
200188
{
201189
propertyAccess = null;
202190

@@ -207,8 +195,15 @@ bool IsContextSourceAccess(MemberAccessExpressionSyntax memberAccess, out Member
207195
// Walk up the chain to find context.Source
208196
while (true)
209197
{
210-
if (current.Expression is MemberAccessExpressionSyntax { Name.Identifier.Text: "Source", Expression: IdentifierNameSyntax identifier } &&
211-
(identifier.Identifier.Text == "context" || identifier.Identifier.Text == "ctx"))
198+
if (current.Expression is
199+
MemberAccessExpressionSyntax
200+
{
201+
Name.Identifier.Text: "Source",
202+
Expression: IdentifierNameSyntax
203+
{
204+
Identifier.Text: "context" or "ctx"
205+
}
206+
})
212207
{
213208
sourceAccess = current;
214209
break;
@@ -240,21 +235,24 @@ bool IsContextSourceAccess(MemberAccessExpressionSyntax memberAccess, out Member
240235

241236
// Also check for direct access like: context.Source (without further property access)
242237
if (memberAccess.Name.Identifier.Text == "Source" &&
243-
memberAccess.Expression is IdentifierNameSyntax ctxId &&
244-
(ctxId.Identifier.Text == "context" || ctxId.Identifier.Text == "ctx"))
245-
{
246-
// This is just context.Source itself, check parent node
247-
if (memberAccess.Parent is MemberAccessExpressionSyntax parentMember)
238+
memberAccess is
248239
{
249-
propertyAccess = parentMember;
250-
return true;
251-
}
240+
Expression: IdentifierNameSyntax
241+
{
242+
Identifier.Text: "context" or "ctx"
243+
},
244+
Parent: MemberAccessExpressionSyntax parentMember
245+
})
246+
// This is just context.Source itself, check parent node
247+
{
248+
propertyAccess = parentMember;
249+
return true;
252250
}
253251

254252
return false;
255253
}
256254

257-
bool IsSafeProperty(IPropertySymbol propertySymbol)
255+
static bool IsSafeProperty(IPropertySymbol propertySymbol)
258256
{
259257
// Only primary keys and foreign keys are safe to access
260258
// because they are always included in EF projections
@@ -344,5 +342,4 @@ static bool IsForeignKeyProperty(IPropertySymbol propertySymbol)
344342
var typeName = underlyingType.ToString();
345343
return typeName == "System.Guid";
346344
}
347-
348345
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
global using System.Collections.Immutable;
2+
global using Microsoft.CodeAnalysis;
3+
global using Microsoft.CodeAnalysis.CSharp;
4+
global using Microsoft.CodeAnalysis.CSharp.Syntax;
5+
global using Microsoft.CodeAnalysis.Diagnostics;

src/GraphQL.EntityFramework.Analyzers/GraphQL.EntityFramework.Analyzers.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<TargetFramework>netstandard2.0</TargetFramework>
4+
<LangVersion>preview</LangVersion>
45
<IsRoslynComponent>true</IsRoslynComponent>
56
<DevelopmentDependency>true</DevelopmentDependency>
67
<IncludeBuildOutput>false</IncludeBuildOutput>

src/GraphQL.EntityFramework/GraphApi/FieldBuilderExtensions.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ public static FieldBuilder<TSource, IEnumerable<TReturn>> ResolveList<TDbContext
210210
var compiledProjection = projection.Compile();
211211

212212
field.Resolver = new FuncFieldResolver<TSource, IEnumerable<TReturn>>(
213-
async context =>
213+
context =>
214214
{
215215
// Resolve service from request services
216216
var executionContext = context.ExecutionContext;
@@ -240,7 +240,7 @@ public static FieldBuilder<TSource, IEnumerable<TReturn>> ResolveList<TDbContext
240240
}
241241
catch (Exception exception)
242242
{
243-
throw new Exception(
243+
throw new(
244244
$"""
245245
Failed to execute projection-based list resolve for field `{field.Name}`
246246
TSource: {typeof(TSource).FullName}
@@ -252,7 +252,7 @@ public static FieldBuilder<TSource, IEnumerable<TReturn>> ResolveList<TDbContext
252252

253253
// Note: For list results, we don't apply filters on the collection itself
254254
// Filters would be applied to individual items if needed
255-
return result ?? Enumerable.Empty<TReturn>();
255+
return result;
256256
});
257257

258258
return builder;
@@ -321,7 +321,7 @@ public static FieldBuilder<TSource, IEnumerable<TReturn>> ResolveListAsync<TDbCo
321321
}
322322
catch (Exception exception)
323323
{
324-
throw new Exception(
324+
throw new(
325325
$"""
326326
Failed to execute projection-based async list resolve for field `{field.Name}`
327327
TSource: {typeof(TSource).FullName}
@@ -333,7 +333,7 @@ public static FieldBuilder<TSource, IEnumerable<TReturn>> ResolveListAsync<TDbCo
333333

334334
// Note: For list results, we don't apply filters on the collection itself
335335
// Filters would be applied to individual items if needed
336-
return result ?? Enumerable.Empty<TReturn>();
336+
return result;
337337
});
338338

339339
return builder;
@@ -346,7 +346,7 @@ public static FieldBuilder<TSource, IEnumerable<TReturn>> ResolveListAsync<TDbCo
346346
// This is a workaround since we can't access it directly
347347
var serviceType = service.GetType();
348348
var method = serviceType.GetMethod("ResolveFilter",
349-
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
349+
BindingFlags.NonPublic | BindingFlags.Instance);
350350

351351
if (method is null)
352352
{

0 commit comments

Comments
 (0)