public async Task ITypeSymbolExtensionsShouldDetectVoidReturnTypeAsDesired() { string controllerCode = @" public interface IDto{ } public class SampleDto:IDto{ } public class SamplesController : DtoController<SampleDto> { [ActionAttribute] public void Do(){ } [ActionAttribute] public Task Do2(){ } [ActionAttribute] public int Do3(){ return 0; } [ActionAttribute] public async Task<int> Do4(){ return 0; } } public class DtoController<TDto> where TDto : IDto { } "; DefaultProjectDtoControllersProvider controllersProvider = new DefaultProjectDtoControllersProvider(); DtoController controller = (await controllersProvider.GetProjectDtoControllersWithTheirOperations(CreateProjectFromSourceCodes(controllerCode))).Single(); Assert.IsTrue(controller.Operations.ElementAt(0).ReturnType.IsVoid()); Assert.IsTrue(controller.Operations.ElementAt(1).ReturnType.IsVoid()); Assert.IsFalse(controller.Operations.ElementAt(2).ReturnType.IsVoid()); Assert.IsFalse(controller.Operations.ElementAt(3).ReturnType.IsVoid()); }
public virtual async Task <IList <DtoController> > GetProjectDtoControllersWithTheirOperations(Project project, IList <Project>?allSourceProjects = null) { if (project == null) { throw new ArgumentNullException(nameof(project)); } IList <DtoController> dtoControllers = new List <DtoController>(); foreach (Document doc in project.Documents) { if (!doc.SupportsSemanticModel) { continue; } SemanticModel semanticModel = await doc.GetSemanticModelAsync().ConfigureAwait(false) ?? throw new InvalidOperationException("SemanticModel is null"); SyntaxNode root = await doc.GetSyntaxRootAsync().ConfigureAwait(false) ?? throw new InvalidOperationException("SyntaxRoot is null"); List <ClassDeclarationSyntax> dtoControllersClassDecs = new List <ClassDeclarationSyntax>(); foreach (ClassDeclarationSyntax classDeclarationSyntax in root.DescendantNodes() .OfType <ClassDeclarationSyntax>()) { if (classDeclarationSyntax.BaseList == null) { continue; } INamedTypeSymbol controllerSymbol = (INamedTypeSymbol)semanticModel.GetDeclaredSymbol(classDeclarationSyntax); bool isController = controllerSymbol.IsDtoController(); if (isController == true) { dtoControllersClassDecs.Add(classDeclarationSyntax); } } if (!dtoControllersClassDecs.Any()) { continue; } foreach (ClassDeclarationSyntax dtoControllerClassDec in dtoControllersClassDecs) { INamedTypeSymbol controllerSymbol = (INamedTypeSymbol)semanticModel.GetDeclaredSymbol(dtoControllerClassDec); DtoController dtoController = new DtoController { ControllerSymbol = controllerSymbol, Name = controllerSymbol.Name.Replace("Controller", string.Empty, StringComparison.InvariantCultureIgnoreCase), Operations = new List <ODataOperation>(), ModelSymbol = controllerSymbol.BaseType.TypeArguments.ExtendedSingleOrDefault($"Looking for model of ${controllerSymbol.Name}", t => t.IsDto()) ?? await BuildAutoDto(controllerSymbol).ConfigureAwait(false) }; if (dtoController.ModelSymbol is ITypeParameterSymbol symbol) { dtoController.ModelSymbol = symbol.ConstraintTypes.ExtendedSingleOrDefault($"Looking for model on generic model {symbol.Name}", t => t.IsDto()); } if (dtoController.ControllerSymbol.IsGenericType || dtoController.ControllerSymbol.IsAbstract) { continue; } dtoControllers.Add(dtoController); foreach (MethodDeclarationSyntax methodDecSyntax in dtoControllerClassDec.DescendantNodes().OfType <MethodDeclarationSyntax>()) { IMethodSymbol methodSymbol = (IMethodSymbol)semanticModel.GetDeclaredSymbol(methodDecSyntax); if (!methodSymbol.IsOperation(out AttributeData operationAttribute)) { continue; } ODataOperation operation = new ODataOperation { Method = methodSymbol, Kind = operationAttribute.AttributeClass.Name == "ActionAttribute" ? ODataOperationKind.Action : ODataOperationKind.Function, ReturnType = methodSymbol.ReturnType }; if (operation.Kind == ODataOperationKind.Function) { operation.Parameters = operation.Method.Parameters .Where(p => p.Type.Name != "CancellationToken" && p.Type.Name != "ODataQueryOptions") .Select(parameter => new ODataOperationParameter { Name = parameter.Name, Type = parameter.Type }).ToList(); } else if (operation.Kind == ODataOperationKind.Action && operation.Method.Parameters.Any()) { IParameterSymbol actionParameterContainer = operation.Method.Parameters .Where(p => p.Type.Name != "CancellationToken" && p.Type.Name != "ODataQueryOptions") .ExtendedSingleOrDefault($"Finding parameter of {operation.Method.ContainingType.Name}.{operation.Method.Name}. It's expected to see 0 or 1 parameter only."); if (actionParameterContainer != null) { if (actionParameterContainer.Type.IsDto() || actionParameterContainer.Type.IsComplexType() || actionParameterContainer.Type.IsCollectionType()) { operation.Parameters = new List <ODataOperationParameter> { new ODataOperationParameter { Name = actionParameterContainer.Name, Type = actionParameterContainer.Type } }; } // ToDo: else if (parameter is string or primitive or enum or date time or date time offset) { throw an exception; } else { operation.Parameters = actionParameterContainer.Type.GetMembers() .OfType <IPropertySymbol>() .Select(prop => new ODataOperationParameter { Name = prop.Name, Type = prop.Type }).ToList(); } } } dtoController.Operations.Add(operation); } } } return(dtoControllers); }
public virtual IList <DtoController> GetProjectDtoControllersWithTheirOperations(Project project) { if (project == null) { throw new ArgumentNullException(nameof(project)); } IList <DtoController> dtoControllers = new List <DtoController>(); foreach (Document doc in project.Documents) { if (!doc.SupportsSemanticModel) { continue; } SemanticModel semanticModel = doc.GetSemanticModelAsync().Result; SyntaxNode root = doc.GetSyntaxRootAsync(CancellationToken.None).Result; List <ClassDeclarationSyntax> dtoControllersClassDecs = new List <ClassDeclarationSyntax>(); foreach (ClassDeclarationSyntax classDeclarationSyntax in root.DescendantNodes() .OfType <ClassDeclarationSyntax>()) { if (classDeclarationSyntax.BaseList == null) { continue; } INamedTypeSymbol controllerSymbol = (INamedTypeSymbol)semanticModel.GetDeclaredSymbol(classDeclarationSyntax); INamedTypeSymbol type = controllerSymbol; bool isController = false; while (type != null) { isController = type.BaseType?.Name == "DtoController"; if (isController == true) { break; } type = type.BaseType; } if (isController == true) { dtoControllersClassDecs.Add(classDeclarationSyntax); } } if (!dtoControllersClassDecs.Any()) { continue; } foreach (ClassDeclarationSyntax dtoControllerClassDec in dtoControllersClassDecs) { INamedTypeSymbol controllerSymbol = (INamedTypeSymbol)semanticModel.GetDeclaredSymbol(dtoControllerClassDec); DtoController dtoController = new DtoController { ControllerSymbol = controllerSymbol, Name = controllerSymbol.Name.Replace("Controller", string.Empty), Operations = new List <ODataOperation>(), ModelSymbol = controllerSymbol.BaseType.TypeArguments.SingleOrDefault(t => t.IsDto()) }; if (dtoController.ModelSymbol != null && dtoController.ModelSymbol is ITypeParameterSymbol) { dtoController.ModelSymbol = ((ITypeParameterSymbol)dtoController.ModelSymbol).ConstraintTypes.SingleOrDefault(t => t.IsDto()); } if (dtoController.ModelSymbol == null) { continue; } dtoControllers.Add(dtoController); if (dtoController.ControllerSymbol.IsGenericType) { continue; } foreach (MethodDeclarationSyntax methodDecSyntax in dtoControllerClassDec.DescendantNodes().OfType <MethodDeclarationSyntax>()) { IMethodSymbol methodSymbol = (IMethodSymbol)semanticModel.GetDeclaredSymbol(methodDecSyntax); ImmutableArray <AttributeData> attrs = methodSymbol.GetAttributes(); AttributeData actionAttribute = attrs.SingleOrDefault(att => att.AttributeClass.Name == "ActionAttribute"); AttributeData functionAttribute = attrs.SingleOrDefault(att => att.AttributeClass.Name == "FunctionAttribute"); if (actionAttribute == null && functionAttribute == null) { continue; } ODataOperation operation = new ODataOperation { Method = methodSymbol, Kind = actionAttribute != null ? ODataOperationKind.Action : ODataOperationKind.Function, ReturnType = methodSymbol.ReturnType }; operation.Parameters = operation.Method.GetAttributes() .Where(att => att.AttributeClass.Name == "ParameterAttribute") .Select(parameterAttribute => new ODataOperationParameter { Name = parameterAttribute.ConstructorArguments[0].Value.ToString(), Type = ((ITypeSymbol)parameterAttribute.ConstructorArguments[1].Value), IsOptional = parameterAttribute.ConstructorArguments.Count() == 3 && object.Equals(parameterAttribute.ConstructorArguments[2].Value, true) }).ToList(); dtoController.Operations.Add(operation); } } } return(dtoControllers); }
public virtual async Task <IList <DtoController> > GetProjectDtoControllersWithTheirOperations(Project project, IList <Project> allSourceProjects = null) { if (project == null) { throw new ArgumentNullException(nameof(project)); } IList <DtoController> dtoControllers = new List <DtoController>(); foreach (Document doc in project.Documents) { if (!doc.SupportsSemanticModel) { continue; } SemanticModel semanticModel = await doc.GetSemanticModelAsync(); SyntaxNode root = await doc.GetSyntaxRootAsync(); List <ClassDeclarationSyntax> dtoControllersClassDecs = new List <ClassDeclarationSyntax>(); foreach (ClassDeclarationSyntax classDeclarationSyntax in root.DescendantNodes() .OfType <ClassDeclarationSyntax>()) { if (classDeclarationSyntax.BaseList == null) { continue; } INamedTypeSymbol controllerSymbol = (INamedTypeSymbol)semanticModel.GetDeclaredSymbol(classDeclarationSyntax); INamedTypeSymbol type = controllerSymbol; bool isController = false; while (type != null) { isController = type.BaseType?.Name == "DtoController"; if (isController == true) { break; } type = type.BaseType; } if (isController == true) { dtoControllersClassDecs.Add(classDeclarationSyntax); } } if (!dtoControllersClassDecs.Any()) { continue; } foreach (ClassDeclarationSyntax dtoControllerClassDec in dtoControllersClassDecs) { INamedTypeSymbol controllerSymbol = (INamedTypeSymbol)semanticModel.GetDeclaredSymbol(dtoControllerClassDec); DtoController dtoController = new DtoController { ControllerSymbol = controllerSymbol, Name = controllerSymbol.Name.Replace("Controller", string.Empty), Operations = new List <ODataOperation>(), ModelSymbol = controllerSymbol.BaseType.TypeArguments.ExtendedSingleOrDefault($"Looking for model of ${controllerSymbol.Name}", t => t.IsDto()) }; if (dtoController.ModelSymbol is ITypeParameterSymbol) { dtoController.ModelSymbol = ((ITypeParameterSymbol)dtoController.ModelSymbol).ConstraintTypes.ExtendedSingleOrDefault($"Looking for model on generic model {dtoController.ModelSymbol.Name}", t => t.IsDto()); } if (dtoController.ModelSymbol == null) { continue; } dtoControllers.Add(dtoController); if (dtoController.ControllerSymbol.IsGenericType) { continue; } foreach (MethodDeclarationSyntax methodDecSyntax in dtoControllerClassDec.DescendantNodes().OfType <MethodDeclarationSyntax>()) { IMethodSymbol methodSymbol = (IMethodSymbol)semanticModel.GetDeclaredSymbol(methodDecSyntax); ImmutableArray <AttributeData> attrs = methodSymbol.GetAttributes(); AttributeData actionAttribute = attrs.ExtendedSingleOrDefault($"Looking for action attribute on {methodSymbol.Name}", att => att.AttributeClass.Name == "ActionAttribute"); AttributeData functionAttribute = attrs.ExtendedSingleOrDefault($"Looking for function attribute on {methodSymbol.Name}", att => att.AttributeClass.Name == "FunctionAttribute"); if (actionAttribute == null && functionAttribute == null) { continue; } ODataOperation operation = new ODataOperation { Method = methodSymbol, Kind = actionAttribute != null ? ODataOperationKind.Action : ODataOperationKind.Function, ReturnType = methodSymbol.ReturnType }; if (operation.Kind == ODataOperationKind.Function) { operation.Parameters = operation.Method.Parameters .Where(p => p.Type.Name != "CancellationToken" && p.Type.Name != "ODataQueryOptions") .Select(parameter => new ODataOperationParameter { Name = parameter.Name, Type = parameter.Type }).ToList(); } else if (operation.Kind == ODataOperationKind.Action && operation.Method.Parameters.Any()) { IParameterSymbol actionParameterContainer = operation.Method.Parameters .Where(p => p.Type.Name != "CancellationToken" && p.Type.Name != "ODataQueryOptions") .ExtendedSingleOrDefault($"Looking for one parameter other than cancellation token or odata query options on {operation.Method.Name}"); if (actionParameterContainer != null) { operation.Parameters = actionParameterContainer.Type.GetMembers() .OfType <IPropertySymbol>() .Select(prop => new ODataOperationParameter { Name = prop.Name, Type = prop.Type }).ToList(); } } dtoController.Operations.Add(operation); } } } return(dtoControllers); }