-
-
Notifications
You must be signed in to change notification settings - Fork 803
[Analyzer] Support IsSelected attribute #9527
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -666,6 +666,8 @@ public virtual void WriteResolverFields(IOutputTypeInfo type) | |||||||||||
| { | ||||||||||||
| WriteResolverField(resolver); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| WriteIsSelectedFields(resolver); | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
|
|
@@ -692,23 +694,34 @@ public virtual void WriteResolverConstructor(IOutputTypeInfo type, ILocalTypeLoo | |||||||||||
| WriteResolverConstructor( | ||||||||||||
| type, | ||||||||||||
| typeLookup, | ||||||||||||
| type.Resolvers.Any(t => t.RequiresParameterBindings)); | ||||||||||||
| type.Resolvers.Any(t => t.RequiresParameterBindings), | ||||||||||||
| type.Resolvers.Any(HasIsSelectedFields)); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| protected void WriteResolverConstructor( | ||||||||||||
| IOutputTypeInfo type, | ||||||||||||
| ILocalTypeLookup typeLookup, | ||||||||||||
| bool requiresParameterBindings) | ||||||||||||
| bool requiresParameterBindings, | ||||||||||||
| bool requiresIsSelectedInit = false) | ||||||||||||
| { | ||||||||||||
| if (!requiresParameterBindings) | ||||||||||||
| if (!requiresParameterBindings && !requiresIsSelectedInit) | ||||||||||||
| { | ||||||||||||
| return; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| Writer.WriteLine(); | ||||||||||||
| Writer.WriteIndentedLine( | ||||||||||||
| "public __Resolvers(global::{0} bindingResolver)", | ||||||||||||
| WellKnownTypes.ParameterBindingResolver); | ||||||||||||
|
|
||||||||||||
| if (requiresParameterBindings) | ||||||||||||
| { | ||||||||||||
| Writer.WriteIndentedLine( | ||||||||||||
| "public __Resolvers(global::{0} bindingResolver)", | ||||||||||||
| WellKnownTypes.ParameterBindingResolver); | ||||||||||||
| } | ||||||||||||
| else | ||||||||||||
| { | ||||||||||||
| Writer.WriteIndentedLine("public __Resolvers()"); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| Writer.WriteIndentedLine("{"); | ||||||||||||
|
|
||||||||||||
| using (Writer.IncreaseIndent()) | ||||||||||||
|
|
@@ -725,6 +738,7 @@ protected virtual void WriteResolversBindingInitialization(IOutputTypeInfo type, | |||||||||||
| foreach (var resolver in type.Resolvers) | ||||||||||||
| { | ||||||||||||
| WriteResolverBindingInitialization(resolver, typeLookup); | ||||||||||||
| WriteIsSelectedInitialization(resolver); | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
|
|
@@ -1700,6 +1714,63 @@ private void WriteResolverArguments(Resolver resolver, IMethodSymbol resolverMet | |||||||||||
| i); | ||||||||||||
| break; | ||||||||||||
|
|
||||||||||||
| case ResolverParameterKind.IsSelected: | ||||||||||||
| var (variant, fieldNames, _) = GetIsSelectedInfo(parameter); | ||||||||||||
|
|
||||||||||||
| switch (variant) | ||||||||||||
| { | ||||||||||||
| case IsSelectedVariant.SingleField: | ||||||||||||
| Writer.WriteIndentedLine( | ||||||||||||
| "var args{0} = context.Select().IsSelected(\"{1}\");", | ||||||||||||
| i, | ||||||||||||
| fieldNames[0]); | ||||||||||||
| break; | ||||||||||||
|
|
||||||||||||
| case IsSelectedVariant.MultipleFields: | ||||||||||||
| var sb = new StringBuilder(); | ||||||||||||
| for (var j = 0; j < fieldNames.Length; j++) | ||||||||||||
| { | ||||||||||||
| if (j > 0) | ||||||||||||
| { | ||||||||||||
| sb.Append(", "); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| sb.Append('"'); | ||||||||||||
| sb.Append(fieldNames[j]); | ||||||||||||
| sb.Append('"'); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| Writer.WriteIndentedLine( | ||||||||||||
| "var args{0} = context.Select().IsSelected({1});", | ||||||||||||
| i, | ||||||||||||
| sb.ToString()); | ||||||||||||
| break; | ||||||||||||
|
|
||||||||||||
| case IsSelectedVariant.FieldSet: | ||||||||||||
| Writer.WriteIndentedLine( | ||||||||||||
| "var args{0} = context.Select().IsSelected(_isSelected_{1}_{2});", | ||||||||||||
| i, | ||||||||||||
| resolver.Member.Name, | ||||||||||||
| parameter.Name); | ||||||||||||
| break; | ||||||||||||
|
|
||||||||||||
| case IsSelectedVariant.Pattern: | ||||||||||||
| Writer.WriteIndentedLine( | ||||||||||||
| "var args{0}_selectionContext = new global::HotChocolate.Resolvers.IsSelectedContext(context.Schema, context.Select());", | ||||||||||||
| i); | ||||||||||||
| Writer.WriteIndentedLine( | ||||||||||||
| "global::HotChocolate.Resolvers.IsSelectedVisitor.Instance.Visit(_isSelected_{0}_{1}, args{2}_selectionContext);", | ||||||||||||
| resolver.Member.Name, | ||||||||||||
| parameter.Name, | ||||||||||||
| i); | ||||||||||||
| Writer.WriteIndentedLine( | ||||||||||||
| "var args{0} = args{0}_selectionContext.AllSelected;", | ||||||||||||
| i); | ||||||||||||
| break; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| break; | ||||||||||||
|
|
||||||||||||
| case ResolverParameterKind.Unknown: | ||||||||||||
| Writer.WriteIndentedLine( | ||||||||||||
| "var args{0} = _binding_{1}_{2}.Execute<{3}>(context);", | ||||||||||||
|
|
@@ -1966,6 +2037,165 @@ private static string EscapeChar(char c) | |||||||||||
| }; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| protected void WriteIsSelectedFields(Resolver resolver) | ||||||||||||
| { | ||||||||||||
| foreach (var parameter in resolver.Parameters) | ||||||||||||
| { | ||||||||||||
| if (parameter.Kind != ResolverParameterKind.IsSelected) | ||||||||||||
| { | ||||||||||||
| continue; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| var (variant, _, _) = GetIsSelectedInfo(parameter); | ||||||||||||
|
|
||||||||||||
| switch (variant) | ||||||||||||
| { | ||||||||||||
| case IsSelectedVariant.FieldSet: | ||||||||||||
| Writer.WriteIndentedLine( | ||||||||||||
| "private readonly global::System.Collections.Generic.HashSet<string> _isSelected_{0}_{1};", | ||||||||||||
| resolver.Member.Name, | ||||||||||||
| parameter.Name); | ||||||||||||
| break; | ||||||||||||
|
|
||||||||||||
| case IsSelectedVariant.Pattern: | ||||||||||||
| Writer.WriteIndentedLine( | ||||||||||||
| "private readonly global::HotChocolate.Language.SelectionSetNode _isSelected_{0}_{1};", | ||||||||||||
| resolver.Member.Name, | ||||||||||||
| parameter.Name); | ||||||||||||
| break; | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| protected void WriteIsSelectedInitialization(Resolver resolver) | ||||||||||||
| { | ||||||||||||
| foreach (var parameter in resolver.Parameters) | ||||||||||||
| { | ||||||||||||
| if (parameter.Kind != ResolverParameterKind.IsSelected) | ||||||||||||
| { | ||||||||||||
| continue; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| var (variant, fieldNames, patternString) = GetIsSelectedInfo(parameter); | ||||||||||||
|
|
||||||||||||
| switch (variant) | ||||||||||||
| { | ||||||||||||
| case IsSelectedVariant.FieldSet: | ||||||||||||
| var sb = new StringBuilder(); | ||||||||||||
| for (var i = 0; i < fieldNames.Length; i++) | ||||||||||||
| { | ||||||||||||
| if (i > 0) | ||||||||||||
| { | ||||||||||||
| sb.Append(", "); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| sb.Append('"'); | ||||||||||||
| sb.Append(fieldNames[i]); | ||||||||||||
| sb.Append('"'); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| Writer.WriteIndentedLine( | ||||||||||||
| "_isSelected_{0}_{1} = new global::System.Collections.Generic.HashSet<string>([{2}]);", | ||||||||||||
|
||||||||||||
| "_isSelected_{0}_{1} = new global::System.Collections.Generic.HashSet<string>([{2}]);", | |
| "_isSelected_{0}_{1} = new global::System.Collections.Generic.HashSet<string>(new string[] {{ {2} }});", |
Copilot
AI
Apr 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
patternString is emitted directly into a C# string literal for ParseSelectionSet(...) without escaping. If the pattern includes quotes/backslashes/newlines (e.g. field arguments or directives with string values), the generated source will not compile; please escape the value (you already have EscapeString(...) in this file).
| Writer.WriteIndentedLine( | |
| $"_isSelected_{resolver.Member.Name}_{parameter.Name} = global::HotChocolate.Language.Utf8GraphQLParser.Syntax.ParseSelectionSet(\"{{ {patternString} }}\");"); | |
| var selectionSet = EscapeString($"{{ {patternString} }}"); | |
| Writer.WriteIndentedLine( | |
| $"_isSelected_{resolver.Member.Name}_{parameter.Name} = global::HotChocolate.Language.Utf8GraphQLParser.Syntax.ParseSelectionSet(\"{selectionSet}\");"); |
Copilot
AI
Apr 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The SingleField vs Pattern detection for the single-string ctor argument only checks for spaces / {. A value like name(arg: "x"), name@skip(if:true), or other non-GraphQL-Name characters would be treated as SingleField and generate IsSelected("...") (likely always false) instead of being parsed as a selection set. Consider using a proper GraphQL Name check (or parsing) to decide the variant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ResolverParameterKind.IsSelectedis handled here for non-batch resolvers, but the batch-resolver argument generation switch (inWriteBatchResolver) does not handleIsSelectedand will fall back todefault(...), causing batch resolvers to always receivefalse(or the default) for[IsSelected]parameters. Add corresponding handling in the batch-resolver path (likely mirroring theSelectioncase usingcontexts[0]).