-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathInterfaceResolver.cs
More file actions
162 lines (141 loc) · 5.91 KB
/
InterfaceResolver.cs
File metadata and controls
162 lines (141 loc) · 5.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
namespace GDScriptInterface.SourceGenerator;
/// <summary>
/// Provides methods to resolve and validate interfaces for generating GDScript interface mappings.
/// </summary>
internal static class InterfaceResolver
{
/// <summary>
/// Attempts to resolve the target interface and build a type map for the given class symbol.
/// </summary>
/// <param name="context">The source production context used for reporting diagnostics.</param>
/// <param name="compilation">The compilation context used for type resolution.</param>
/// <param name="classSymbol">The class symbol to analyze.</param>
/// <param name="iface">The resolved interface symbol, if successful.</param>
/// <param name="propInfo">The resolved GDScript property information.</param>
/// <returns>True if the interface was successfully resolved; otherwise, false.</returns>
public static bool TryResolveTargetInterface(
SourceProductionContext context,
Compilation compilation,
INamedTypeSymbol classSymbol,
out INamedTypeSymbol iface,
out Dictionary<ITypeSymbol, GDScriptPropertyInfo> propInfo)
{
iface = null!;
propInfo = []; // Initialize an empty type map
// Find the attribute marking the class for interface mapping
var attr = classSymbol.GetAttributes()
.FirstOrDefault(a =>
a.AttributeClass?.Name ==
"GenerateGDScriptInterfaceMappingAttribute");
if (attr is null)
return false; // No matching attribute found
// Validate the attribute's constructor arguments
if (attr.ConstructorArguments.Length != 1 ||
attr.ConstructorArguments[0].Value is not INamedTypeSymbol ifaceSymbol)
{
ErrorReporter.ReportInvalidAttributeUsage(
context,
classSymbol,
"Attribute requires a single interface type parameter");
return false; // Invalid attribute usage
}
// Ensure the provided type is an interface
if (ifaceSymbol.TypeKind != TypeKind.Interface)
{
ErrorReporter.ReportInvalidAttributeUsage(
context,
classSymbol,
$"Type '{ifaceSymbol.Name}' is not an interface");
return false; // Provided type is not an interface
}
iface = ifaceSymbol;
// Validate the interface and build the type map
if (!ValidateInterface(context, compilation, iface, out propInfo))
return false;
return true; // Successfully resolved the interface
}
/// <summary>
/// Validates the given interface and ensures all its members are compatible with the Variant system.
/// </summary>
/// <param name="context">The source production context used for reporting diagnostics.</param>
/// <param name="compilation">The compilation context used for type resolution.</param>
/// <param name="iface">The interface symbol to validate.</param>
/// <param name="propInfo">The resolved GDScript property information.</param>
/// <returns>True if the interface is valid; otherwise, false.</returns>
private static bool ValidateInterface(
SourceProductionContext context,
Compilation compilation,
INamedTypeSymbol iface,
out Dictionary<ITypeSymbol, GDScriptPropertyInfo> propInfo
)
{
var localPropInfo = new Dictionary<ITypeSymbol, GDScriptPropertyInfo>(SymbolEqualityComparer.Default);
// Generic interfaces are not supported
if (iface.IsGenericType)
{
ErrorReporter.ReportGenericInterfaceNotSupported(context, iface);
propInfo = [];
return false;
}
// Helper function to ensure a type is resolved and added to the type map
bool Ensure(ITypeSymbol t, ISymbol symbol)
{
if (t == null) return true; // Skip null types
if (localPropInfo.ContainsKey(t)) return true; // Skip already-resolved types
if (VariantResolver.TryResolveVariantType(compilation, t, out var propInfo))
{
localPropInfo[t] = propInfo; // Add resolved propInfo to the map
return true;
}
ErrorReporter.ReportInvalidType(context, iface, t, symbol); // Report invalid type
return false;
}
// Process method parameters and return types
foreach (var method in iface.GetMembers().OfType<IMethodSymbol>())
{
// Generic methods are not supported
if (method.IsGenericMethod)
{
ErrorReporter.ReportGenericMethodNotSupported(context, iface, method);
propInfo = [];
return false;
}
if (method.MethodKind != MethodKind.Ordinary)
continue; // Skip non-ordinary methods (e.g., constructors)
foreach (var param in method.Parameters)
{
if (!Ensure(param.Type, param))
{
propInfo = [];
return false;
}
}
if (!Ensure(method.ReturnType, method))
{
propInfo = [];
return false;
}
}
// Process property types and index parameters
foreach (var prop in iface.GetMembers().OfType<IPropertySymbol>())
{
// Indexers are not supported
if (prop.IsIndexer)
{
ErrorReporter.ReportIndexerNotSupported(context, iface, prop);
propInfo = [];
return false;
}
if (!Ensure(prop.Type, prop))
{
propInfo = [];
return false;
}
}
propInfo = localPropInfo;
return true;
}
}