private MethodTransformationResult TransformMethod(MethodDeclarationSyntax methodNode, bool canCopy, MethodTransformationResult result, ITypeTransformationMetadata typeMetadata,
                                                           INamespaceTransformationMetadata namespaceMetadata)
        {
            //var result = new MethodTransformationResult(methodResult);
            var methodResult     = result.AnalyzationResult;
            var methodConversion = methodResult.Conversion;

            if (!canCopy)
            {
                methodConversion &= ~MethodConversion.Copy;
            }
            //var methodNode = customNode ?? methodResult.Node;
            var methodBodyNode = methodResult.GetBodyNode();

            // Calculate whitespace method trivias
            result.EndOfLineTrivia             = methodNode.GetEndOfLine();
            result.LeadingWhitespaceTrivia     = methodNode.GetLeadingWhitespace();
            result.IndentTrivia                = methodNode.GetIndent(result.LeadingWhitespaceTrivia, typeMetadata.LeadingWhitespaceTrivia);
            result.BodyLeadingWhitespaceTrivia = Whitespace(result.LeadingWhitespaceTrivia.ToFullString() + result.IndentTrivia.ToFullString());

            if (methodConversion == MethodConversion.Ignore)
            {
                return(result);
            }

            if (methodBodyNode == null)
            {
                if (methodConversion.HasFlag(MethodConversion.ToAsync))
                {
                    result.Transformed = methodNode;
                    if (methodConversion.HasFlag(MethodConversion.Copy))
                    {
                        result.AddMethod(methodResult.Node);
                    }
                    return(result);
                }
                if (methodConversion.HasFlag(MethodConversion.Copy))
                {
                    result.Transformed = methodResult.Node;
                }
                return(result);
            }
            var startMethodSpan = methodResult.Node.Span.Start;

            methodNode       = methodNode.WithAdditionalAnnotations(new SyntaxAnnotation(result.Annotation));
            startMethodSpan -= methodNode.SpanStart;

            // First we need to annotate nodes that will be modified in order to find them later on.
            // We cannot rely on spans after the first modification as they will change
            var typeReferencesAnnotations = new List <string>();

            foreach (var typeReference in methodResult.TypeReferences.Where(o => o.TypeAnalyzationResult.Conversion == TypeConversion.NewType))
            {
                var reference  = typeReference.ReferenceLocation;
                var startSpan  = reference.Location.SourceSpan.Start - startMethodSpan;
                var nameNode   = methodNode.GetSimpleName(startSpan, reference.Location.SourceSpan.Length);
                var annotation = Guid.NewGuid().ToString();
                methodNode = methodNode.ReplaceNode(nameNode, nameNode.WithAdditionalAnnotations(new SyntaxAnnotation(annotation)));
                typeReferencesAnnotations.Add(annotation);
            }

            // For copied methods we need just to replace type references
            if (methodConversion.HasFlag(MethodConversion.Copy))
            {
                var copiedMethod = methodNode;
                // Modify references
                foreach (var refAnnotation in typeReferencesAnnotations)
                {
                    var nameNode = copiedMethod.GetAnnotatedNodes(refAnnotation).OfType <SimpleNameSyntax>().First();
                    copiedMethod = copiedMethod
                                   .ReplaceNode(nameNode, nameNode.WithIdentifier(Identifier(nameNode.Identifier.Value + "Async")));
                }
                if (!methodConversion.HasFlag(MethodConversion.ToAsync))
                {
                    result.Transformed = copiedMethod;
                    return(result);
                }
                result.AddMethod(copiedMethod.WithoutAnnotations(result.Annotation));
            }

            foreach (var childFunction in methodResult.ChildFunctions.Where(o => o.Conversion != MethodConversion.Ignore))
            {
                var functionNode   = childFunction.GetNode();
                var functionKind   = functionNode.Kind();
                var typeSpanStart  = functionNode.SpanStart - startMethodSpan;
                var typeSpanLength = functionNode.Span.Length;
                var funcNode       = methodNode.DescendantNodesAndSelf()
                                     .First(o => o.IsKind(functionKind) && o.SpanStart == typeSpanStart && o.Span.Length == typeSpanLength);
                var transformFuncResult = TransformFunction(childFunction, result, typeMetadata, namespaceMetadata);
                result.TransformedFunctions.Add(transformFuncResult);
                methodNode = methodNode.ReplaceNode(funcNode, funcNode.WithAdditionalAnnotations(new SyntaxAnnotation(transformFuncResult.Annotation)));
            }

            foreach (var referenceResult in methodResult.FunctionReferences
                     .Where(o => o.GetConversion() == ReferenceConversion.ToAsync))
            {
                var transfromReference = new FunctionReferenceTransformationResult(referenceResult);
                var isCref             = referenceResult.IsCref;
                var reference          = referenceResult.ReferenceLocation;
                var startSpan          = reference.Location.SourceSpan.Start - startMethodSpan;
                var nameNode           = methodNode.GetSimpleName(startSpan, reference.Location.SourceSpan.Length, isCref);
                methodNode = methodNode.ReplaceNode(nameNode, nameNode.WithAdditionalAnnotations(new SyntaxAnnotation(transfromReference.Annotation)));
                result.TransformedFunctionReferences.Add(transfromReference);

                if (isCref || referenceResult.IsNameOf || !methodResult.OmitAsync)
                {
                    continue;
                }
                // We need to annotate the reference node (InvocationExpression, IdentifierName) in order to know if we need to wrap the node in a Task.FromResult
                var refNode       = referenceResult.ReferenceNode;
                var bodyReference = (IBodyFunctionReferenceAnalyzationResult)referenceResult;
                if (bodyReference.UseAsReturnValue || refNode.IsReturned())
                {
                    startSpan = refNode.SpanStart - startMethodSpan;
                    var referenceNode = methodNode.DescendantNodes().First(o => o.SpanStart == startSpan && o.Span.Length == refNode.Span.Length);
                    methodNode = methodNode.ReplaceNode(referenceNode, referenceNode.WithAdditionalAnnotations(new SyntaxAnnotation(Annotations.TaskReturned)));
                }
            }
            // Before modifying, fixup method body formatting in order to prevent weird formatting when adding additinal code
            methodNode = FixupBodyFormatting(methodNode, result);

            // Modify references
            foreach (var refAnnotation in typeReferencesAnnotations)
            {
                var nameNode = methodNode.GetAnnotatedNodes(refAnnotation).OfType <SimpleNameSyntax>().First();
                methodNode = methodNode
                             .ReplaceNode(nameNode, nameNode.WithIdentifier(Identifier(nameNode.Identifier.Value + "Async")));
            }

            foreach (var transformFunction in result.TransformedFunctions)
            {
                var funcNode = methodNode.GetAnnotatedNodes(transformFunction.Annotation).First();
                methodNode = methodNode
                             .ReplaceNode(funcNode, transformFunction.Transformed);
            }

            // We have to order by OriginalStartSpan in order to have consistent formatting when adding awaits
            foreach (var transfromReference in result.TransformedFunctionReferences.OrderByDescending(o => o.OriginalStartSpan))
            {
                methodNode = TransformFunctionReference(methodNode, methodResult, transfromReference, typeMetadata, namespaceMetadata);
            }

            result.Transformed = methodNode;

            return(result);
        }
예제 #2
0
        private static HttpEndpoint ReadMethodAsHttpEndpoint(HttpController parent, MethodDeclarationSyntax syntax)
        {
            var attributes = syntax.DescendantNodes().OfType <AttributeListSyntax>().SelectMany(x => x.Attributes).ToList();

            var endpoint = new HttpEndpoint(parent);

            endpoint.Name = syntax.Identifier.ValueText.CleanMethodName();


            endpoint.Virtual  = syntax.Modifiers.Any(x => x.Text == "virtual");
            endpoint.Override = syntax.Modifiers.Any(x => x.Text == "override");
            endpoint.New      = syntax.Modifiers.Any(x => x.Text == "new");


            //Ignore generator attribute
            endpoint.Ignored = attributes.SingleOrDefault(x => x.Name.ToFullString().MatchesAttribute(nameof(NotGeneratedAttribute))) != null;


            //Route Attribute

            var routeAttribute = attributes.SingleOrDefault(x => x.Name.ToFullString().MatchesAttribute(nameof(RouteAttribute)));

            if (routeAttribute != null)            //Fetch route from RouteAttribute
            {
                endpoint.Route = new HttpRoute(routeAttribute.ArgumentList.Arguments.ToFullString().Replace("\"", "").Trim());
            }


            //HTTP Attribute
            var knownHttpAttributes = new List <string>
            {
                $"{Constants.Http}{HttpAttributeType.Delete}",
                $"{Constants.Http}{HttpAttributeType.Get}",
                $"{Constants.Http}{HttpAttributeType.Patch}",
                $"{Constants.Http}{HttpAttributeType.Post}",
                $"{Constants.Http}{HttpAttributeType.Put}",
            };

            var httpAttribute = attributes.SingleOrDefault(x => knownHttpAttributes.Any(y => x.Name.ToFullString().MatchesAttribute(y)));

            if (httpAttribute == null)
            {
                endpoint.Ignored = true;
            }
            else
            {
                var httpType = (HttpAttributeType)Enum.Parse(typeof(HttpAttributeType),
                                                             httpAttribute.Name
                                                             .ToFullString()
                                                             .Replace(Constants.Http, "")
                                                             .Replace(Constants.Attribute, ""));

                endpoint.HttpType = Helpers.HttpMethodFromEnum(httpType);
            }



            if (endpoint.Route == null && httpAttribute?.ArgumentList != null)            //If Route was never fetched from RouteAttribute or if they used the Http(template) override
            {
                endpoint.Route = new HttpRoute(httpAttribute.ArgumentList.Arguments.ToFullString().Replace("\"", "").Trim());
            }

            //Ignore method if it doesn't have a route or http attribute
            if (endpoint.Route == null && httpAttribute == null)
            {
                endpoint.Ignored = true;
                return(endpoint);
            }


            //Obsolete Attribute
            var obsoleteAttribute = attributes.SingleOrDefault(x => x.Name.ToFullString().MatchesAttribute(nameof(ObsoleteAttribute)));

            if (obsoleteAttribute != null)
            {
                endpoint.Obsolete        = true;
                endpoint.ObsoleteMessage = obsoleteAttribute.ArgumentList.Arguments.ToFullString().Replace("\"", "").Trim();
            }

            //Authorize Attribute
            endpoint.IsSecured = attributes.SingleOrDefault(x => x.Name.ToFullString().MatchesAttribute(nameof(AuthorizeAttribute))) != null;


            //Response types
            var responseTypes = attributes.Where(x => x.Name.ToFullString().MatchesAttribute(nameof(ProducesResponseTypeAttribute)));
            var responses     = responseTypes.Select(x => new ResponseTypeDefinition(x)).ToList();

            responses.Add(new ResponseTypeDefinition(true));

            endpoint.ResponseTypes = responses.Select(x => new ResponseType(x.Type, Helpers.EnumParse <HttpStatusCode>(x.StatusValue))).ToList();

            var duplicateResponseTypes = endpoint.GetResponseTypes().GroupBy(x => x.Status).Where(x => x.Count() > 1).ToList();

            if (duplicateResponseTypes.Any())
            {
                throw new NotSupportedException($"Endpoint has multiple response types of the same status defined. {string.Join(", ", duplicateResponseTypes.Select(x => x.Key?.ToString()))}");
            }
            //Add after so we don't get duplicate error from the null Status
            endpoint.ResponseTypes.Add(new ExceptionResponseType());



            var parameters = syntax.ParameterList.Parameters.Select(x => new ParameterDefinition(x, endpoint.GetFullRoute(parent))).ToList();


            var routeParams = parameters.Where(x => x.Options.FromRoute).Select(x => new RouteParameter(x.RouteName, x.Type, x.Default)).ToList();
            var queryParams = parameters.Where(x => x.Options.FromQuery).Select(x => new QueryParameter(x.Options.QueryName, x.Type, x.Default, x.Options.QueryObject)).ToList();
            var bodyParams  = parameters.Where(x => x.Options.FromBody).Select(x => new BodyParameter(x.Name, x.Type, x.Default)).SingleOrDefault();


            endpoint.Parameters = routeParams.Cast <IParameter>().Union(queryParams).Union(new List <IParameter> {
                bodyParams
            }).NotNull().ToList();

            endpoint.Parameters.Add(new CancellationTokenModifier());
            endpoint.Parameters.Add(new CookieModifier());
            endpoint.Parameters.Add(new HeadersModifier());
            endpoint.Parameters.Add(new TimeoutModifier());
            if (endpoint.IsSecured)
            {
                endpoint.Parameters.Add(new SecurityModifier());
            }


            var parameterHeaders = attributes.Where(x => x.Name.ToFullString().MatchesAttribute(nameof(HeaderParameterAttribute)))
                                   .Select(x => new ParameterHeaderDefinition(x))
                                   .ToList();

            endpoint.ParameterHeader = parameterHeaders.Select(x => new ParameterHeader(x.Name, x.Type, x.DefaultValue)).ToList();



            var headers = attributes.Where(x => x.Name.ToFullString().MatchesAttribute(nameof(IncludeHeaderAttribute)))
                          .Select(x => new HeaderDefinition(x))
                          .ToList();

            endpoint.ConstantHeader = headers.Select(x => new ConstantHeader(x.Name, x.Value)).ToList();


            var rawReturnType = syntax.ReturnType?.ToFullString();

            HashSet <string> returnContainerTypes = new HashSet <string>()
            {
                typeof(ValueTask).FullName,
                typeof(Task).FullName,
                typeof(ActionResult).FullName
            };


            var returnType = Helpers.GetTypeFromString(rawReturnType.Trim());

            while (returnContainerTypes.Any(x => Helpers.IsType(x, returnType?.Name)))
            {
                returnType = returnType.Arguments.SingleOrDefault();
            }

            if (Helpers.IsType(typeof(IActionResult).FullName, returnType?.Name))
            {
                returnType = null;
            }

            if (returnType?.Name == "void" ||
                (Helpers.IsType(typeof(Task).FullName, returnType?.Name) && (!returnType?.Arguments.Any() ?? false)))
            {
                returnType = null;
            }

            HashSet <string> fileResults = new HashSet <string>()
            {
                nameof(PhysicalFileResult),
                nameof(FileResult),
                nameof(FileContentResult),
                nameof(FileStreamResult),
                nameof(VirtualFileResult)
            };

            if (fileResults.Any(x => Helpers.IsType(x, returnType?.Name)))
            {
                returnType             = new Helpers.TypeString(typeof(Stream).FullName);
                endpoint.ReturnsStream = true;
            }

            rawReturnType = returnType?.ToString();

            endpoint.ReturnType = rawReturnType;



            var duplicateParameters = endpoint.GetParametersWithoutResponseTypes().GroupBy(x => x.Name).Where(x => x.Count() > 1).ToList();

            if (duplicateParameters.Any())
            {
                throw new NotSupportedException($"Endpoint has multiple parameters of the same name defined. {string.Join(", ", duplicateParameters.Select(x => x.Key?.ToString()))}");
            }



            return(endpoint);
        }
예제 #3
0
        private static HashSet <string> GetClassTagHashSetUncached(IMethodSymbol methodSymbol, MethodDeclarationSyntax methodSyntax)
        {
            var model = Program.GetModel(methodSyntax);
            var ret   = new HashSet <string>();

            foreach (var invoke in methodSyntax.DescendantNodes().OfType <InvocationExpressionSyntax>())
            {
                var invokeMethod = model.GetSymbolInfo(invoke).Symbol.As <IMethodSymbol>();

                if (invokeMethod.TypeParameters.Length == 0)
                {
                    continue;
                }
                var origInvoke = invokeMethod.OriginalDefinition.As <IMethodSymbol>();

                HashSet <string> invokeTags;

                ClassTagInfo invokeInfo;
                if (_methods.TryGetValue(origInvoke, out invokeInfo))
                {
                    invokeTags = GetClassTagHashSet(origInvoke, invokeInfo.Syntax);
                }
                else
                {
                    //not a method we're writing out.  Check if it's a BCL method with known ClassTags.
                    invokeTags = TryGetBCLClassTags(origInvoke);

                    if (invokeTags == null)
                    {
                        continue; //not a BCL method we know of and not a method we're writing out. no tags needed
                    }
                }

                //invokeTags is now filled with the invoked methods tags.  See if those line up with our own tags.
                var tagIndexes = invokeTags.Select(o => invokeMethod.TypeParameters.IndexOf(invokeMethod.TypeParameters.FirstOrDefault(z => z.ToString() == o))).Where(o => o != -1);
                foreach (var retTag in tagIndexes.Select(o => invokeMethod.TypeArguments[o].ToString()))
                {
                    ret.Add(retTag);
                }
            }

            foreach (var objCreation in methodSyntax.DescendantNodes().OfType <ObjectCreationExpressionSyntax>())
            {
                var ctor = model.GetSymbolInfo(objCreation).Symbol.As <IMethodSymbol>();
                //if (objCreation.ToString() == "new List<C>()")
                //    Debugger.Break();

                var tags = TryGetBCLClassTags(ctor);
                if (tags == null)
                {
                    continue;
                }

                var genName = objCreation.Type as GenericNameSyntax;
                if (genName == null)
                {
                    continue;
                }

                foreach (var arg in genName.TypeArgumentList.Arguments)
                {
                    ret.Add(arg.ToString());
                }
            }

            foreach (var arrayCreation in methodSyntax.DescendantNodes().OfType <ArrayCreationExpressionSyntax>())
            {
                var info = model.GetTypeInfo(arrayCreation.Type.ElementType).Type;
                if (info.TypeKind == TypeKind.TypeParameter)
                {
                    ret.Add(info.ToString());
                }
            }

            return(ret);
        }
예제 #4
0
        private static Endpoint ReadAsEndpoint(Controller parent, MethodDeclarationSyntax syntax)
        {
            var attributes = syntax.DescendantNodes().OfType <AttributeListSyntax>().SelectMany(x => x.Attributes).ToList();

            var endpoint = new Endpoint(parent);

            endpoint.Name = syntax.Identifier.ValueText.Trim();


            endpoint.Virtual  = syntax.Modifiers.Any(x => x.Text == "virtual");
            endpoint.Override = syntax.Modifiers.Any(x => x.Text == "override");
            endpoint.New      = syntax.Modifiers.Any(x => x.Text == "new");


            //Ignore generator attribute
            endpoint.Ignored = attributes.SingleOrDefault(x => x.Name.ToFullString().MatchesAttribute(NoClientAttribute.AttributeName)) != null;


            //Route Attribute

            var routeAttribute = attributes.SingleOrDefault(x => x.Name.ToFullString().MatchesAttribute(Constants.Route));

            if (routeAttribute != null)            //Fetch route from RouteAttribute
            {
                endpoint.Route = new Route(routeAttribute.ArgumentList.Arguments.ToFullString().Replace("\"", "").Trim());
            }


            //HTTP Attribute

            var knownHttpAttributes = new List <string>
            {
                $"{Constants.Http}{HttpAttributeType.Delete}",
                $"{Constants.Http}{HttpAttributeType.Get}",
                $"{Constants.Http}{HttpAttributeType.Patch}",
                $"{Constants.Http}{HttpAttributeType.Post}",
                $"{Constants.Http}{HttpAttributeType.Put}",
            };

            var httpAttribute = attributes.SingleOrDefault(x => knownHttpAttributes.Any(y => x.Name.ToFullString().MatchesAttribute(y)));

            if (httpAttribute == null)
            {
                endpoint.Ignored = true;
            }
            else
            {
                var httpType = (HttpAttributeType)Enum.Parse(typeof(HttpAttributeType),
                                                             httpAttribute.Name
                                                             .ToFullString()
                                                             .Replace(Constants.Http, "")
                                                             .Replace(Constants.Attribute, ""));

                endpoint.HttpType = Helpers.HttpMethodFromEnum(httpType);
            }



            if (endpoint.Route == null && httpAttribute?.ArgumentList != null)            //If Route was never fetched from RouteAttribute or if they used the Http(template) override
            {
                endpoint.Route = new Route(httpAttribute.ArgumentList.Arguments.ToFullString().Replace("\"", "").Trim());
            }

            //Ignore method if it doesn't have a route or http attribute
            if (endpoint.Route == null && httpAttribute == null)
            {
                endpoint.Ignored = true;
                return(endpoint);
            }


            //Obsolete Attribute
            var obsoleteAttribute = attributes.SingleOrDefault(x => x.Name.ToFullString().MatchesAttribute(Constants.Obsolete));

            if (obsoleteAttribute != null)
            {
                endpoint.Obsolete        = true;
                endpoint.ObsoleteMessage = obsoleteAttribute.ArgumentList.Arguments.ToFullString().Replace("\"", "").Trim();
            }

            //Authorize Attribute
            endpoint.IsSecured = attributes.SingleOrDefault(x => x.Name.ToFullString().MatchesAttribute(Constants.Authorize)) != null;


            //Response types
            var responseTypes = attributes.Where(x => x.Name.ToFullString().MatchesAttribute(Constants.ProducesResponseType));
            var responses     = responseTypes.Select(x => new ResponseTypeDefinition(x)).ToList();

            responses.Add(new ResponseTypeDefinition(true));

            endpoint.ResponseTypes = responses.Select(x => new Framework.ResponseTypes.ResponseType(x.Type, Helpers.EnumParse <HttpStatusCode>(x.StatusValue))).ToList();

            var duplicateResponseTypes = endpoint.GetResponseTypes().GroupBy(x => x.Status).Where(x => x.Count() > 1).ToList();

            if (duplicateResponseTypes.Any())
            {
                throw new NotSupportedException($"Endpoint has multiple response types of the same status defined. {string.Join(", ", duplicateResponseTypes.Select(x => x.Key?.ToString()))}");
            }



            var parameters = syntax.ParameterList.Parameters.Select(x => new ParameterDefinition(x, endpoint.GetFullRoute(parent))).ToList();


            var routeParams = parameters.Where(x => x.Options.FromRoute).Select(x => new RouteParameter(x.RouteName, x.Type, x.Default)).ToList();
            var queryParams = parameters.Where(x => x.Options.FromQuery).Select(x => new QueryParameter(x.Options.QueryName, x.Type, x.Default)).ToList();
            var bodyParams  = parameters.Where(x => x.Options.FromBody).Select(x => new BodyParameter(x.Name, x.Type, x.Default)).SingleOrDefault();


            endpoint.Parameters = routeParams.Cast <IParameter>().Union(queryParams).Union(new List <IParameter> {
                bodyParams
            }).NotNull().ToList();

            endpoint.Parameters.Add(new CancellationTokenModifier());
            endpoint.Parameters.Add(new CookieModifier());
            endpoint.Parameters.Add(new HeadersModifier());
            endpoint.Parameters.Add(new TimeoutModifier());
            if (endpoint.IsSecured)
            {
                endpoint.Parameters.Add(new SecurityModifier());
            }


            var parameterHeaders = attributes.Where(x => x.Name.ToFullString().MatchesAttribute(HeaderParameterAttribute.AttributeName))
                                   .Select(x => new ParameterHeaderDefinition(x))
                                   .ToList();

            endpoint.ParameterHeader = parameterHeaders.Select(x => new Framework.Headers.ParameterHeader(x.Name, x.Type, x.DefaultValue)).ToList();



            var headers = attributes.Where(x => x.Name.ToFullString().MatchesAttribute(IncludeHeaderAttribute.AttributeName))
                          .Select(x => new HeaderDefinition(x))
                          .ToList();

            endpoint.ConstantHeader = headers.Select(x => new Framework.Headers.ConstantHeader(x.Name, x.Value)).ToList();


            var actionResultReturn = syntax.ReturnType.ToFullString().Contains(Constants.IActionResult);

            var returnType = syntax.ReturnType?.ToFullString();

            if (!actionResultReturn)
            {
                var regex = new Regex(@"(ValueTask|Task|ActionResult)<(.+)>");
                var match = regex.Match(returnType);
                if (match.Success)
                {
                    returnType = match.Groups[2].Value;
                }

                returnType = returnType.Trim();


                if (returnType == "void" || returnType == "Task")
                {
                    returnType = null;
                }
            }
            else
            {
                returnType = null;
            }
            endpoint.ReturnType = returnType;



            var duplicateParameters = endpoint.GetParametersWithoutResponseTypes().GroupBy(x => x.Name).Where(x => x.Count() > 1).ToList();

            if (duplicateParameters.Any())
            {
                throw new NotSupportedException($"Endpoint has multiple parameters of the same name defined. {string.Join(", ", duplicateParameters.Select(x => x.Key?.ToString()))}");
            }



            return(endpoint);
        }
예제 #5
0
 private static IEnumerable <LocalFunctionStatementAdapter> GetLocalFunctions(MethodDeclarationSyntax methodDeclaration)
 => methodDeclaration.DescendantNodes()
 .Where(member => member.IsKind(SyntaxKindEx.LocalFunctionStatement))
 .Select(member => new LocalFunctionStatementAdapter((LocalFunctionStatementSyntaxWrapper)member));
예제 #6
0
 public static AttributeSyntax[] GetMethodAttributes(MethodDeclarationSyntax mds, string name)
 {
     return(mds.DescendantNodes().OfType <AttributeSyntax>()
            .Where(attrSyntax => attrSyntax.Name.ToString().Contains(name)).ToArray());
 }
        private async Task <Document> ConvertToAsyncPackageAsync(CodeFixContext context, Diagnostic diagnostic, CancellationToken cancellationToken)
        {
            SemanticModel semanticModel = await context.Document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

            Compilation?compilation = await context.Document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);

            Assumes.NotNull(compilation);
            SyntaxNode root = await context.Document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            BaseTypeSyntax          baseTypeSyntax         = root.FindNode(diagnostic.Location.SourceSpan).FirstAncestorOrSelf <BaseTypeSyntax>();
            ClassDeclarationSyntax  classDeclarationSyntax = baseTypeSyntax.FirstAncestorOrSelf <ClassDeclarationSyntax>();
            MethodDeclarationSyntax initializeMethodSyntax = classDeclarationSyntax.DescendantNodes()
                                                             .OfType <MethodDeclarationSyntax>()
                                                             .FirstOrDefault(method => method.Modifiers.Any(modifier => modifier.IsKind(SyntaxKind.OverrideKeyword)) && method.Identifier.Text == Types.Package.Initialize);
            InvocationExpressionSyntax?baseInitializeInvocationSyntax = initializeMethodSyntax?.Body?.DescendantNodes()
                                                                        .OfType <InvocationExpressionSyntax>()
                                                                        .FirstOrDefault(ies => ies.Expression is MemberAccessExpressionSyntax memberAccess && memberAccess.Name?.Identifier.Text == Types.Package.Initialize && memberAccess.Expression is BaseExpressionSyntax);
            var             getServiceInvocationsSyntax = new List <InvocationExpressionSyntax>();
            AttributeSyntax?packageRegistrationSyntax   = null;

            {
                INamedTypeSymbol userClassSymbol             = semanticModel.GetDeclaredSymbol(classDeclarationSyntax, context.CancellationToken);
                INamedTypeSymbol packageRegistrationType     = compilation.GetTypeByMetadataName(Types.PackageRegistrationAttribute.FullName);
                AttributeData?   packageRegistrationInstance = userClassSymbol?.GetAttributes().FirstOrDefault(a => Equals(a.AttributeClass, packageRegistrationType));
                if (packageRegistrationInstance?.ApplicationSyntaxReference != null)
                {
                    packageRegistrationSyntax = (AttributeSyntax)await packageRegistrationInstance.ApplicationSyntaxReference.GetSyntaxAsync(cancellationToken).ConfigureAwait(false);
                }
            }

            if (initializeMethodSyntax != null)
            {
                getServiceInvocationsSyntax.AddRange(
                    from invocation in initializeMethodSyntax.DescendantNodes().OfType <InvocationExpressionSyntax>()
                    let memberBinding = invocation.Expression as MemberAccessExpressionSyntax
                                        let identifierName = invocation.Expression as IdentifierNameSyntax
                                                             where identifierName?.Identifier.Text == Types.Package.GetService ||
                                                             (memberBinding.Name.Identifier.Text == Types.Package.GetService && memberBinding.Expression.IsKind(SyntaxKind.ThisExpression))
                                                             select invocation);
            }

            // Make it easier to track nodes across changes.
            var nodesToTrack = new List <SyntaxNode?>
            {
                baseTypeSyntax,
                initializeMethodSyntax,
                baseInitializeInvocationSyntax,
                packageRegistrationSyntax,
            };

            nodesToTrack.AddRange(getServiceInvocationsSyntax);
            nodesToTrack.RemoveAll(n => n == null);
            SyntaxNode updatedRoot = root.TrackNodes(nodesToTrack);

            // Replace the Package base type with AsyncPackage
            baseTypeSyntax = updatedRoot.GetCurrentNode(baseTypeSyntax);
            SimpleBaseTypeSyntax asyncPackageBaseTypeSyntax = SyntaxFactory.SimpleBaseType(Types.AsyncPackage.TypeSyntax.WithAdditionalAnnotations(Simplifier.Annotation))
                                                              .WithLeadingTrivia(baseTypeSyntax.GetLeadingTrivia())
                                                              .WithTrailingTrivia(baseTypeSyntax.GetTrailingTrivia());

            updatedRoot = updatedRoot.ReplaceNode(baseTypeSyntax, asyncPackageBaseTypeSyntax);

            // Update the PackageRegistration attribute
            if (packageRegistrationSyntax != null)
            {
                LiteralExpressionSyntax trueExpression = SyntaxFactory.LiteralExpression(SyntaxKind.TrueLiteralExpression);
                packageRegistrationSyntax = updatedRoot.GetCurrentNode(packageRegistrationSyntax);
                AttributeArgumentSyntax allowsBackgroundLoadingSyntax = packageRegistrationSyntax.ArgumentList.Arguments.FirstOrDefault(a => a.NameEquals?.Name?.Identifier.Text == Types.PackageRegistrationAttribute.AllowsBackgroundLoading);
                if (allowsBackgroundLoadingSyntax != null)
                {
                    updatedRoot = updatedRoot.ReplaceNode(
                        allowsBackgroundLoadingSyntax,
                        allowsBackgroundLoadingSyntax.WithExpression(trueExpression));
                }
                else
                {
                    updatedRoot = updatedRoot.ReplaceNode(
                        packageRegistrationSyntax,
                        packageRegistrationSyntax.AddArgumentListArguments(
                            SyntaxFactory.AttributeArgument(trueExpression).WithNameEquals(SyntaxFactory.NameEquals(Types.PackageRegistrationAttribute.AllowsBackgroundLoading))));
                }
            }

            // Find the Initialize override, if present, and update it to InitializeAsync
            if (initializeMethodSyntax != null)
            {
                IdentifierNameSyntax cancellationTokenLocalVarName = SyntaxFactory.IdentifierName("cancellationToken");
                IdentifierNameSyntax progressLocalVarName          = SyntaxFactory.IdentifierName("progress");
                initializeMethodSyntax = updatedRoot.GetCurrentNode(initializeMethodSyntax);
                BlockSyntax newBody = initializeMethodSyntax.Body;

                SyntaxTriviaList leadingTrivia = SyntaxFactory.TriviaList(
                    SyntaxFactory.Comment(@"// When initialized asynchronously, we *may* be on a background thread at this point."),
                    SyntaxFactory.CarriageReturnLineFeed,
                    SyntaxFactory.Comment(@"// Do any initialization that requires the UI thread after switching to the UI thread."),
                    SyntaxFactory.CarriageReturnLineFeed,
                    SyntaxFactory.Comment(@"// Otherwise, remove the switch to the UI thread if you don't need it."),
                    SyntaxFactory.CarriageReturnLineFeed);

                ExpressionStatementSyntax switchToMainThreadStatement = SyntaxFactory.ExpressionStatement(
                    SyntaxFactory.AwaitExpression(
                        SyntaxFactory.InvocationExpression(
                            SyntaxFactory.MemberAccessExpression(
                                SyntaxKind.SimpleMemberAccessExpression,
                                SyntaxFactory.MemberAccessExpression(
                                    SyntaxKind.SimpleMemberAccessExpression,
                                    SyntaxFactory.ThisExpression(),
                                    SyntaxFactory.IdentifierName(Types.ThreadHelper.JoinableTaskFactory)),
                                SyntaxFactory.IdentifierName(Types.JoinableTaskFactory.SwitchToMainThreadAsync)))
                        .AddArgumentListArguments(SyntaxFactory.Argument(cancellationTokenLocalVarName))))
                                                                        .WithLeadingTrivia(leadingTrivia)
                                                                        .WithTrailingTrivia(SyntaxFactory.CarriageReturnLineFeed);

                if (baseInitializeInvocationSyntax != null)
                {
                    var baseInitializeAsyncInvocationBookmark = new SyntaxAnnotation();
                    AwaitExpressionSyntax baseInitializeAsyncInvocationSyntax = SyntaxFactory.AwaitExpression(
                        baseInitializeInvocationSyntax
                        .WithLeadingTrivia()
                        .WithExpression(
                            SyntaxFactory.MemberAccessExpression(
                                SyntaxKind.SimpleMemberAccessExpression,
                                SyntaxFactory.BaseExpression(),
                                SyntaxFactory.IdentifierName(Types.AsyncPackage.InitializeAsync)))
                        .AddArgumentListArguments(
                            SyntaxFactory.Argument(cancellationTokenLocalVarName),
                            SyntaxFactory.Argument(progressLocalVarName)))
                                                                                .WithLeadingTrivia(baseInitializeInvocationSyntax.GetLeadingTrivia())
                                                                                .WithAdditionalAnnotations(baseInitializeAsyncInvocationBookmark);
                    newBody = newBody.ReplaceNode(initializeMethodSyntax.GetCurrentNode(baseInitializeInvocationSyntax), baseInitializeAsyncInvocationSyntax);
                    StatementSyntax baseInvocationStatement = newBody.GetAnnotatedNodes(baseInitializeAsyncInvocationBookmark).First().FirstAncestorOrSelf <StatementSyntax>();

                    newBody = newBody.InsertNodesAfter(
                        baseInvocationStatement,
                        new[] { switchToMainThreadStatement.WithLeadingTrivia(switchToMainThreadStatement.GetLeadingTrivia().Insert(0, SyntaxFactory.LineFeed)) });
                }
                else
                {
                    newBody = newBody.WithStatements(
                        newBody.Statements.Insert(0, switchToMainThreadStatement));
                }

                MethodDeclarationSyntax initializeAsyncMethodSyntax = initializeMethodSyntax
                                                                      .WithIdentifier(SyntaxFactory.Identifier(Types.AsyncPackage.InitializeAsync))
                                                                      .WithReturnType(Types.Task.TypeSyntax.WithAdditionalAnnotations(Simplifier.Annotation))
                                                                      .AddModifiers(SyntaxFactory.Token(SyntaxKind.AsyncKeyword))
                                                                      .AddParameterListParameters(
                    SyntaxFactory.Parameter(cancellationTokenLocalVarName.Identifier).WithType(Types.CancellationToken.TypeSyntax.WithAdditionalAnnotations(Simplifier.Annotation)),
                    SyntaxFactory.Parameter(progressLocalVarName.Identifier).WithType(Types.IProgress.TypeSyntaxOf(Types.ServiceProgressData.TypeSyntax).WithAdditionalAnnotations(Simplifier.Annotation)))
                                                                      .WithBody(newBody);
                updatedRoot = updatedRoot.ReplaceNode(initializeMethodSyntax, initializeAsyncMethodSyntax);

                // Replace GetService calls with GetServiceAsync
                getServiceInvocationsSyntax = updatedRoot.GetCurrentNodes <InvocationExpressionSyntax>(getServiceInvocationsSyntax).ToList();
                updatedRoot = updatedRoot.ReplaceNodes(
                    getServiceInvocationsSyntax,
                    (orig, node) =>
                {
                    InvocationExpressionSyntax invocation = node;
                    if (invocation.Expression is IdentifierNameSyntax methodName)
                    {
                        invocation = invocation.WithExpression(SyntaxFactory.IdentifierName(Types.AsyncPackage.GetServiceAsync));
                    }
                    else if (invocation.Expression is MemberAccessExpressionSyntax memberAccess)
                    {
                        invocation = invocation.WithExpression(
                            memberAccess.WithName(SyntaxFactory.IdentifierName(Types.AsyncPackage.GetServiceAsync)));
                    }

                    return(SyntaxFactory.ParenthesizedExpression(SyntaxFactory.AwaitExpression(invocation))
                           .WithAdditionalAnnotations(Simplifier.Annotation));
                });

                updatedRoot = await Utils.AddUsingTaskEqualsDirectiveAsync(updatedRoot, cancellationToken);
            }

            Document newDocument = context.Document.WithSyntaxRoot(updatedRoot);

            newDocument = await ImportAdder.AddImportsAsync(newDocument, Simplifier.Annotation, cancellationToken : cancellationToken);

            return(newDocument);
        }
예제 #8
0
        public static void GetLocalVars(MethodDeclarationSyntax methodT, SemanticModel model, ref Dictionary <string, DeclareData> declares)
        {
            var         methodSy = model.GetDeclaredSymbol(methodT);
            DeclareData data     = new DeclareData();
            TypeInfo    typeInfo;
            var         assignmentList = methodT.DescendantNodes().OfType <AssignmentExpressionSyntax>();

            foreach (var assignment in assignmentList)
            {
                if (assignment.Kind() == SyntaxKind.SimpleAssignmentExpression)
                {
                    if (assignment.Left.Kind() == SyntaxKind.SimpleMemberAccessExpression)
                    {
                        //this.ViewBag.b | ViewBag.b
                        MemberAccessExpressionSyntax memberAccessExpressionSyntax = (MemberAccessExpressionSyntax)(assignment.Left);
                        //this.ViewBag
                        if (memberAccessExpressionSyntax.Expression.Kind() == SyntaxKind.SimpleMemberAccessExpression)
                        {
                            //this.ViewBag
                            MemberAccessExpressionSyntax thisMemberAccessExpressionSyntax
                                = (MemberAccessExpressionSyntax)(memberAccessExpressionSyntax.Expression);
                            if (thisMemberAccessExpressionSyntax.Name.Identifier.Text == "ViewBag" &&
                                thisMemberAccessExpressionSyntax.Expression.Kind() == SyntaxKind.ThisExpression)
                            {
                                data             = new DeclareData();
                                data.IsAttribute = false;
                                data.Name        = memberAccessExpressionSyntax.Name.Identifier.Text;
                                typeInfo         = model.GetTypeInfo(assignment.Right);
                                data.Type        = typeInfo.Type.ToString();
                                if (declares.ContainsKey(data.Name))
                                {
                                    declares.Remove(data.Name);
                                }
                                declares.Add(data.Name, data);
                            }
                        }
                        //ViewBag
                        if (memberAccessExpressionSyntax.Expression.Kind() == SyntaxKind.IdentifierName)
                        {
                            IdentifierNameSyntax leftIdentifierNameSyntax
                                = (IdentifierNameSyntax)(memberAccessExpressionSyntax.Expression);
                            if (leftIdentifierNameSyntax.Identifier.Text == "ViewBag")
                            {
                                data             = new DeclareData();
                                data.IsAttribute = false;
                                data.Name        = memberAccessExpressionSyntax.Name.Identifier.Text;
                                typeInfo         = model.GetTypeInfo(assignment.Right);
                                data.Type        = typeInfo.Type.ToString();
                                if (declares.ContainsKey(data.Name))
                                {
                                    declares.Remove(data.Name);
                                }
                                declares.Add(data.Name, data);
                            }
                        }
                    }
                }
            }
            foreach (var par in methodSy.Parameters)
            {
                //排除泛型参数
                if (par.Type.TypeKind != TypeKind.TypeParameter)
                {
                    data             = new DeclareData();
                    data.IsAttribute = false;
                    data.Type        = par.Type.ToString();
                    data.Name        = par.Name;
                    if (!declares.ContainsKey(data.Name))
                    {
                        declares.Add(data.Name, data);
                    }
                }
            }
        }
예제 #9
0
파일: Compiler.cs 프로젝트: zihotki/Excess
        private SyntaxNode rewriteFunction(MethodDeclarationSyntax node, bool asPublic)
        {
            //since no return type has been spacified we need to know if it returns something
            bool       returns = node.DescendantNodes().OfType <ReturnStatementSyntax>().Any();
            TypeSyntax rtype   = returns ? SyntaxFactory.IdentifierName("object") : SyntaxFactory.IdentifierName("void");

            List <SyntaxToken> modifiers = new List <SyntaxToken>();
            bool found = false;

            foreach (var mod in node.Modifiers)
            {
                switch (mod.CSharpKind())
                {
                case SyntaxKind.PublicKeyword:
                case SyntaxKind.PrivateKeyword:
                case SyntaxKind.ProtectedKeyword:
                case SyntaxKind.InternalKeyword:
                {
                    found = true;
                    break;
                }
                }

                modifiers.Add(mod);
            }

            if (!found)
            {
                if (asPublic)
                {
                    modifiers.AddRange(Compiler.Public);
                }
                else
                {
                    modifiers.AddRange(Compiler.Private);
                }
            }

            SyntaxNode result = SyntaxFactory.MethodDeclaration(rtype, node.Identifier)
                                .WithModifiers(SyntaxFactory.TokenList(modifiers))
                                .WithParameterList(rewriteParamList(node.ParameterList))
                                .WithBody(rewriteBody(node.Body))
                                .WithAdditionalAnnotations(new SyntaxAnnotation("ExcessFunction"));

            return(returns? ctx_.AddLinker(result, (ctx, linkNode, newNode, model) =>
            {
                MethodDeclarationSyntax mthod = (MethodDeclarationSyntax)linkNode;
                ControlFlowAnalysis cfa = model.AnalyzeControlFlow(mthod.Body);

                ITypeSymbol rt = null;
                foreach (var rs in cfa.ReturnStatements)
                {
                    ReturnStatementSyntax rss = (ReturnStatementSyntax)rs;
                    ITypeSymbol type = model.GetSpeculativeTypeInfo(rss.Expression.SpanStart, rss.Expression, SpeculativeBindingOption.BindAsExpression).Type;

                    if (type == null)
                    {
                        continue;
                    }

                    if (type.TypeKind == TypeKind.Error)
                    {
                        return newNode;
                    }

                    if (rt == null)
                    {
                        rt = type;
                    }
                    else if (rt != type)
                    {
                        return newNode;
                    }
                }

                if (rt == null)
                {
                    return newNode;
                }

                MethodDeclarationSyntax res = (MethodDeclarationSyntax)newNode;
                return res.WithReturnType(SyntaxFactory.ParseTypeName(rt.Name));
            }) : result);
        }
예제 #10
0
        public int GetNesting(MethodDeclarationSyntax method)
        {
            var set = new HashSet<int>() { 0 };

            var nodes = method.DescendantNodes().OfType<StatementSyntax>().Where(IsEnlargersNesting).ToList();
            int c = 0;

            for (var j = 0; j < nodes.Count; )
            {
                var list = new List<Tuple<SyntaxNode, int>> { Tuple.Create((SyntaxNode)nodes[j], 1) };
                for (var i = 0; i < list.Count; ++i)
                {
                    //Console.WriteLine("Родитель = {0} ,  {1}   -   {2}  !!!!!!!!!", list[i].Item1, list[i].Item2, list[i].Item1.GetType());

                    var li = list[i].Item1.ChildNodes().OfType<SyntaxNode>();
                    set.Add(list[i].Item2);
                    foreach (var statementSyntax in li)
                    {
                        //Console.Write("Ребёнок = ");
                        //Console.WriteLine(statementSyntax);

                        if (IsEnlargersNesting(statementSyntax))
                        {
                            list.Add(Tuple.Create(statementSyntax, list[i].Item2 + 1));
                        }
                        else
                        {
                            if (!(statementSyntax is LiteralExpressionSyntax || statementSyntax is ExpressionStatementSyntax || statementSyntax is IdentifierNameSyntax || statementSyntax is BinaryExpressionSyntax))
                                list.Add(Tuple.Create(statementSyntax, list[i].Item2));
                        }
                        //Console.WriteLine("++++++++++++");
                    }
                    //list.AddRange(li.Where(ii => !(ii is ExpressionStatementSyntax || ii is LiteralExpressionSyntax)));

                    // Console.WriteLine("###########");
                }
                var count = list.Count(ii => IsEnlargersNesting(ii.Item1));
                // Console.WriteLine(count.ToString());
                //Console.WriteLine("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n\n");
                // j++;
                j += count;
            }

            return set.Last();
        }
예제 #11
0
        /// <inheritdoc cref="Tester.Solution"/>
        public override bool StaticTest(MethodDeclarationSyntax testMethod)
        {
            if (!testMethod.DescendantNodes().Any(node => node.IsKind(SyntaxKind.AddExpression) ||
                                                  node.IsKind(SyntaxKind.AddAssignmentExpression)))
            {
                WriteLineColor("TestMethod do not contains any add expresion", ConsoleColor.Red);

                return(false);
            }

            if (!testMethod.DescendantNodes().Any(node => node.IsKind(SyntaxKind.SubtractExpression) ||
                                                  node.IsKind(SyntaxKind.SubtractAssignmentExpression)))
            {
                WriteLineColor("TestMethod do not contains any subtract expresion", ConsoleColor.Red);

                return(false);
            }

            if (!testMethod.DescendantNodes().Any(node => node.IsKind(SyntaxKind.DivideExpression) ||
                                                  node.IsKind(SyntaxKind.DivideAssignmentExpression)))
            {
                WriteLineColor("TestMethod do not contains any divide expresion", ConsoleColor.Red);

                return(false);
            }

            if (!testMethod.DescendantNodes().Any(node => node.IsKind(SyntaxKind.MultiplyExpression) ||
                                                  node.IsKind(SyntaxKind.MultiplyAssignmentExpression)))
            {
                WriteLineColor("TestMethod do not contains any multiply expresion", ConsoleColor.Red);

                return(false);
            }

            if (!testMethod.DescendantNodes().Any(node => node.IsKind(SyntaxKind.ModuloExpression) ||
                                                  node.IsKind(SyntaxKind.ModuloAssignmentExpression)))
            {
                WriteLineColor("TestMethod do not contains any modulo expresion", ConsoleColor.Red);

                return(false);
            }

            if (!testMethod.DescendantNodes().Any(node => node.IsKind(SyntaxKind.OrAssignmentExpression) ||
                                                  node.IsKind(SyntaxKind.BitwiseOrExpression)))
            {
                WriteLineColor("TestMethod do not contains any OR expresion", ConsoleColor.Red);

                return(false);
            }

            if (!testMethod.DescendantNodes().Any(node => node.IsKind(SyntaxKind.AndAssignmentExpression) ||
                                                  node.IsKind(SyntaxKind.BitwiseAndExpression)))
            {
                WriteLineColor("TestMethod do not contains any AND expresion", ConsoleColor.Red);

                return(false);
            }

            if (!testMethod.DescendantNodes().Any(node => node.IsKind(SyntaxKind.ExclusiveOrExpression) ||
                                                  node.IsKind(SyntaxKind.ExclusiveOrAssignmentExpression)))
            {
                WriteLineColor("TestMethod do not contains any AND expresion", ConsoleColor.Red);

                return(false);
            }

            if (!testMethod.DescendantNodes().Any(node => node.IsKind(SyntaxKind.LeftShiftAssignmentExpression) ||
                                                  node.IsKind(SyntaxKind.LeftShiftExpression) ||
                                                  node.IsKind(SyntaxKind.RightShiftAssignmentExpression) ||
                                                  node.IsKind(SyntaxKind.RightShiftAssignmentExpression)))
            {
                WriteLineColor("TestMethod do not contains any shift expresion", ConsoleColor.Red);

                return(false);
            }

            return(true);
        }
            public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
            {
                BlockSyntax accessorBlock = node.DescendantNodes().OfType<BlockSyntax>().FirstOrDefault();

                Symbol symbol = _semanticModel.GetDeclaredSymbol(node);

                if (symbol != null)
                {
                    _members.Add(new MemberInfo(node, symbol, _semanticModel, new List<BlockSyntax>()
                    {
                        accessorBlock
                    }));
                }

                base.VisitMethodDeclaration(node);
            }
        public MethodDefinition(
            ClassDefinition parentClass,
            MethodDeclarationSyntax methodSyntax)
        {
            ParentClass  = parentClass;
            MethodSyntax = methodSyntax;

            Name = MethodSyntax.Identifier.ValueText.Trim();

            Options = new MethodOptions();

            var attributes = MethodSyntax.DescendantNodes().OfType <AttributeListSyntax>().SelectMany(x => x.Attributes).ToList();


            //Ignore generator attribute

            var ignoreAttribute = attributes.SingleOrDefault(x => x.Name.ToFullString().MatchesAttribute(NoClientAttribute.AttributeName));

            if (ignoreAttribute != null)
            {
                IsNotEndpoint = true;
                return;
            }


            //Route Attribute

            var routeAttribute = attributes.SingleOrDefault(x => x.Name.ToFullString().MatchesAttribute(Constants.Route));

            if (routeAttribute != null)            //Fetch route from RouteAttribute
            {
                Options.Route = routeAttribute.ArgumentList.Arguments.ToFullString().Replace("\"", "").Trim();
            }


            //HTTP Attribute

            var knownHttpAttributes = new List <string>
            {
                $"{Constants.Http}{HttpAttributeType.Delete}",
                $"{Constants.Http}{HttpAttributeType.Get}",
                $"{Constants.Http}{HttpAttributeType.Patch}",
                $"{Constants.Http}{HttpAttributeType.Post}",
                $"{Constants.Http}{HttpAttributeType.Put}",
            };

            var httpAttribute = attributes.SingleOrDefault(x => knownHttpAttributes.Any(y => x.Name.ToFullString().MatchesAttribute(y)));

            if (httpAttribute == null)
            {
                IsNotEndpoint = true;
                return;
            }

            Options.HttpType = (HttpAttributeType)Enum.Parse(typeof(HttpAttributeType),
                                                             httpAttribute.Name
                                                             .ToFullString()
                                                             .Replace(Constants.Http, "")
                                                             .Replace(Constants.Attribute, ""));

            if (Options.Route == null && httpAttribute.ArgumentList != null)            //If Route was never fetched from RouteAttribute or if they used the Http(template) override
            {
                Options.Route = httpAttribute.ArgumentList.Arguments.ToFullString().Replace("\"", "").Trim();
            }
            else if (Options.Route == null)
            {
                Options.Route = string.Empty;
            }

            //Obsolete Attribute
            var obsoleteAttribute = attributes.SingleOrDefault(x => x.Name.ToFullString().MatchesAttribute(Constants.Obsolete));

            if (obsoleteAttribute != null)
            {
                Options.Obsolete = obsoleteAttribute.ArgumentList.Arguments.ToFullString().Replace("\"", "").Trim();
            }

            //Authorize Attribute
            Options.Authorize = attributes.SingleOrDefault(x => x.Name.ToFullString().MatchesAttribute(Constants.Authorize)) != null;


            //Response types
            var responseTypes = attributes.Where(x => x.Name.ToFullString().MatchesAttribute(Constants.ProducesResponseType));

            Responses = responseTypes.Select(x => new ResponseTypeDefinition(x)).ToList();
            Responses.Add(new ResponseTypeDefinition(true));

            Parameters = MethodSyntax.ParameterList.Parameters.Select(x => new ParameterDefinition(x, FullRouteTemplate)).ToList();


            ParameterHeaders = attributes.Where(x => x.Name.ToFullString().MatchesAttribute(HeaderParameterAttribute.AttributeName))
                               .Select(x => new ParameterHeaderDefinition(x))
                               .ToList();

            Headers = attributes.Where(x => x.Name.ToFullString().MatchesAttribute(IncludeHeaderAttribute.AttributeName))
                      .Select(x => new HeaderDefinition(x))
                      .ToList();

            Options.ActionResultReturn = MethodSyntax.ReturnType.ToFullString().Contains(Constants.IActionResult);

            Options.ReturnType = MethodSyntax.ReturnType?.ToFullString();
            if (!Options.ActionResultReturn)
            {
                var regex = new Regex(@"(ValueTask|Task|ActionResult)<(.+)>");
                var match = regex.Match(Options.ReturnType);
                if (match.Success)
                {
                    Options.ReturnType = match.Groups[2].Value;
                }

                Options.ReturnType = Options.ReturnType.Trim();


                if (Options.ReturnType == "void" || Options.ReturnType == "Task")
                {
                    Options.ReturnType = null;
                }
            }
            else
            {
                Options.ReturnType = null;
            }
        }
예제 #14
0
 private static IEnumerable<SyntaxNode> GetSyntaxesNodesIncreaseNesting(MethodDeclarationSyntax methodDeclaration)
 {
     return methodDeclaration.DescendantNodes()
         .OfType<SyntaxNode>().Where(IsEnlargersNesting);
 }
예제 #15
0
        /// <summary>
        /// Get summary.
        /// </summary>
        /// <param name="theSyntaxNode">The syntax node to add the summary.</param>
        /// <returns>The syntax list.</returns>
        private static DocumentationCommentTriviaSyntax GetSummary(MethodDeclarationSyntax theSyntaxNode)
        {
            var summaryStart = XmlElementStartTag(XmlName(Identifier(Constants.Summary)))
                               .WithLessThanToken(Token(SyntaxKind.LessThanToken))
                               .WithGreaterThanToken(Token(SyntaxKind.GreaterThanToken)).NormalizeWhitespace();

            var summaryEnd = XmlElementEndTag(XmlName(Identifier(Constants.Summary))).NormalizeWhitespace()
                             .WithLessThanSlashToken(Token(SyntaxKind.LessThanSlashToken))
                             .WithGreaterThanToken(Token(SyntaxKind.GreaterThanToken));

            var summaryComment = " " + Convert.Method(theSyntaxNode);

            var summaryText = SingletonList <XmlNodeSyntax>(
                XmlText().NormalizeWhitespace()
                .WithTextTokens(
                    TokenList(
                        XmlTextNewLine(TriviaList(), Environment.NewLine, Environment.NewLine, TriviaList()).NormalizeWhitespace(),
                        XmlTextLiteral(
                            TriviaList(DocumentationCommentExterior("///")),
                            summaryComment,
                            summaryComment,
                            TriviaList()).NormalizeWhitespace(),
                        XmlTextNewLine(TriviaList(), Environment.NewLine, Environment.NewLine, TriviaList()).NormalizeWhitespace(),
                        XmlTextLiteral(
                            TriviaList(DocumentationCommentExterior("///")),
                            " ",
                            " ",
                            TriviaList()))).NormalizeWhitespace());

            var xmlComment = XmlText()
                             .WithTextTokens(
                TokenList(
                    XmlTextLiteral(
                        TriviaList(DocumentationCommentExterior("///")),
                        " ",
                        " ",
                        TriviaList()))).NormalizeWhitespace();

            var newLine = XmlText().WithTextTokens(TokenList(XmlTextNewLine(TriviaList(), Environment.NewLine, Environment.NewLine, TriviaList()))).NormalizeWhitespace();

            var summaryElement = XmlElement(summaryStart, summaryEnd).WithContent(summaryText);

            var list = List(new XmlNodeSyntax[] { xmlComment, summaryElement, newLine });

            // Add parameter comments
            if (theSyntaxNode.ParameterList.Parameters.Any())
            {
                foreach (var parameter in theSyntaxNode.ParameterList.Parameters)
                {
                    list = list.AddRange(
                        List(
                            new XmlNodeSyntax[]
                    {
                        xmlComment,

                        XmlElement(
                            XmlElementStartTag(XmlName(Identifier("param")))
                            .WithAttributes(
                                SingletonList <XmlAttributeSyntax>(
                                    XmlNameAttribute(
                                        XmlName(Identifier(TriviaList(Space), "name", TriviaList())),
                                        Token(SyntaxKind.DoubleQuoteToken),
                                        IdentifierName(parameter.Identifier.ValueText),
                                        Token(SyntaxKind.DoubleQuoteToken)))),
                            XmlElementEndTag(XmlName(Identifier("param"))))
                        .WithContent(
                            SingletonList <XmlNodeSyntax>(
                                XmlText()
                                .WithTextTokens(
                                    TokenList(
                                        XmlTextLiteral(
                                            TriviaList(),
                                            Convert.Parameter(parameter.Identifier.ValueText, parameter.Type.ToString()),
                                            "comment",
                                            TriviaList()))))),

                        newLine
                    }));
                }
            }

            // Add returns comments
            var returntype = theSyntaxNode.ReturnType.ToString();

            if (returntype != "void")
            {
                list = list.AddRange(
                    List(
                        new XmlNodeSyntax[]
                {
                    xmlComment,

                    XmlElement(XmlElementStartTag(XmlName(Identifier("returns"))), XmlElementEndTag(XmlName(Identifier("returns"))))
                    .WithContent(
                        SingletonList <XmlNodeSyntax>(
                            XmlText().WithTextTokens(TokenList(XmlTextLiteral(TriviaList(), Convert.Returns(returntype), "comment", TriviaList()))))),

                    newLine
                }));
            }

            // Add typeparams comments
            if (theSyntaxNode.TypeParameterList != null)
            {
                if (theSyntaxNode.TypeParameterList.Parameters.Any())
                {
                    foreach (var parameter in theSyntaxNode.TypeParameterList.Parameters)
                    {
                        list = list.AddRange(
                            List(
                                new XmlNodeSyntax[]
                        {
                            xmlComment,

                            XmlElement(
                                XmlElementStartTag(XmlName(Identifier("typeparam")))
                                .WithAttributes(
                                    SingletonList <XmlAttributeSyntax>(
                                        XmlNameAttribute(
                                            XmlName(Identifier(TriviaList(Space), "name", TriviaList())),
                                            Token(SyntaxKind.DoubleQuoteToken),
                                            IdentifierName(parameter.Identifier.ValueText),
                                            Token(SyntaxKind.DoubleQuoteToken)))),
                                XmlElementEndTag(XmlName(Identifier("typeparam"))))
                            .WithContent(
                                SingletonList <XmlNodeSyntax>(
                                    XmlText()
                                    .WithTextTokens(
                                        TokenList(
                                            XmlTextLiteral(
                                                TriviaList(),
                                                string.Empty,
                                                "comment",
                                                TriviaList()))))),

                            newLine
                        }));
                    }
                }
            }

            // Add exceptions comments
            var throws = theSyntaxNode.DescendantNodes().OfType <ThrowStatementSyntax>();

            foreach (var syntax in throws)
            {
                if (syntax.ChildNodes().OfType <ObjectCreationExpressionSyntax>().Any())
                {
                    var identifier   = syntax.DescendantNodes().OfType <IdentifierNameSyntax>().FirstOrDefault();
                    var argumentList = syntax.DescendantNodes().OfType <ArgumentListSyntax>().FirstOrDefault();
                    var parms        = argumentList.DescendantTokens().Where(x => x.IsKind(SyntaxKind.StringLiteralToken)).ToList();
                    var parmText     = string.Empty;

                    if (parms.Any())
                    {
                        parmText = parms.Last().ValueText;
                    }

                    list = list.AddRange(
                        List(
                            new XmlNodeSyntax[]
                    {
                        xmlComment,

                        XmlElement(
                            XmlElementStartTag(XmlName(Identifier("exception")))
                            .WithAttributes(
                                SingletonList <XmlAttributeSyntax>(
                                    XmlNameAttribute(
                                        XmlName(Identifier(TriviaList(Space), "cref", TriviaList())),
                                        Token(SyntaxKind.DoubleQuoteToken),
                                        IdentifierName(identifier.Identifier.ValueText),
                                        Token(SyntaxKind.DoubleQuoteToken)))),
                            XmlElementEndTag(XmlName(Identifier("exception"))))
                        .WithContent(
                            SingletonList <XmlNodeSyntax>(
                                XmlText()
                                .WithTextTokens(
                                    TokenList(
                                        XmlTextLiteral(
                                            TriviaList(),
                                            parmText,
                                            "comment",
                                            TriviaList()))))),

                        newLine
                    }));
                }
            }

            return(DocumentationCommentTrivia(SyntaxKind.SingleLineDocumentationCommentTrivia, list));
        }
        /// Search for all "Find" methods within the given method body.
        /// <param name="context">context for a symbol action.</param>
        /// <param name="method">within which method body to search for "Find" methods.</param>
        /// <param name="searched">
        ///     methods that we have already checked,
        ///     <method symbol, if this method is the "Find" method we are looking for>.
        /// </param>
        /// <param name="isRoot">whether the given method is the root method.</param>
        /// <returns>
        ///     all invocation methods within the given method and all its child methods (methods to be called within the parent
        ///     method body),
        ///     that are considered as "Find" methods we are looking for.
        /// </returns>
        private IEnumerable <ExpressionSyntax> SearchForFindCalls(SyntaxNodeAnalysisContext context,
                                                                  MethodDeclarationSyntax method,
                                                                  IDictionary <IMethodSymbol, bool> searched, bool isRoot)
        {
            // find all invocation expressions in the given method body
            var invocations = method.DescendantNodes().OfType <InvocationExpressionSyntax>();

            // iterate with all invocation expressions
            foreach (var invocation in invocations)
            {
                // try to get the simbol info for the invoking expression
                SymbolInfo symbolInfo;
                if (!context.TryGetSymbolInfo(invocation, out symbolInfo))
                {
                    continue;
                }

                // if the invoking expression is a method
                var methodSymbol = symbolInfo.Symbol as IMethodSymbol;
                if (methodSymbol != null)
                {
                    // if this method was already considered as "Find" method, return it
                    if (searched.ContainsKey(methodSymbol))
                    {
                        if (searched[methodSymbol])
                        {
                            yield return(invocation);
                        }
                    }
                    else
                    {
                        // if this is a method we are looking for (Find method), return it
                        if (FindMethodNames.Contains(methodSymbol.Name) &&
                            ContainingSymbols.Contains(methodSymbol.ContainingSymbol.ToString()))
                        {
                            searched.Add(methodSymbol, true);
                            yield return(invocation);
                        }
                        // this method is not the "Find" method we are looking for, but it might be a user-defined function,
                        // and it might contain child-methods which have "Find" methods inside
                        else
                        {
                            var methodDeclarations = methodSymbol.DeclaringSyntaxReferences;

                            // let's assume this method does not contain "Find" calls within its body
                            searched.Add(methodSymbol, false);

                            // check all invocation expressions within this method
                            foreach (var methodDeclaration in methodDeclarations)
                            {
                                var theMethodSyntax = methodDeclaration.GetSyntax() as MethodDeclarationSyntax;
                                if (theMethodSyntax != null)
                                {
                                    // check whether this child method has any "Find" invocation inside
                                    var childFindCallers =
                                        SearchForFindCalls(context, theMethodSyntax, searched, false);
                                    if (childFindCallers != null && childFindCallers.Any())
                                    {
                                        // update the searched directionary with new info
                                        // (although the parent method does not contain any "Find" method we are looking for,
                                        //  one of its child method has at least one "Find" method inside)
                                        searched[methodSymbol] = true;

                                        // cache all first-level method calls that have "Find" calls we are looking for
                                        // (here, we only care about "Find" methods in the first-level child methods, with all deeper child methods being ignored)
                                        if (isRoot)
                                        {
                                            _indirectCallers.Add(invocation, childFindCallers.First());
                                        }

                                        // this is the method we are interested in
                                        yield return(invocation);

                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
예제 #17
0
        //TODO: Try to simplify this method - it's very hard to follow
        private IEnumerable <ExpressionSyntax> SearchForFindCalls(SyntaxNodeAnalysisContext context,
                                                                  MethodDeclarationSyntax method, IDictionary <IMethodSymbol, bool> searched, bool isRoot)
        {
            var invocations = method.DescendantNodes().OfType <InvocationExpressionSyntax>();

            foreach (var invocation in invocations)
            {
                SymbolInfo symbolInfo;
                if (!context.TryGetSymbolInfo(invocation, out symbolInfo))
                {
                    continue;
                }

                var methodSymbol = symbolInfo.Symbol as IMethodSymbol;

                if (methodSymbol != null)
                {
                    if (searched.ContainsKey(methodSymbol))
                    {
                        if (searched[methodSymbol])
                        {
                            yield return(invocation);
                        }
                    }
                    else
                    {
                        if (FindMethodNames.Contains(methodSymbol.Name) &&
                            ContainingSymbols.Contains(methodSymbol.ContainingSymbol.ToString()))
                        {
                            searched.Add(methodSymbol, true);
                            yield return(invocation);
                        }
                        else
                        {
                            var methodDeclarations = methodSymbol.DeclaringSyntaxReferences;

                            searched.Add(methodSymbol, false); //let's assume there won't be any calls

                            foreach (var methodDeclaration in methodDeclarations)
                            {
                                var theMethodSyntax = methodDeclaration.GetSyntax() as MethodDeclarationSyntax;

                                if (theMethodSyntax != null)
                                {
                                    var childFindCallers = SearchForFindCalls(context, theMethodSyntax, searched, false);

                                    if (childFindCallers != null && childFindCallers.Any())
                                    {
                                        searched[methodSymbol] = true; //update the searched dictionary with new info

                                        if (isRoot)
                                        {
                                            _indirectCallers.Add(invocation, childFindCallers.First());
                                        }

                                        yield return(invocation);

                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
예제 #18
0
        private static AspNetCoreHttpEndpoint ReadMethodAsHttpEndpoint(AspNetCoreHttpController parent, MethodDeclarationSyntax syntax)
        {
            var attributes = syntax.DescendantNodes().OfType <AttributeListSyntax>().SelectMany(x => x.Attributes).ToList();

            var endpoint = new AspNetCoreHttpEndpoint(parent);

            endpoint.Name          = syntax.Identifier.ValueText.Trim();
            endpoint.FormattedName = syntax.Identifier.ValueText.CleanMethodName();


            endpoint.Virtual  = syntax.Modifiers.Any(x => x.Text == "virtual");
            endpoint.Override = syntax.Modifiers.Any(x => x.Text == "override");
            endpoint.New      = syntax.Modifiers.Any(x => x.Text == "new");


            //Ignore generator attribute
            endpoint.Ignored = attributes.HasAttribute <NotGeneratedAttribute>();


            //Route Attribute

            var routeAttribute = attributes.GetAttribute <RouteAttribute>();

            if (routeAttribute != null)            //Fetch route from RouteAttribute
            {
                endpoint.Route = new HttpRoute(routeAttribute.GetAttributeValue());
            }


            //HTTP Attribute
            var knownHttpAttributes = new List <string>
            {
                $"{Constants.Http}{HttpAttributeType.Delete}",
                $"{Constants.Http}{HttpAttributeType.Get}",
                $"{Constants.Http}{HttpAttributeType.Patch}",
                $"{Constants.Http}{HttpAttributeType.Post}",
                $"{Constants.Http}{HttpAttributeType.Put}",
            };

            var httpAttribute = attributes.SingleOrDefault(x => knownHttpAttributes.Any(y => x.Name.ToFullString().MatchesAttribute(y)));

            if (httpAttribute == null)
            {
                endpoint.Ignored = true;
            }
            else
            {
                var httpType = (HttpAttributeType)Enum.Parse(typeof(HttpAttributeType),
                                                             httpAttribute.Name
                                                             .ToFullString()
                                                             .Replace(Constants.Http, "")
                                                             .Replace(Constants.Attribute, ""));

                endpoint.HttpType = Helpers.HttpMethodFromEnum(httpType);
            }



            if (endpoint.Route == null && httpAttribute?.ArgumentList != null)            //If Route was never fetched from RouteAttribute or if they used the Http(template) override
            {
                endpoint.Route = new HttpRoute(httpAttribute.GetAttributeValue());
            }

            //Ignore method if it doesn't have a route or http attribute
            if (endpoint.Route == null && httpAttribute == null)
            {
                endpoint.Ignored = true;
                return(endpoint);
            }

            if (endpoint.Route == null)
            {
                endpoint.Route = new HttpRoute(string.Empty);
            }

            var versionAttribute = attributes.GetAttribute <ApiVersionAttribute>();

            if (versionAttribute != null)
            {
                var version = new ApiVersionDefinition(versionAttribute);

                var versionConstraint = endpoint.Route.Constraints.OfType <ApiVersionContraint>().SingleOrDefault();
                if (versionConstraint != null)
                {
                    endpoint.Route.Version = new Framework.AspNetCoreHttp.Routes.ApiVersion(version.Version, false);
                }
                else
                {
                    endpoint.Route.Version = new Framework.AspNetCoreHttp.Routes.ApiVersion(version.Version, true);
                }
            }


            if (endpoint.Route.Version != null && parent.Route.Version != null)
            {
                throw new NotSupportedException($"Endpoint {parent.Name}.{endpoint.FormattedName} has {nameof(ApiVersionAttribute)} on both it's method and class");
            }

            //Obsolete Attribute
            var obsoleteAttribute = attributes.GetAttribute <ObsoleteAttribute>();

            if (obsoleteAttribute != null)
            {
                endpoint.Obsolete        = true;
                endpoint.ObsoleteMessage = obsoleteAttribute.GetAttributeValue();
            }

            //Authorize Attribute
            endpoint.IsSecured = attributes.HasAttribute <AuthorizeAttribute>();


            //Response types
            var responseTypes = attributes.GetAttributes <ProducesResponseTypeAttribute>();
            var responses     = responseTypes.Select(x => new ResponseTypeDefinition(x)).ToList();

            responses.Add(new ResponseTypeDefinition(true));

            endpoint.ResponseTypes = responses.Select(x => new ResponseType(x.Type, Helpers.EnumParse <HttpStatusCode>(x.StatusValue))).ToList();

            var duplicateResponseTypes = endpoint.GetResponseTypes().GroupBy(x => x.Status).Where(x => x.Count() > 1).ToList();

            if (duplicateResponseTypes.Any())
            {
                throw new NotSupportedException($"Endpoint has multiple response types of the same status defined. {string.Join(", ", duplicateResponseTypes.Select(x => x.Key?.ToString()))}");
            }
            //Add after so we don't get duplicate error from the null Status
            endpoint.ResponseTypes.Add(new ExceptionResponseType());



            var parameters = syntax.ParameterList.Parameters.Select(x => new ParameterDefinition(x, endpoint.GetFullRoute(parent))).ToList();


            var routeParams = parameters.Where(x => x.Options.FromRoute).Select(x => new RouteParameter(x.RouteName, x.Type, x.Default)).ToList();
            var queryParams = parameters.Where(x => x.Options.FromQuery).Select(x => new QueryParameter(x.Options.QueryName, x.Type, x.Default, x.Options.QueryObject)).ToList();
            var bodyParam   = parameters.Where(x => x.Options.FromBody).Select(x => new BodyParameter(x.Name, x.Type, x.Default)).SingleOrDefault();


            endpoint.Parameters = routeParams.Cast <IParameter>().Union(queryParams).Union(new List <IParameter> {
                bodyParam
            }).NotNull().ToList();

            endpoint.Parameters.Add(new CancellationTokenModifier());
            endpoint.Parameters.Add(new CookieModifier());
            endpoint.Parameters.Add(new HeadersModifier());
            endpoint.Parameters.Add(new TimeoutModifier());
            if (endpoint.IsSecured)
            {
                endpoint.Parameters.Add(new SecurityModifier());
            }


            var parameterHeaders = attributes.GetAttributes <HeaderParameterAttribute>()
                                   .Select(x => new ParameterHeaderDefinition(x))
                                   .ToList();

            endpoint.ParameterHeader = parameterHeaders.Select(x => new ParameterHeader(x.Name, x.Type, x.DefaultValue)).ToList();



            var headers = attributes.GetAttributes <IncludeHeaderAttribute>()
                          .Select(x => new HeaderDefinition(x))
                          .ToList();

            endpoint.ConstantHeader = headers.Select(x => new ConstantHeader(x.Name, x.Value)).ToList();


            var rawReturnType = syntax.ReturnType?.ToFullString();

            var returnType = Helpers.GetTypeFromString(rawReturnType.Trim());

            while (returnType.IsContainerReturnType())
            {
                returnType = returnType.Arguments.SingleOrDefault();
            }

            if (Helpers.IsType(typeof(IActionResult).FullName, returnType?.Name))
            {
                returnType = null;
            }

            if (returnType?.Name == "void" ||
                (Helpers.IsType(typeof(Task).FullName, returnType?.Name) && (!returnType?.Arguments.Any() ?? false)))
            {
                returnType = null;
            }

            if (returnType.IsFileReturnType())
            {
                returnType             = new Helpers.TypeString(typeof(Stream).FullName);
                endpoint.ReturnsStream = true;
            }

            rawReturnType = returnType?.ToString();

            endpoint.ReturnType = rawReturnType?.Trim();

            var okStatus = endpoint.ResponseTypes.SingleOrDefault(x => x.Status == HttpStatusCode.OK);

            if (okStatus != null &&
                endpoint.ReturnType != null &&
                Helpers.IsType(okStatus.ActionType, endpoint.ReturnType))
            {
                //Remove the OkStatus since it is the same as the method return
                endpoint.ResponseTypes.Remove(okStatus);
            }
            else if (okStatus != null &&
                     endpoint.ReturnType != null &&
                     !Helpers.IsType(okStatus.ActionType, endpoint.ReturnType))
            {
                throw new NotSupportedException($"Endpoint {parent.Name}.{endpoint.FormattedName} has a OK response type of {okStatus.ActionType}, but the method return {endpoint.ReturnType}");
            }

            var duplicateParameters = endpoint.GetParametersWithoutResponseTypes().GroupBy(x => x.Name).Where(x => x.Count() > 1).ToList();

            if (duplicateParameters.Any())
            {
                throw new NotSupportedException($"Endpoint {parent.Name}.{endpoint.FormattedName} has multiple parameters of the same name defined. {string.Join(", ", duplicateParameters.Select(x => x.Key?.ToString()))}");
            }

            var invalidParameters = endpoint.GetParameters().Where(x => !Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsValidIdentifier(x.Name)).ToList();

            if (invalidParameters.Any())
            {
                throw new NotSupportedException($"Endpoint {parent.Name}.{endpoint.FormattedName} has parameters that are invalid variable names. {string.Join(", ", invalidParameters.Select(x => x.Name))}");
            }

            var fullRoute = endpoint.GetFullRoute(parent);

            if (fullRoute?.Version?.Query ?? false)
            {
                endpoint.Parameters.Add(new QueryParameter($"api-version={fullRoute?.Version}"));
            }


            return(endpoint);
        }
 public static bool InvokesExpression(this MethodDeclarationSyntax methodDeclaration, ExpressionSyntax expression) =>
 methodDeclaration?
 .DescendantNodes <InvocationExpressionSyntax>()
 .Any(invocationExpression => invocationExpression.Expression.IsEquivalentWhenNormalized(expression)) ?? false;
예제 #20
0
        private static HubEndpoint ReadMethodAsHubEndpoint(HubController parent, MethodDeclarationSyntax syntax)
        {
            var attributes = syntax.DescendantNodes().OfType <AttributeListSyntax>().SelectMany(x => x.Attributes).ToList();

            var endpoint = new HubEndpoint(parent);

            endpoint.Name = syntax.Identifier.ValueText.CleanMethodName();


            endpoint.Virtual  = syntax.Modifiers.Any(x => x.Text == "virtual");
            endpoint.Override = syntax.Modifiers.Any(x => x.Text == "override");
            endpoint.New      = syntax.Modifiers.Any(x => x.Text == "new");


            //Ignore generator attribute
            endpoint.Ignored = attributes.HasAttribute <NotGeneratedAttribute>();

            //Obsolete Attribute
            var obsoleteAttribute = attributes.GetAttribute <ObsoleteAttribute>();

            if (obsoleteAttribute != null)
            {
                endpoint.Obsolete        = true;
                endpoint.ObsoleteMessage = obsoleteAttribute.GetAttributeValue();
            }

            //Response types
            var messageAttributes = attributes.GetAttributes <ProducesMessageAttribute>();
            var messages          = messageAttributes.Select(x => new MessageDefinition(x)).ToList();

            endpoint.Messages = messages.Select(x => new Message(x.Name, x.Types)).ToList();

            var duplicateMessages = endpoint.Messages.GroupBy(x => x.Name).Where(x => x.Count() > 1 && !x.All(y => y.Types.SequenceEqual(x.First().Types))).ToList();

            if (duplicateMessages.Any())
            {
                throw new NotSupportedException($"Hub has the same message with different parameters defined on different endpoints. {string.Join(", ", duplicateMessages.Select(x => x.Key?.ToString()))}");
            }



            var parameters = syntax.ParameterList.Parameters.Select(x => new HubParameterDefinition(x)).ToList();
            var hubParams  = parameters.Select(x => new HubParameter(x.Name, x.Type, x.Default)).ToList();

            endpoint.Parameters = hubParams.Cast <IParameter>().NotNull().ToList();

            var duplicateParameters = endpoint.GetParameters().GroupBy(x => x.Name).Where(x => x.Count() > 1).ToList();

            if (duplicateParameters.Any())
            {
                throw new NotSupportedException($"Endpoint has multiple parameters of the same name defined. {string.Join(", ", duplicateParameters.Select(x => x.Key?.ToString()))}");
            }

            var invalidParameters = endpoint.GetParameters().Where(x => !Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsValidIdentifier(x.Name)).ToList();

            if (invalidParameters.Any())
            {
                throw new NotSupportedException($"Endpoint {parent.Name}.{endpoint.Name} has parameters that are invalid variable names. {string.Join(", ", invalidParameters.Select(x => x.Name))}");
            }


            var rawReturnType = syntax.ReturnType?.ToFullString();

            var returnType = Helpers.GetTypeFromString(rawReturnType.Trim());

            while (returnType.IsContainerReturnType())
            {
                returnType = returnType.Arguments.SingleOrDefault();
            }

            if (Helpers.IsType(typeof(ChannelReader <>).FullName.CleanGenericTypeDefinition(), returnType?.Name))
            {
                endpoint.Channel     = true;
                endpoint.ChannelType = returnType.Arguments.SingleOrDefault().ToString();
            }


            return(endpoint);
        }
예제 #21
0
        // returns a list of all Invocations in a given method
        public List <Invocation> GetInvocations(MethodDeclarationSyntax methodDecl)
        {
            // output list
            List <Invocation> allPossibleInvocations = new List <Invocation>();

            // get all invocation expressions
            List <InvocationExpressionSyntax> invocationExpressions = methodDecl.DescendantNodes().OfType <InvocationExpressionSyntax>().ToList();

            if (!invocationExpressions.Any())
            {
                return(allPossibleInvocations);
            }

            // transform each invocation to the struct Invocation(callee, line number)
            List <Invocation> directInvocations = new List <Invocation>();

            foreach (InvocationExpressionSyntax invocExpr in invocationExpressions)
            {
                List <int> lines = new List <int> {
                    invocExpr.GetLocation().GetLineSpan().StartLinePosition.Line
                };                                      // the line number of the invocation, NOTE: the line numbers start at 0!
                List <MethodDeclarationSyntax> methods; // the invoced method
                SymbolInfo symbInfo = semMod.GetSymbolInfo(invocExpr);
                if (symbInfo.Symbol != null)
                {
                    methods = new List <MethodDeclarationSyntax> {
                        GetMethodDeclSyntax(symbInfo.Symbol as IMethodSymbol)
                    };
                }
                else
                {
                    methods = new List <MethodDeclarationSyntax> {
                        GetMethodDeclSyntax(symbInfo.CandidateSymbols.FirstOrDefault() as IMethodSymbol)
                    };
                }
                directInvocations.Add(new Invocation(lines, methods));
            }

            // remove non user defined callee's which are null
            directInvocations.RemoveAll(invoc => invoc.Methods.FirstOrDefault() == null);

            if (!directInvocations.Any())
            {
                return(allPossibleInvocations);
            }

            // remove duplicate invocations but add the line numbers to the unique one
            directInvocations = (from invoc in directInvocations
                                 group invoc.Lines by invoc.Methods.First() into grouped
                                 select new Invocation(grouped.SelectMany(l => l).ToList(), new List <MethodDeclarationSyntax> {
                grouped.Key
            })).ToList();

            foreach (Invocation invoc in directInvocations)
            {
                if (invoc.Methods.First().Parent is InterfaceDeclarationSyntax)
                {
                    // if method is declared in an interface add the list of all implementing methods instead + each overrides
                    List <MethodDeclarationSyntax> possibleInvocations = new List <MethodDeclarationSyntax>();
                    List <MethodDeclarationSyntax> implemMeths         = GetInterfaceMethodImplementingMethods(invoc.Methods.First());
                    foreach (MethodDeclarationSyntax implemMeth in implemMeths)
                    {
                        possibleInvocations.AddRange(GetOverridingMethodsAndSelf(implemMeth));
                    }
                    allPossibleInvocations.Add(new Invocation(invoc.Lines, possibleInvocations));
                }
                else
                {
                    // add the method itself and the overrides
                    allPossibleInvocations.Add(new Invocation(invoc.Lines, GetOverridingMethodsAndSelf(invoc.Methods.First())));
                }
            }

            return(allPossibleInvocations);
        }
예제 #22
0
        public static FunctionEndpoint ReadMethodAsFunction(MethodDeclarationSyntax syntax, HostJson hostData)
        {
            var attributes = syntax.DescendantNodes().OfType <AttributeListSyntax>().SelectMany(x => x.Attributes).ToList();

            var endpoint = new FunctionEndpoint();

            try
            {
                var endpointName = attributes.GetAttribute <FunctionNameAttribute>();

                if (endpointName == null)
                {
                    endpoint.Ignored = true;
                    return(endpoint);
                }

                endpoint.Name = endpointName.GetAttributeValue();

                //Ignore generator attribute
                endpoint.Ignored = attributes.HasAttribute <NotGeneratedAttribute>();


                //Obsolete Attribute
                var obsoleteAttribute = attributes.GetAttribute <ObsoleteAttribute>();
                if (obsoleteAttribute != null)
                {
                    endpoint.Obsolete        = true;
                    endpoint.ObsoleteMessage = obsoleteAttribute.GetAttributeValue();
                }

                //Response types
                var responseTypes = attributes.GetAttributes <ProducesResponseTypeAttribute>();
                var responses     = responseTypes.Select(x => new ResponseTypeDefinition(x)).ToList();
                responses.Add(new ResponseTypeDefinition(true));

                endpoint.ResponseTypes = responses.Select(x => new ResponseType(x.Type, Helpers.EnumParse <HttpStatusCode>(x.StatusValue))).ToList();

                var duplicateResponseTypes = endpoint.GetResponseTypes().GroupBy(x => x.Status).Where(x => x.Count() > 1).ToList();

                if (duplicateResponseTypes.Any())
                {
                    throw new NotSupportedException($"Endpoint has multiple response types of the same status defined. {string.Join(", ", duplicateResponseTypes.Select(x => x.Key?.ToString()))}");
                }
                //Add after so we don't get duplicate error from the null Status
                endpoint.ResponseTypes.Add(new ExceptionResponseType());


                //Need to check if the function has a HttpTrigger
                var httpTriggerAttribute = syntax.ParameterList.Parameters.SingleOrDefault(x => x.AttributeLists.SelectMany(y => y.Attributes).HasAttribute <HttpTriggerAttribute>());

                if (httpTriggerAttribute == null)
                {
                    endpoint.Ignored = true;
                    return(endpoint);
                }

                var triggerAttribute = new HttpTriggerParameter(httpTriggerAttribute);

                endpoint.SupportedMethods = triggerAttribute.Methods;

                var routePrefix = hostData?.http?.routePrefix ?? "api";

                if (triggerAttribute.Route != null)
                {
                    var route = triggerAttribute.Route.TrimStart('/');

                    if (!string.IsNullOrEmpty(routePrefix))
                    {
                        if (!route.StartsWith(routePrefix))
                        {
                            route = $"{routePrefix}/" + route;
                        }

                        route = "/" + route;
                    }

                    endpoint.Route = new HttpRoute(route);
                }
                else
                {
                    if (!string.IsNullOrEmpty(routePrefix))
                    {
                        endpoint.Route = new HttpRoute($"{routePrefix}/{endpoint.Name}");
                    }
                    else
                    {
                        endpoint.Route = new HttpRoute($"{endpoint.Name}");
                    }
                }


                var expectedBodyParameters = attributes.GetAttributes <ExpectedBodyParameterAttribute>()
                                             .Select(x => new ExpectedBodyParamterDefinition(x))
                                             .GroupBy(x => x.Method)
                                             .ToDictionary(x => x.Key, y => y.Select(z => (IParameter) new BodyParameter("body", z.Type, null)));

                var expectedQueryParameters = attributes.GetAttributes <ExpectedQueryParameterAttribute>()
                                              .Select(x => new ExpectedQueryParamterDefinition(x))
                                              .GroupBy(x => x.Method)
                                              .ToDictionary(x => x.Key, y => y.Select(z => (IParameter) new QueryParameter(z.Name, z.Type, null, z.IsQueryObject)));

                endpoint.HttpParameters = expectedBodyParameters.Union(expectedQueryParameters).ToDictionary();

                var parameters = syntax.ParameterList.Parameters.Select(x => new ParameterDefinition(x, endpoint.GetFullRoute())).ToList();

                var routeParams = parameters.Where(x => x.Options.FromRoute).Select(x => new RouteParameter(x.RouteName, x.Type, x.Default)).ToList();

                endpoint.Parameters = routeParams.Cast <IParameter>().NotNull().ToList();

                endpoint.Parameters.Add(new CancellationTokenModifier());
                endpoint.Parameters.Add(new CookieModifier());
                endpoint.Parameters.Add(new HeadersModifier());
                endpoint.Parameters.Add(new TimeoutModifier());

                if (triggerAttribute.AuthLevel == AuthorizationLevel.User)
                {
                    if (!endpoint.ResponseTypes.Any(x => x.Status == HttpStatusCode.Unauthorized))
                    {
                        endpoint.ResponseTypes.Add(new ResponseType(HttpStatusCode.Unauthorized));
                    }

                    endpoint.Parameters.Add(new SecurityModifier());
                }
                else if (triggerAttribute.AuthLevel == AuthorizationLevel.Anonymous)
                {
                }
                else
                {
                    if (!endpoint.ResponseTypes.Any(x => x.Status == HttpStatusCode.Unauthorized))
                    {
                        endpoint.ResponseTypes.Add(new ResponseType(HttpStatusCode.Unauthorized));
                    }

                    endpoint.Parameters.Add(new FunctionAuthModifier());
                }


                var parameterHeaders = attributes.GetAttributes <HeaderParameterAttribute>()
                                       .Select(x => new ParameterHeaderDefinition(x))
                                       .ToList();
                endpoint.ParameterHeader = parameterHeaders.Select(x => new ParameterHeader(x.Name, x.Type, x.DefaultValue)).ToList();



                var headers = attributes.GetAttributes <IncludeHeaderAttribute>()
                              .Select(x => new HeaderDefinition(x))
                              .ToList();
                endpoint.ConstantHeader = headers.Select(x => new ConstantHeader(x.Name, x.Value)).ToList();


                var rawReturnType = syntax.ReturnType?.ToFullString();

                var returnType = Helpers.GetTypeFromString(rawReturnType.Trim());

                while (returnType.IsContainerReturnType())
                {
                    returnType = returnType.Arguments.SingleOrDefault();
                }

                if (Helpers.IsType(typeof(IActionResult).FullName, returnType?.Name))
                {
                    returnType = null;
                }

                if (returnType?.Name == "void" ||
                    (Helpers.IsType(typeof(Task).FullName, returnType?.Name) && (!returnType?.Arguments.Any() ?? false)))
                {
                    returnType = null;
                }

                if (returnType.IsFileReturnType())
                {
                    returnType             = new Helpers.TypeString(typeof(Stream).FullName);
                    endpoint.ReturnsStream = true;
                }

                rawReturnType = returnType?.ToString();

                endpoint.ReturnType = rawReturnType?.Trim();

                foreach (var method in endpoint.SupportedMethods)
                {
                    var duplicateParameters = endpoint.GetParametersWithoutResponseTypesForHttpMethod(method).GroupBy(x => x.Name).Where(x => x.Count() > 1).ToList();

                    if (duplicateParameters.Any())
                    {
                        throw new NotSupportedException($"Function has multiple parameters of the same name defined. {string.Join(", ", duplicateParameters.Select(x => x.Key?.ToString()))}");
                    }


                    var invalidParameters = endpoint.GetParametersForHttpMethod(method).Where(x => !Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsValidIdentifier(x.Name)).ToList();

                    if (invalidParameters.Any())
                    {
                        throw new NotSupportedException($"Function {endpoint.Name} has parameters that are invalid variable names. {string.Join(", ", invalidParameters.Select(x => x.Name))}");
                    }
                }
            }
            catch (NotSupportedException nse)
            {
                if (endpoint.Ignored)
                {
                    return(endpoint);
                }

                endpoint.Failed = true;
                endpoint.Error  = nse.Message;
            }
#if !DEBUG
            catch (Exception ex)
            {
                endpoint.Failed            = true;
                endpoint.UnexpectedFailure = true;
                endpoint.Error             = ex.ToString();
            }
#endif


            return(endpoint);
        }
예제 #23
0
        //TODO: Try to simplify this currMethodSyntax - it's very hard to follow
        private IEnumerable <ExpressionSyntax> SearchForTargetExpression(SyntaxNodeAnalysisContext context,
                                                                         IMethodSymbol checkMethodSymbol,
                                                                         MethodDeclarationSyntax currMethodSyntax, IDictionary <ISymbol, bool> searchedSymbol, bool isRoot)
        {
            foreach (var oneTargetExp in currMethodSyntax.DescendantNodes().OfType <InvocationExpressionSyntax>())
            {
                /*
                 * var oneTargetIdentifierExps = oneTargetExp.DescendantNodes().OfType<IdentifierNameSyntax>();
                 * foreach(var oneTargetIdentifierExp in oneTargetIdentifierExps)
                 * {
                 * }
                 */

                bool isContainedByBranch = false;

                SyntaxNode parent = oneTargetExp;
                while (parent != currMethodSyntax)
                {
                    if (parent is IfStatementSyntax || parent is SwitchStatementSyntax || parent is ForStatementSyntax ||
                        parent is ForEachStatementSyntax || parent is WhileStatementSyntax)
                    {
                        isContainedByBranch = true;
                        break;
                    }

                    parent = parent.Parent;
                }

                if (isContainedByBranch)
                {
                    continue;
                }

                bool hasPrecedingReturn = false;

                foreach (var oneReturnSyntax in currMethodSyntax.DescendantNodes().OfType <ReturnStatementSyntax>())
                {
                    if (oneReturnSyntax.SpanStart < oneTargetExp.SpanStart)
                    {
                        hasPrecedingReturn = true;
                        break;
                    }
                }
                if (hasPrecedingReturn)
                {
                    continue;
                }

                SymbolInfo oneSymbolInfo;
                if (!context.TryGetSymbolInfo(oneTargetExp, out oneSymbolInfo))
                {
                    continue;
                }

                var targetSymbol = oneSymbolInfo.Symbol as IMethodSymbol;
                if (targetSymbol != null)
                {
                    if (searchedSymbol.ContainsKey(targetSymbol))
                    {
                        if (searchedSymbol[targetSymbol])
                        {
                            yield return((ExpressionSyntax)oneTargetExp);
                        }
                    }
                    else
                    {
                        if (targetSymbol == checkMethodSymbol)
                        {
                            searchedSymbol.Add(targetSymbol, true);
                            if (isRoot)
                            {
                                _directCallers.Add(oneTargetExp, oneTargetExp);
                            }
                            yield return(oneTargetExp);
                        }
                    }
                }
            }


            var invocationExps = currMethodSyntax.DescendantNodes().OfType <InvocationExpressionSyntax>();

            foreach (var oneInvocationExp in invocationExps)
            {
                SymbolInfo oneSymbolInfo;
                if (!context.TryGetSymbolInfo(oneInvocationExp, out oneSymbolInfo))
                {
                    continue;
                }


                bool       isContainedByBranch = false;
                SyntaxNode parent = oneInvocationExp;
                while (parent != currMethodSyntax)
                {
                    if (parent is IfStatementSyntax || parent is SwitchStatementSyntax)
                    {
                        isContainedByBranch = true;
                        break;
                    }

                    parent = parent.Parent;
                }
                if (isContainedByBranch)
                {
                    continue;
                }

                var methodSymbol = oneSymbolInfo.Symbol as IMethodSymbol;

                if (methodSymbol != null)
                {
                    if (searchedSymbol.ContainsKey(methodSymbol))
                    {
                        if (searchedSymbol[methodSymbol])
                        {
                            yield return(oneInvocationExp);
                        }
                    }
                    else
                    {
                        var methodDeclarations = methodSymbol.DeclaringSyntaxReferences;
                        searchedSymbol.Add(methodSymbol, false); //let's assume there won't be any calls

                        foreach (var methodDeclaration in methodDeclarations)
                        {
                            var theMethodSyntax = methodDeclaration.GetSyntax() as MethodDeclarationSyntax;

                            if (theMethodSyntax != null)
                            {
                                var childResults = SearchForTargetExpression(context, checkMethodSymbol, theMethodSyntax, searchedSymbol, false);

                                if (childResults != null && childResults.Any())
                                {
                                    searchedSymbol[methodSymbol] = true; //update the searched dictionary with new info

                                    if (isRoot)
                                    {
                                        _indirectCallers.Add(oneInvocationExp, childResults.First());
                                    }

                                    yield return(oneInvocationExp);

                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }
 /// <summary>
 /// Returns true if the method throws exceptions or returns null.
 /// </summary>
 internal static bool ThrowsOrReturnsNull(this MethodDeclarationSyntax syntaxNode) =>
 syntaxNode.DescendantNodes().OfType <ThrowStatementSyntax>().Any() ||
 syntaxNode.DescendantNodes().OfType <ExpressionSyntax>().Any(expression => expression.IsKind(SyntaxKindEx.ThrowExpression)) ||
 syntaxNode.DescendantNodes().OfType <ReturnStatementSyntax>().Any(returnStatement => returnStatement.Expression.IsKind(SyntaxKind.NullLiteralExpression)) ||
 // For simplicity this returns true for any method witch contains a NullLiteralExpression but this could be a source of FNs
 syntaxNode.DescendantNodes().OfType <ExpressionSyntax>().Any(expression => expression.IsKind(SyntaxKind.NullLiteralExpression));
예제 #25
0
 private static IEnumerable<StatementSyntax> GetStatementSyntaxs(MethodDeclarationSyntax method)
 {
     return method.DescendantNodes()
                     .OfType<StatementSyntax>()
                     .Where(statement => !(statement is BlockSyntax));
 }
        static Action AnalyzeActionMethod(
            Compilation compilation,
            SemanticModel semanticModel,
            MethodDeclarationSyntax actionMethodDeclaration,
            Dictionary <string, Compilation> referencedSymbols)
        {
            // Get some symbols by full name.

            var httpGetSymbol = compilation.GetTypeByMetadataName("System.Web.Http.HttpGetAttribute");

            var httpPostSymbol = compilation.GetTypeByMetadataName("System.Web.Http.HttpPostAttribute");

            var httpPutSymbol = compilation.GetTypeByMetadataName("System.Web.Http.HttpPutAttribute");

            var httpDeleteSymbol = compilation.GetTypeByMetadataName("System.Web.Http.HttpDeleteAttribute");

            var routeSymbol = compilation.GetTypeByMetadataName("System.Web.Http.RouteAttribute");

            var fromBodySymbol = compilation.GetTypeByMetadataName("System.Web.Http.FromBodyAttribute");

            var fromUriSymbol = compilation.GetTypeByMetadataName("System.Web.Http.FromUriAttribute");

            var methodSymbol = semanticModel.GetDeclaredSymbol(actionMethodDeclaration);

            var action = new Action
            {
                DocumentationCommentId = methodSymbol.GetDocumentationCommentId(),

                Name = methodSymbol.Name,

                BodyParameters = new List <Parameter>(),

                QueryParameters = new List <Parameter>(),

                RouteParameters = new List <Parameter>(),

                Examples = new List <Example>(),

                Results = new List <Result>()
            };

            // Go through the method's XML comments.

            var methodCommentsXml = methodSymbol.GetDocumentationCommentXml();

            var parameterNotes = new Dictionary <string, XElement>();

            if (!string.IsNullOrWhiteSpace(methodCommentsXml))
            {
                var methodCommentsRoot = XElement.Parse(methodCommentsXml);

                var summary = methodCommentsRoot.Element("summary");

                if (summary != null)
                {
                    foreach (var cref in summary.GetCrefs())
                    {
                        if (!referencedSymbols.ContainsKey(cref))
                        {
                            referencedSymbols.Add(cref, compilation);
                        }
                    }

                    action.Summary = summary.ToMarkup();
                }

                var remarks = methodCommentsRoot.Element("remarks");

                if (remarks != null)
                {
                    foreach (var cref in remarks.GetCrefs())
                    {
                        if (!referencedSymbols.ContainsKey(cref))
                        {
                            referencedSymbols.Add(cref, compilation);
                        }
                    }

                    action.Remarks = remarks.ToMarkup();
                }

                var returns = methodCommentsRoot.Element("returns");

                if (returns != null)
                {
                    foreach (var cref in returns.GetCrefs())
                    {
                        if (!referencedSymbols.ContainsKey(cref))
                        {
                            referencedSymbols.Add(cref, compilation);
                        }
                    }

                    action.Returns = returns.ToMarkup();
                }

                foreach (var example in methodCommentsRoot.Elements("example"))
                {
                    action.Examples.Add(new Example
                    {
                        Label = example.Attribute("label").Value,

                        Content = string
                                  .Concat(example.Nodes())
                                  .NormalizeCodeIndentation()
                    });
                }

                parameterNotes = methodCommentsRoot
                                 .Elements("param")
                                 .ToDictionary(
                    p => p.Attribute("name").Value,
                    p => p);
            }

            var methodName = actionMethodDeclaration.Identifier.ValueText;

            var routeKeys = Enumerable.Empty <string>();

            // Go through the method's attributes.

            foreach (var attributeList in actionMethodDeclaration.AttributeLists)
            {
                foreach (var attribute in attributeList.Attributes)
                {
                    // For each attribute, try to pull a recognized value.

                    var attributeTypeInfo = semanticModel.GetTypeInfo(attribute);

                    if (attributeTypeInfo.Type.MetadataName == httpGetSymbol.MetadataName)
                    {
                        action.Method = "GET";
                    }
                    else if (attributeTypeInfo.Type.MetadataName == httpPostSymbol.MetadataName)
                    {
                        action.Method = "POST";
                    }
                    else if (attributeTypeInfo.Type.MetadataName == httpPutSymbol.MetadataName)
                    {
                        action.Method = "PUT";
                    }
                    else if (attributeTypeInfo.Type.MetadataName == httpDeleteSymbol.MetadataName)
                    {
                        action.Method = "DELETE";
                    }
                    else if (attributeTypeInfo.Type.MetadataName == routeSymbol.MetadataName)
                    {
                        var routeArgument = attribute
                                            .ArgumentList
                                            .Arguments
                                            .Single()
                                            .Expression as LiteralExpressionSyntax;

                        action.Route = routeArgument.Token.ValueText;

                        routeKeys = Regex
                                    .Matches(action.Route, "(?<={)[^}]+(?=})")
                                    .Cast <Match>()
                                    .Select(m => m.Value)
                                    .ToList();
                    }
                }
            }

            // Go through parameters

            foreach (var parameter in actionMethodDeclaration.ParameterList.Parameters)
            {
                var predefinedType = parameter.Type as PredefinedTypeSyntax;

                //var typeName = predefinedType != null ?
                //    predefinedType.Keyword.ValueText :
                //    (parameter.Type as SimpleNameSyntax).Identifier.ValueText;

                var typeInfo = semanticModel.GetTypeInfo(parameter.Type);

                var documentCommentId = typeInfo.Type.GetDocumentationCommentId();

                if (!string.IsNullOrWhiteSpace(documentCommentId) &&
                    !referencedSymbols.ContainsKey(documentCommentId))
                {
                    referencedSymbols.Add(documentCommentId, compilation);
                }

                var parameterType = semanticModel.GetTypeInfo(parameter.Type);

                var parameterName = parameter.Identifier.ValueText;

                var fromBody = false;

                var fromUri = false;

                foreach (var attributeList in parameter.AttributeLists)
                {
                    foreach (var attribute in attributeList.Attributes)
                    {
                        var attributeTypeInfo = semanticModel.GetTypeInfo(attribute);

                        if (attributeTypeInfo.Type.MetadataName == fromBodySymbol.MetadataName)
                        {
                            fromBody = true;
                        }
                        else if (attributeTypeInfo.Type.MetadataName == fromUriSymbol.MetadataName)
                        {
                            fromUri = true;
                        }
                    }
                }

                var outParameter = new Parameter
                {
                    Key = parameterName,

                    TypeName = typeInfo.Type.ToTypeDisplayName(),

                    TypeDocumentCommentId = typeInfo.Type.GetDocumentationCommentId(),

                    Optional = false, // TODO: not this

                    Notes = parameterNotes.ContainsKey(parameterName) ?
                            parameterNotes[parameterName].ToMarkup() :
                            null
                };

                if (routeKeys.Contains(parameterName))
                {
                    action.RouteParameters.Add(outParameter);
                }
                else if (fromUri || parameterType.Type.IsValueType)
                {
                    action.QueryParameters.Add(outParameter);
                }
                else if (fromBody || !parameterType.Type.IsValueType)
                {
                    action.BodyParameters.Add(outParameter);
                }
            }

            var returnStatements = actionMethodDeclaration
                                   .DescendantNodes()
                                   .OfType <ReturnStatementSyntax>()
                                   .ToList();

            foreach (var returnStatement in returnStatements)
            {
                var resultMethodInvocation = returnStatement
                                             .DescendantNodes()
                                             .OfType <InvocationExpressionSyntax>()
                                             .First();

                var invokedMethodSymbol = semanticModel
                                          .GetSymbolInfo(resultMethodInvocation.Expression)
                                          .Symbol as IMethodSymbol;

                var argumentSyntax = resultMethodInvocation
                                     .ArgumentList
                                     .Arguments
                                     .SingleOrDefault();

                var result = new Result
                {
                    Kind = invokedMethodSymbol.Name
                };

                if (argumentSyntax != null)
                {
                    var l = argumentSyntax.DescendantNodes().First();

                    var literalExpression = l as LiteralExpressionSyntax;

                    if (literalExpression != null)
                    {
                        result.ArgumentText = ScrubArgumentText(literalExpression.Token.ValueText);
                    }

                    var argumentSymbol = semanticModel.GetSymbolInfo(argumentSyntax);

                    var argumentTypeInfo = semanticModel.GetTypeInfo(argumentSyntax.Expression);

                    var typeName = argumentTypeInfo.Type.Name;

                    result.ArgumentType = argumentTypeInfo.Type.ToDisplayString();
                }

                action.Results.Add(result);
            }

            return(action);
        }