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); }
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); }
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); }
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); }
private static IEnumerable <LocalFunctionStatementAdapter> GetLocalFunctions(MethodDeclarationSyntax methodDeclaration) => methodDeclaration.DescendantNodes() .Where(member => member.IsKind(SyntaxKindEx.LocalFunctionStatement)) .Select(member => new LocalFunctionStatementAdapter((LocalFunctionStatementSyntaxWrapper)member));
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); }
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); } } } }
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); }
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(); }
/// <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; } }
private static IEnumerable<SyntaxNode> GetSyntaxesNodesIncreaseNesting(MethodDeclarationSyntax methodDeclaration) { return methodDeclaration.DescendantNodes() .OfType<SyntaxNode>().Where(IsEnlargersNesting); }
/// <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; } } } } } } } }
//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; } } } } } } } }
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;
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); }
// 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); }
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); }
//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));
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); }