Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions packages/ack_generator/lib/src/analyzer/schema_ast_analyzer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,16 @@ class SchemaAstAnalyzer {
),
null,
);
case 'enumString':
case 'literal':
return (typeProvider.stringType, null);
case 'enumValues':
final resolvedType = _resolveEnumValuesType(invocation, library);
if (resolvedType != null) {
return (resolvedType, null);
}
// Fallback to String if the enum type can't be resolved
return (typeProvider.stringType, null);
Comment thread
leoafarias marked this conversation as resolved.
Outdated
default:
throw InvalidGenerationSourceError(
'Unsupported schema method: Ack.$schemaMethod()',
Expand All @@ -591,6 +601,59 @@ class SchemaAstAnalyzer {
}
}

/// Extracts the enum type name from an `Ack.enumValues<T>(...)` invocation.
///
/// Tries the explicit type argument first, then infers from the argument
/// pattern (e.g. `UserRole.values`).
String? _extractEnumTypeNameFromInvocation(MethodInvocation invocation) {
// From type argument: Ack.enumValues<UserRole>(...)
final typeArgs = invocation.typeArguments?.arguments;
if (typeArgs != null && typeArgs.isNotEmpty) {
final typeAnnotation = typeArgs.first;
if (typeAnnotation is NamedType) {
return typeAnnotation.name2.lexeme;
}
return typeArgs.first.toString();
Comment thread
leoafarias marked this conversation as resolved.
Outdated
}

// From argument pattern: Ack.enumValues(UserRole.values)
final args = invocation.argumentList.arguments;
if (args.isNotEmpty) {
final firstArg = args.first;
if (firstArg is PrefixedIdentifier &&
firstArg.identifier.name == 'values') {
return firstArg.prefix.name;
}
}

return null;
}

/// Resolves the enum DartType from an `Ack.enumValues<T>(...)` invocation
/// by extracting the type name and looking it up in the library.
DartType? _resolveEnumValuesType(
MethodInvocation invocation,
LibraryElement2 library,
) {
final enumTypeName = _extractEnumTypeNameFromInvocation(invocation);
if (enumTypeName == null) return null;

for (final enumElement in library.enums) {
if (enumElement.name3 == enumTypeName) {
return enumElement.thisType;
}
}

// Also check classes (for class-based enums)
for (final classElement in library.classes) {
if (classElement.name3 == enumTypeName) {
return classElement.thisType;
}
}
Comment on lines +716 to +926

Copilot AI Feb 16, 2026

Copy link

Choose a reason for hiding this comment

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

_resolveEnumValuesType only checks library.enums/library.classes, so enums imported from other libraries (or referenced via prefixed imports / re-exports) won’t resolve and will trigger the fallback path. Since the AST is resolved in these builders, prefer extracting T directly from the invocation’s resolved static type (EnumSchema) or from the type argument’s resolved element/type instead of searching only the current library.

Copilot uses AI. Check for mistakes.

return null;
}

_ListElementRef _resolveListElementRef(Expression firstArg) {
if (firstArg is MethodInvocation) {
final baseInvocation = _findBaseAckInvocation(firstArg);
Expand Down Expand Up @@ -1112,6 +1175,10 @@ class SchemaAstAnalyzer {
return 'List<$nestedType>';
}

if (methodName == 'enumValues') {

Copilot AI Feb 16, 2026

Copy link

Choose a reason for hiding this comment

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

In list element type string extraction, using _extractEnumTypeNameFromInvocation will miss common shapes like alias.UserRole.values (AST is typically a PropertyAccess, not a PrefixedIdentifier) and can again drop prefixes, leading to incorrect List<...> representation strings. Consider reusing the same resolved-type-based approach as enumValues field resolution so list element typing works with prefixed/imported enums too.

Suggested change
if (methodName == 'enumValues') {
if (methodName == 'enumValues') {
// Prefer resolved static types so we correctly handle shapes like
// `alias.UserRole.values` and preserve import prefixes.
final enumArgs = ref.ackBase!.argumentList.arguments;
if (enumArgs.isNotEmpty) {
final argType = enumArgs.first.staticType;
if (argType is InterfaceType) {
InterfaceType? enumType;
// Typical case: `MyEnum.values` has static type `List<MyEnum>`.
if (argType.isDartCoreList && argType.typeArguments.isNotEmpty) {
final elementType = argType.typeArguments.first;
if (elementType is InterfaceType) {
enumType = elementType;
}
} else {
// Fallback: treat the interface type itself as the enum type.
enumType = argType;
}
final enumElement = enumType?.element2;
if (enumElement is EnumElement2) {
// Use the analyzer's display string to keep prefixes and drop nullability.
return enumType!.getDisplayString(withNullability: false);
}
}
}
// Fall back to the existing AST-based extraction to avoid regressions.

Copilot uses AI. Check for mistakes.
return _extractEnumTypeNameFromInvocation(ref.ackBase!) ?? 'dynamic';
}

// Map primitive schema types
return _mapSchemaMethodToType(methodName);
}
Expand Down Expand Up @@ -1264,6 +1331,8 @@ class SchemaAstAnalyzer {
String _mapSchemaMethodToType(String methodName) {
switch (methodName) {
case 'string':
case 'enumString':
case 'literal':
return 'String';
case 'integer':
return 'int';
Expand Down
Loading
Loading