Skip to content

Add collection return support to ScanForTypes attribute#55

Merged
Dreamescaper merged 3 commits intomainfrom
copilot/return-found-types-as-array
Mar 26, 2026
Merged

Add collection return support to ScanForTypes attribute#55
Dreamescaper merged 3 commits intomainfrom
copilot/return-found-types-as-array

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 20, 2026

ScanForTypes methods can now return Type[] or IEnumerable<Type> (no Handler needed) to get the matched types directly, or return TResponse[]/IEnumerable<TResponse> when a Handler mapping T → TResponse is provided. This feature is exclusive to ScanForTypes; GenerateServiceRegistrations is unaffected.

New capabilities

// Case 1: return matched types directly — no Handler required
[ScanForTypes(AssignableTo = typeof(IService))]
public static partial Type[] GetServiceTypes();
// Generates: return [typeof(MyService1), typeof(MyService2)];

// Case 2: map each found type via a Handler
[ScanForTypes(AssignableTo = typeof(IService), Handler = nameof(Describe))]
public static partial ServiceInfo[] GetServiceInfos();

private static ServiceInfo Describe<T>() => new(typeof(T).Name);
// Generates: return [Describe<MyService1>(), Describe<MyService2>()];

Changes

  • MethodModel — added ReturnTypeIsCollection / CollectionElementTypeName fields and a GetCollectionReturnInfo() helper that detects array and IEnumerable<T> return types using SpecialType.System_Collections_Generic_IEnumerable_T.
  • MethodImplementationModel — added CollectionItems (EquatableArray<string>) holding the pre-built expression strings for each collection element.
  • ParseHandlerMethodModel — relaxes the "Handler required" rule when the return type is Type[]/IEnumerable<Type>; validates Handler return type matches the collection element type for mapped returns.
  • FindServicesToRegister — populates CollectionItems with typeof(T) expressions (no-Handler path) or handler invocation expressions (Handler path).
  • DependencyInjectionGenerator — adds GenerateCollectionSource() emitting a C# collection expression (return [...]); routes generation accordingly.
  • DiagnosticDescriptors — new diagnostic DI0015 (WrongHandlerReturnTypeForCollectionReturn) when the Handler's return type doesn't match the collection element type.
Original prompt

This section details on the original issue you should resolve

<issue_title>Return found types as an array</issue_title>
<issue_description>In some cases I need to process found types, and using CustomHandler is not sufficient.

Acceptance Criteria:

  1. When method with ScanForTypes attribute returns Type[] or IEnumerable, and Handler is not specified - we return collection of found types (use collection expressions in return).
  2. When method with ScanForTypes attribute returns TResponse[] or IEnumerable, and Handler method returns TResponse (i.e. somehow maps found T type to TResponse type) - we return collection of found types mapped by Handler (use collection expressions in return).
  3. If Handler return type does not correspond to collection return type - report error.
  4. It is only supported for ScanForTypes attribute, not GenerateServiceRegistrations attribute.</issue_description>

Comments on the Issue (you are @copilot in this section)


📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.

…ble<T>)

Co-authored-by: Dreamescaper <17177729+Dreamescaper@users.noreply.github.com>
Copilot AI changed the title [WIP] Return found types as an array implementation Add collection return support to ScanForTypes attribute Mar 20, 2026
Copilot AI requested a review from Dreamescaper March 20, 2026 13:44
Copy link
Copy Markdown
Owner

@Dreamescaper Dreamescaper left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update README

{
public static partial global::System.Type[] GetServiceTypes()
{
return [typeof(global::GeneratorTests.MyService1), typeof(global::GeneratorTests.MyService2)];
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
Update to use multiline collection expression, e.g.

            public static partial global::System.Type[] GetServiceTypes()
            {
                return [
                    typeof(global::GeneratorTests.MyService1), 
                    typeof(global::GeneratorTests.MyService2)
                ];
            }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in a8286a9. GenerateCollectionSource now emits:

return [
    typeof(global::GeneratorTests.MyService1),
    typeof(global::GeneratorTests.MyService2)
];

All test expectations updated to match.

? GenerateRegistrationsSource(method, registrations)
: GenerateCustomHandlingSource(method, customHandling);
: method.ReturnTypeIsCollection
? GenerateCollectionSource(method, collectionItems)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
replace nested ternary with switch expression

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced in a8286a9:

string source = (registrations.Count, method.ReturnTypeIsCollection) switch
{
    ( > 0, _) => GenerateRegistrationsSource(method, registrations),
    (_, true) => GenerateCollectionSource(method, collectionItems),
    _ => GenerateCustomHandlingSource(method, customHandling)
};

typesFound = true;

if (attribute.CustomHandler != null)
var implementationTypeName = implementationType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
extract collection handling and custom handler to private methods

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in a8286a9. Extracted to AddCollectionItems and AddCustomHandlerItems private methods. The loop body is now:

if (method.ReturnTypeIsCollection)
    AddCollectionItems(implementationType, matchedTypes, attribute, method, collectionItems);
else if (attribute.CustomHandler != null)
    AddCustomHandlerItems(implementationType, matchedTypes, attribute, customHandlers);
else
    { /* registration logic */ }

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 26, 2026

Update README

Updated in a8286a9. Added two new examples to the README: "Get all matched types as a collection" (returning Type[] without a Handler) and "Map matched types to a custom result type" (returning TResponse[] with a mapping Handler).

Copilot AI requested a review from Dreamescaper March 26, 2026 13:01
@Dreamescaper Dreamescaper marked this pull request as ready for review March 26, 2026 13:07
@Dreamescaper Dreamescaper merged commit 405679a into main Mar 26, 2026
1 check passed
@Dreamescaper Dreamescaper deleted the copilot/return-found-types-as-array branch March 26, 2026 13:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Return found types as an array

2 participants