|
2 | 2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | 3 | // See the LICENSE file in the project root for more information. |
4 | 4 |
|
| 5 | +using System; |
5 | 6 | using System.Collections.Generic; |
| 7 | +using System.Diagnostics.CodeAnalysis; |
6 | 8 | using System.Diagnostics.Contracts; |
7 | 9 | using System.Linq; |
8 | 10 | using System.Text; |
@@ -129,12 +131,15 @@ private static IEnumerable<MemberDeclarationSyntax> CreateCommandMembers(Generat |
129 | 131 | fieldName = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1)}"; |
130 | 132 |
|
131 | 133 | // Get the command type symbols |
132 | | - ExtractCommandTypesFromMethod( |
| 134 | + if (!TryMapCommandTypesFromMethod( |
133 | 135 | context, |
134 | 136 | methodSymbol, |
135 | | - out ITypeSymbol commandInterfaceTypeSymbol, |
136 | | - out ITypeSymbol commandClassTypeSymbol, |
137 | | - out ITypeSymbol delegateTypeSymbol); |
| 137 | + out ITypeSymbol? commandInterfaceTypeSymbol, |
| 138 | + out ITypeSymbol? commandClassTypeSymbol, |
| 139 | + out ITypeSymbol? delegateTypeSymbol)) |
| 140 | + { |
| 141 | + return Array.Empty<MemberDeclarationSyntax>(); |
| 142 | + } |
138 | 143 |
|
139 | 144 | // Construct the generated field as follows: |
140 | 145 | // |
@@ -193,33 +198,95 @@ private static IEnumerable<MemberDeclarationSyntax> CreateCommandMembers(Generat |
193 | 198 | /// <param name="commandInterfaceTypeSymbol">The command interface type symbol.</param> |
194 | 199 | /// <param name="commandClassTypeSymbol">The command class type symbol.</param> |
195 | 200 | /// <param name="delegateTypeSymbol">The delegate type symbol for the wrapped method.</param> |
196 | | - private static void ExtractCommandTypesFromMethod( |
| 201 | + /// <returns>Whether or not <paramref name="methodSymbol"/> was valid and the requested types have been set.</returns> |
| 202 | + private static bool TryMapCommandTypesFromMethod( |
197 | 203 | GeneratorExecutionContext context, |
198 | 204 | IMethodSymbol methodSymbol, |
199 | | - out ITypeSymbol commandInterfaceTypeSymbol, |
200 | | - out ITypeSymbol commandClassTypeSymbol, |
201 | | - out ITypeSymbol delegateTypeSymbol) |
| 205 | + [NotNullWhen(true)] out ITypeSymbol? commandInterfaceTypeSymbol, |
| 206 | + [NotNullWhen(true)] out ITypeSymbol? commandClassTypeSymbol, |
| 207 | + [NotNullWhen(true)] out ITypeSymbol? delegateTypeSymbol) |
202 | 208 | { |
| 209 | + // Map <void, void> to IRelayCommand, RelayCommand, Action |
203 | 210 | if (methodSymbol.ReturnsVoid && methodSymbol.Parameters.Length == 0) |
204 | 211 | { |
205 | 212 | commandInterfaceTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.IRelayCommand")!; |
206 | 213 | commandClassTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.RelayCommand")!; |
207 | 214 | delegateTypeSymbol = context.Compilation.GetTypeByMetadataName("System.Action")!; |
| 215 | + |
| 216 | + return true; |
208 | 217 | } |
209 | | - else if (methodSymbol.ReturnsVoid && |
210 | | - methodSymbol.Parameters.Length == 1 && |
211 | | - methodSymbol.Parameters[0] is IParameterSymbol { RefKind: RefKind.None } parameter) |
| 218 | + |
| 219 | + // Map <T, void> to IRelayCommand<T>, RelayCommand<T>, Action<T> |
| 220 | + if (methodSymbol.ReturnsVoid && |
| 221 | + methodSymbol.Parameters.Length == 1 && |
| 222 | + methodSymbol.Parameters[0] is IParameterSymbol { RefKind: RefKind.None, Type: { IsRefLikeType: false, TypeKind: not TypeKind.Pointer and not TypeKind.FunctionPointer } } parameter) |
212 | 223 | { |
213 | | - commandInterfaceTypeSymbol = null; |
214 | | - commandClassTypeSymbol = null; |
215 | | - delegateTypeSymbol = null; |
| 224 | + commandInterfaceTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.IRelayCommand`1")!.Construct(parameter.Type); |
| 225 | + commandClassTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.RelayCommand`1")!.Construct(parameter.Type); |
| 226 | + delegateTypeSymbol = context.Compilation.GetTypeByMetadataName("System.Action`1")!.Construct(parameter.Type); |
| 227 | + |
| 228 | + return true; |
216 | 229 | } |
217 | | - else |
| 230 | + |
| 231 | + if (SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType, context.Compilation.GetTypeByMetadataName("System.Threading.Tasks.Task")!)) |
218 | 232 | { |
219 | | - commandInterfaceTypeSymbol = null; |
220 | | - commandClassTypeSymbol = null; |
221 | | - delegateTypeSymbol = null; |
| 233 | + // Map <void, Task> to IAsyncRelayCommand, AsyncRelayCommand, Func<Task> |
| 234 | + if (methodSymbol.Parameters.Length == 0) |
| 235 | + { |
| 236 | + commandInterfaceTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.IAsyncRelayCommand")!; |
| 237 | + commandClassTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.AsyncRelayCommand")!; |
| 238 | + delegateTypeSymbol = context.Compilation.GetTypeByMetadataName("System.Func`1")!.Construct(context.Compilation.GetTypeByMetadataName("System.Threading.Tasks.Task")!); |
| 239 | + |
| 240 | + return true; |
| 241 | + } |
| 242 | + |
| 243 | + if (methodSymbol.Parameters.Length == 1 && |
| 244 | + methodSymbol.Parameters[0] is IParameterSymbol { RefKind: RefKind.None, Type: { IsRefLikeType: false, TypeKind: not TypeKind.Pointer and not TypeKind.FunctionPointer } } singleParameter) |
| 245 | + { |
| 246 | + // Map <CancellationToken, Task> to IAsyncRelayCommand, AsyncRelayCommand, Func<CancellationToken, Task> |
| 247 | + if (SymbolEqualityComparer.Default.Equals(singleParameter.Type, context.Compilation.GetTypeByMetadataName("System.Threading.CancellationToken")!)) |
| 248 | + { |
| 249 | + commandInterfaceTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.IAsyncRelayCommand")!; |
| 250 | + commandClassTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.AsyncRelayCommand")!; |
| 251 | + delegateTypeSymbol = context.Compilation.GetTypeByMetadataName("System.Func`2")!.Construct( |
| 252 | + context.Compilation.GetTypeByMetadataName("System.Threading.CancellationToken")!, |
| 253 | + context.Compilation.GetTypeByMetadataName("System.Threading.Tasks.Task")!); |
| 254 | + |
| 255 | + return true; |
| 256 | + } |
| 257 | + |
| 258 | + // Map <T, Task> to IAsyncRelayCommand<T>, AsyncRelayCommand<T>, Func<T, Task> |
| 259 | + commandInterfaceTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.IAsyncRelayCommand`1")!.Construct(singleParameter.Type); |
| 260 | + commandClassTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.AsyncRelayCommand`1")!.Construct(singleParameter.Type); |
| 261 | + delegateTypeSymbol = context.Compilation.GetTypeByMetadataName("System.Func`2")!.Construct( |
| 262 | + singleParameter.Type, |
| 263 | + context.Compilation.GetTypeByMetadataName("System.Threading.Tasks.Task")!); |
| 264 | + |
| 265 | + return true; |
| 266 | + } |
| 267 | + |
| 268 | + // Map <T, CancellationToken, Task> to IAsyncRelayCommand<T>, AsyncRelayCommand<T>, Func<T, CancellationToken, Task> |
| 269 | + if (methodSymbol.Parameters.Length == 2 && |
| 270 | + methodSymbol.Parameters[0] is IParameterSymbol { RefKind: RefKind.None, Type: { IsRefLikeType: false, TypeKind: not TypeKind.Pointer and not TypeKind.FunctionPointer } } firstParameter && |
| 271 | + methodSymbol.Parameters[1] is IParameterSymbol { RefKind: RefKind.None, Type: { IsRefLikeType: false, TypeKind: not TypeKind.Pointer and not TypeKind.FunctionPointer } } secondParameter && |
| 272 | + SymbolEqualityComparer.Default.Equals(secondParameter.Type, context.Compilation.GetTypeByMetadataName("System.Threading.CancellationToken")!)) |
| 273 | + { |
| 274 | + commandInterfaceTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.IAsyncRelayCommand`1")!.Construct(firstParameter.Type); |
| 275 | + commandClassTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.AsyncRelayCommand`1")!.Construct(firstParameter.Type); |
| 276 | + delegateTypeSymbol = context.Compilation.GetTypeByMetadataName("System.Func`3")!.Construct( |
| 277 | + firstParameter.Type, |
| 278 | + secondParameter.Type, |
| 279 | + context.Compilation.GetTypeByMetadataName("System.Threading.Tasks.Task")!); |
| 280 | + |
| 281 | + return true; |
| 282 | + } |
222 | 283 | } |
| 284 | + |
| 285 | + commandInterfaceTypeSymbol = null; |
| 286 | + commandClassTypeSymbol = null; |
| 287 | + delegateTypeSymbol = null; |
| 288 | + |
| 289 | + return false; |
223 | 290 | } |
224 | 291 | } |
225 | 292 | } |
0 commit comments