public SyntaxNode Transform(IFunctionTransformationResult transformResult, ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata) { var methodResult = transformResult.AnalyzationResult; if (!methodResult.Conversion.HasFlag(MethodConversion.ToAsync)) { return(null); } var functionNode = transformResult.Transformed; if (functionNode.GetFunctionBody() == null) { return(Update(functionNode, methodResult, namespaceMetadata)); } if (methodResult.SplitTail || methodResult.PreserveReturnType || !methodResult.OmitAsync) { if (!methodResult.OmitAsync) { functionNode = functionNode.AddAsync(); } return(Update(functionNode, methodResult, namespaceMetadata)); } var rewriter = new ReturnTaskFunctionRewriter(transformResult, namespaceMetadata); functionNode = rewriter.Visit(functionNode); return(Update(functionNode, methodResult, namespaceMetadata)); }
public MethodTransformerResult Transform(IMethodOrAccessorTransformationResult transformResult, ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata) { var methodResult = transformResult.AnalyzationResult; if (!methodResult.Conversion.HasFlag(MethodConversion.ToAsync)) { return(MethodTransformerResult.Skip); } var methodNode = transformResult.Transformed; if (methodNode.GetFunctionBody() == null) { return(Update(methodNode, methodResult, namespaceMetadata)); } if (methodResult.SplitTail || methodResult.PreserveReturnType || !methodResult.OmitAsync) { if (!methodResult.OmitAsync) { methodNode = methodNode.AddAsync(); } return(Update(methodNode, methodResult, namespaceMetadata)); } var rewriter = new ReturnTaskFunctionRewriter(transformResult, namespaceMetadata); methodNode = (MethodDeclarationSyntax)rewriter.VisitMethodDeclaration(methodNode); return(Update(methodNode, methodResult, namespaceMetadata)); }
// TODO: helper method private static string GetLockFieldName(ISymbol symbol, bool isStatic, ITypeTransformationMetadata typeTransformMetadata, string prefix = "", string postfix = "") { var fieldName = GetLockFieldName(symbol, isStatic, prefix, postfix); var currentIdx = 2; var newFieldName = fieldName; while (typeTransformMetadata.MemberNames.Contains(newFieldName)) { newFieldName = fieldName + currentIdx; currentIdx++; } return(newFieldName); }
public MethodTransformerResult Transform(IMethodOrAccessorTransformationResult transformResult, ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata) { if (transformResult.AnalyzationResult.OmitAsync) { return(MethodTransformerResult.Skip); } var result = _rewriter.VisitMethod(transformResult.Transformed); return(result == null ? MethodTransformerResult.Skip : MethodTransformerResult.Update(result)); }
public MethodTransformerResult Transform(IMethodOrAccessorTransformationResult methodTransformResult, ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata) { if (!_isEnabled) { return(MethodTransformerResult.Skip); } var leadingTrivia = methodTransformResult.Transformed.GetLeadingTrivia(); var documentation = leadingTrivia.FirstOrDefault(o => o.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia)); var documentationNode = (DocumentationCommentTriviaSyntax)documentation.GetStructure(); var newDocumentationNode = documentationNode ?? DocumentationCommentTrivia(SyntaxKind.SingleLineDocumentationCommentTrivia); // Summary newDocumentationNode = ProcessTag( newDocumentationNode, methodTransformResult, "summary", _configuration.AddOrReplaceMethodSummary, _configuration.CanRemoveMethodSummary, true); // Remarks newDocumentationNode = ProcessTag( newDocumentationNode, methodTransformResult, "remarks", _configuration.AddOrReplaceMethodRemarks, _configuration.CanRemoveMethodRemarks, false); var transformed = methodTransformResult.Transformed; if (documentationNode != null) { transformed = transformed.ReplaceNode(documentationNode, newDocumentationNode); } else { // We have to append our documentation before the whitespace trivia var whitespaceIndex = leadingTrivia.IndexOf(SyntaxKind.WhitespaceTrivia); if (whitespaceIndex <= 0) { whitespaceIndex = 0; } transformed = transformed.WithLeadingTrivia(transformed.GetLeadingTrivia().Insert(whitespaceIndex, Trivia(newDocumentationNode))); } return(MethodTransformerResult.Update(transformed)); }
public MethodTransformerResult Transform(IMethodOrAccessorTransformationResult transformResult, ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata) { var methodResult = transformResult.AnalyzationResult; if (!methodResult.HasYields || !methodResult.Conversion.HasFlag(MethodConversion.ToAsync)) { return(MethodTransformerResult.Skip); } var methodNode = transformResult.Transformed; var rewriter = new YieldRewriter(transformResult); methodNode = (MethodDeclarationSyntax)rewriter.VisitMethodDeclaration(methodNode); return(MethodTransformerResult.Update(methodNode)); }
private SyntaxNode RunFunctionTransformers( FunctionTransformationResult functionTransform, ITypeTransformationMetadata transformResult, INamespaceTransformationMetadata namespaceMetadata) { if (functionTransform.Transformed == null) { return(null); } foreach (var transformer in _configuration.FunctionTransformers) { functionTransform.Transformed = transformer.Transform(functionTransform, transformResult, namespaceMetadata) ?? functionTransform.Transformed; } return(functionTransform.Transformed); }
public SyntaxNode Transform(IFunctionTransformationResult transformResult, ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata) { var methodResult = transformResult.AnalyzationResult; var functionNode = transformResult.Transformed; if ( !methodResult.Conversion.HasFlag(MethodConversion.ToAsync) || methodResult.OmitAsync || !methodResult.GetMethodOrAccessor().CancellationTokenRequired || functionNode.GetFunctionBody() == null) { return(null); } var rewriter = new OperationCanceledExceptionFunctionRewriter(transformResult.EndOfLineTrivia, namespaceMetadata); return(rewriter.Visit(functionNode)); }
public MethodTransformerResult Transform(IMethodOrAccessorTransformationResult transformResult, ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata) { var methodResult = transformResult.AnalyzationResult; var methodNode = transformResult.Transformed; if ( !methodResult.Conversion.HasFlag(MethodConversion.ToAsync) || methodResult.OmitAsync || !methodResult.CancellationTokenRequired || methodNode.GetFunctionBody() == null) { return(MethodTransformerResult.Skip); } var rewriter = new OperationCanceledExceptionFunctionRewriter(transformResult.EndOfLineTrivia, namespaceMetadata); methodNode = (MethodDeclarationSyntax)rewriter.VisitMethodDeclaration(methodNode); return(MethodTransformerResult.Update(methodNode)); }
/// <summary> /// The method with SplitTail needs to be splitted into two methods /// </summary> public MethodTransformerResult Transform(IMethodOrAccessorTransformationResult transformResult, ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata) { var methodResult = transformResult.AnalyzationResult; if (!methodResult.SplitTail) { return(MethodTransformerResult.Skip); } var methodNode = transformResult.Transformed; // Tail method body shall contain all statements after preconditions var skipStatements = methodResult.Preconditions.Count; // If a cancellation guard was generated we need to skip also that if (_analyzeConfiguration.UseCancellationTokens && _analyzeConfiguration.CancellationTokens.Guards && methodResult.CancellationTokenRequired) { skipStatements++; } var tailMethodBody = methodNode.Body .WithStatements(new SyntaxList <StatementSyntax>() .AddRange(methodNode.Body.Statements.Skip(skipStatements))); // Main method shall contain only preconditions and a call to the tail method var bodyStatements = new SyntaxList <StatementSyntax>() .AddRange(methodNode.Body.Statements.Take(skipStatements)); ParameterListSyntax tailCallParameterList; // TODO: handle name collisions var tailIdentifier = Identifier("Internal" + methodNode.Identifier.Value); // The transformed method has already the Async postfix MethodDeclarationSyntax tailMethod = null; LocalFunctionStatementSyntax tailFunction = null; if (_configuration.LocalFunctions) { tailFunction = LocalFunctionStatement( methodNode.ReturnType.WithoutLeadingTrivia(), tailIdentifier) .WithParameterList(ParameterList() .WithCloseParenToken(Token(TriviaList(), SyntaxKind.CloseParenToken, TriviaList(transformResult.EndOfLineTrivia)))) .AddAsync() .WithLeadingTrivia(transformResult.LeadingWhitespaceTrivia) .WithBody(tailMethodBody) .AppendIndent(transformResult.IndentTrivia.ToFullString()); // We do not need any parameter for the local function as we already have the parameters from the parent method tailCallParameterList = ParameterList(); } else { var tailMethodModifiers = TokenList( Token(TriviaList(transformResult.EndOfLineTrivia, transformResult.LeadingWhitespaceTrivia), SyntaxKind.PrivateKeyword, TriviaList(Space))); if (methodNode.Modifiers.Any(o => o.IsKind(SyntaxKind.StaticKeyword))) { tailMethodModifiers = tailMethodModifiers.Add(Token(TriviaList(), SyntaxKind.StaticKeyword, TriviaList(Space))); } tailMethod = methodNode .WithReturnType(methodNode.ReturnType.WithLeadingTrivia()) // Remove lead trivia in case the return type is the first node (eg. void Method()) .WithIdentifier(tailIdentifier) .WithModifiers(tailMethodModifiers) .AddAsync() .WithBody(tailMethodBody); // Tail call shall contain the cancellation token parameter tailCallParameterList = methodNode.ParameterList; } var tailCall = ReturnStatement( Token(TriviaList(transformResult.BodyLeadingWhitespaceTrivia), SyntaxKind.ReturnKeyword, TriviaList(Space)), IdentifierName(tailIdentifier).Invoke(tailCallParameterList), Token(TriviaList(), SyntaxKind.SemicolonToken, TriviaList(transformResult.EndOfLineTrivia)) ); bodyStatements = bodyStatements.Add(tailCall); // Append local function at the end for easier reading if (tailFunction != null) { bodyStatements = bodyStatements.Add(tailFunction); } methodNode = methodNode.WithBody(methodNode.Body .WithStatements(bodyStatements)); var result = MethodTransformerResult.Update(methodNode); if (tailMethod != null) { result.AddMethod(tailMethod); } return(result); }
private T TransformConditionalAccessToIfStatements <T>( T node, SimpleNameSyntax nameNode, ITypeTransformationMetadata typeMetadata, ConditionalAccessExpressionSyntax conditionalAccessNode, InvocationExpressionSyntax invokeNode) where T : SyntaxNode { var statement = (StatementSyntax)conditionalAccessNode.Ancestors().FirstOrDefault(o => o is StatementSyntax); if (statement == null || !(statement.Parent is BlockSyntax block)) { // TODO: convert arrow method/property/function to a normal one // TODO: convert to block if there is no block throw new NotSupportedException( $"Arrow method with null-conditional is not supported. Node: {conditionalAccessNode}"); } var fnName = nameNode.Identifier.ValueText; // TODO: handle name collisions var variableName = $"{char.ToLowerInvariant(fnName[0])}{fnName.Substring(1)}Task"; var leadingTrivia = statement.GetLeadingTrivia(); var newConditionalAccessNode = ConditionalAccessExpression( conditionalAccessNode.Expression, invokeNode) .WithTriviaFrom(conditionalAccessNode); var localVar = LocalDeclarationStatement( VariableDeclaration( IdentifierName(Identifier(leadingTrivia, "var", TriviaList(Space))), SingletonSeparatedList( VariableDeclarator( Identifier(TriviaList(), variableName, TriviaList(Space))) .WithInitializer( EqualsValueClause(newConditionalAccessNode.WithoutTrivia()) .WithEqualsToken(Token(TriviaList(), SyntaxKind.EqualsToken, TriviaList(Space))) ) ))) .WithSemicolonToken(Token(TriviaList(), SyntaxKind.SemicolonToken, TriviaList(typeMetadata.EndOfLineTrivia))); var index = block.Statements.IndexOf(statement); var variableAnnotation = Guid.NewGuid().ToString(); var newBlock = block.ReplaceNode(conditionalAccessNode, conditionalAccessNode.WhenNotNull.ReplaceNode(invokeNode, IdentifierName(variableName) .WithAdditionalAnnotations(new SyntaxAnnotation(variableAnnotation)) .WithLeadingTrivia(conditionalAccessNode.GetLeadingTrivia()) .WithTrailingTrivia(conditionalAccessNode.GetTrailingTrivia()) )); var variable = newBlock.GetAnnotatedNodes(variableAnnotation).OfType <IdentifierNameSyntax>().First(); newBlock = newBlock.ReplaceNode(variable, variable.AddAwait(_configuration.ConfigureAwaitArgument)); var ifBlock = Block() .WithOpenBraceToken( Token(TriviaList(leadingTrivia), SyntaxKind.OpenBraceToken, TriviaList(typeMetadata.EndOfLineTrivia))) .WithCloseBraceToken( Token(TriviaList(leadingTrivia), SyntaxKind.CloseBraceToken, TriviaList(typeMetadata.EndOfLineTrivia))) .WithStatements(SingletonList(newBlock.Statements[index].AppendIndent(typeMetadata.IndentTrivia.ToFullString()))); var ifStatement = IfStatement( BinaryExpression( SyntaxKind.NotEqualsExpression, IdentifierName(Identifier(TriviaList(), variableName, TriviaList(Space))), LiteralExpression(SyntaxKind.NullLiteralExpression)) .WithOperatorToken( Token(TriviaList(), SyntaxKind.ExclamationEqualsToken, TriviaList(Space))), ifBlock ) .WithIfKeyword( Token(TriviaList(leadingTrivia), SyntaxKind.IfKeyword, TriviaList(Space))) .WithCloseParenToken( Token(TriviaList(), SyntaxKind.CloseParenToken, TriviaList(typeMetadata.EndOfLineTrivia))); newBlock = block.WithStatements( block.Statements .RemoveAt(index) .InsertRange(index, new StatementSyntax[] { localVar, ifStatement }) ); return(node.ReplaceNode(block, newBlock)); }
private T TransformFunctionReference <T>(T node, IFunctionAnalyzationResult funcResult, FunctionReferenceTransformationResult transfromReference, ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata) where T : SyntaxNode { var nameNode = node.GetAnnotatedNodes(transfromReference.Annotation).OfType <SimpleNameSyntax>().First(); var funReferenceResult = transfromReference.AnalyzationResult; var bodyFuncReferenceResult = funReferenceResult as IBodyFunctionReferenceAnalyzationResult; var newNameNode = nameNode .WithIdentifier(Identifier(funReferenceResult.AsyncCounterpartName)) .WithTriviaFrom(nameNode); transfromReference.Transformed = newNameNode; var cancellationTokenParamName = funcResult.GetMethodOrAccessor().CancellationTokenRequired ? "cancellationToken" : null; // TODO: remove // If we have a cref change the name to the async counterpart and add/update arguments if (bodyFuncReferenceResult == null) { if (funReferenceResult.IsCref) { var crefNode = (NameMemberCrefSyntax)nameNode.Parent; var paramList = new List <CrefParameterSyntax>(); // If the cref has already the parameters set then use them if (crefNode.Parameters != null) { paramList.AddRange(crefNode.Parameters.Parameters); // If the external async counterpart has a cancellation token, add it if (funReferenceResult.AsyncCounterpartFunction == null && funReferenceResult.ReferenceSymbol.Parameters.Length < funReferenceResult.AsyncCounterpartSymbol.Parameters.Length) { paramList.Add(CrefParameter(IdentifierName(nameof(CancellationToken)))); } } else { // We have to add the parameters to avoid ambiguity var asyncSymbol = funReferenceResult.AsyncCounterpartSymbol; paramList.AddRange(asyncSymbol.Parameters .Select(o => CrefParameter(o.Type .CreateTypeSyntax(true, namespaceMetadata.AnalyzationResult.IsIncluded(o.Type.ContainingNamespace?.ToString()))))); } // If the async counterpart is internal and a token is required add a token parameter if (funReferenceResult.AsyncCounterpartFunction?.GetMethodOrAccessor()?.CancellationTokenRequired == true) { paramList.Add(CrefParameter(IdentifierName(nameof(CancellationToken)))); } node = node.ReplaceNestedNodes( crefNode.Parent as QualifiedCrefSyntax, crefNode, crefNode .ReplaceNode(nameNode, newNameNode) .WithParameters(CrefParameterList(SeparatedList(paramList))), rootNode => UpdateTypeAndRunReferenceTransformers(rootNode, funcResult, funReferenceResult, namespaceMetadata, (type, fullName) => rootNode.WithContainer(type.CreateTypeSyntax(true, fullName).WithTriviaFrom(rootNode.Container))), childNode => RunReferenceTransformers(childNode, funcResult, funReferenceResult, namespaceMetadata) ); } else if (funReferenceResult.IsNameOf) { node = node.ReplaceNestedNodes( nameNode.Parent as MemberAccessExpressionSyntax, nameNode, newNameNode, rootNode => UpdateTypeAndRunReferenceTransformers(rootNode, funcResult, funReferenceResult, namespaceMetadata, (type, fullName) => rootNode.WithExpression(type.CreateTypeSyntax(false, fullName).WithTriviaFrom(rootNode.Expression))), childNode => RunReferenceTransformers(childNode, funcResult, funReferenceResult, namespaceMetadata) ); } return(node); } // If we have a method passed as an argument we need to check if we have to wrap it inside a function if (bodyFuncReferenceResult.AsyncDelegateArgument != null) { if (bodyFuncReferenceResult.WrapInsideFunction) { // TODO: move to analyze step var argumentNode = nameNode.Ancestors().OfType <ArgumentSyntax>().First(); var delReturnType = (INamedTypeSymbol)bodyFuncReferenceResult.AsyncDelegateArgument.ReturnType; var returnType = bodyFuncReferenceResult.AsyncCounterpartSymbol.ReturnType; bool returnTypeMismatch; if (bodyFuncReferenceResult.ReferenceFunction != null) { var refMethod = bodyFuncReferenceResult.ReferenceFunction as IMethodAnalyzationResult; if (refMethod != null && refMethod.PreserveReturnType) { returnTypeMismatch = !delReturnType.Equals(returnType); // TODO Generics } else if (delReturnType.IsGenericType) // Generic Task { returnTypeMismatch = delReturnType.TypeArguments.First().IsAwaitRequired(returnType); } else { returnTypeMismatch = delReturnType.IsAwaitRequired(returnType); } } else { returnTypeMismatch = !delReturnType.Equals(returnType); // TODO Generics } var newArgumentExpression = argumentNode.Expression .ReplaceNestedNodes( nameNode.Parent as MemberAccessExpressionSyntax, nameNode, newNameNode, rootNode => UpdateTypeAndRunReferenceTransformers(rootNode, funcResult, funReferenceResult, namespaceMetadata, (type, fullName) => rootNode.WithExpression(type.CreateTypeSyntax(false, fullName))), childNode => RunReferenceTransformers(childNode, funcResult, funReferenceResult, namespaceMetadata) ) .WrapInsideFunction(bodyFuncReferenceResult.AsyncDelegateArgument, returnTypeMismatch, namespaceMetadata.TaskConflict, invocation => invocation.AddCancellationTokenArgumentIf(cancellationTokenParamName, bodyFuncReferenceResult)); node = node .ReplaceNode(argumentNode.Expression, newArgumentExpression); } else { node = node.ReplaceNestedNodes( nameNode.Parent as MemberAccessExpressionSyntax, nameNode, newNameNode, rootNode => UpdateTypeAndRunReferenceTransformers(rootNode, funcResult, funReferenceResult, namespaceMetadata, (type, fullName) => rootNode.WithExpression(type.CreateTypeSyntax(false, fullName))), childNode => RunReferenceTransformers(childNode, funcResult, funReferenceResult, namespaceMetadata) ); } return(node); } InvocationExpressionSyntax invokeNode = null; var isAccessor = bodyFuncReferenceResult.ReferenceSymbol.IsAccessor(); if (!isAccessor && funReferenceResult.ReferenceNode.IsKind(SyntaxKind.InvocationExpression)) { invokeNode = nameNode.Ancestors().OfType <InvocationExpressionSyntax>().First(); } if (!bodyFuncReferenceResult.AwaitInvocation) { // An arrow method does not have a statement var statement = nameNode.Ancestors().OfType <StatementSyntax>().FirstOrDefault(); var statementInParentFunction = nameNode.Ancestors().TakeWhile(o => !o.Equals(statement)).Any(o => o.IsFunction()); var newNode = (SyntaxNode)statement ?? node; if (invokeNode != null) { newNode = newNode.ReplaceNestedNodes( invokeNode, nameNode, newNameNode, rootNode => UpdateTypeAndRunReferenceTransformers(rootNode .AddCancellationTokenArgumentIf(cancellationTokenParamName, bodyFuncReferenceResult), funcResult, funReferenceResult, namespaceMetadata, (memberNode, type, fullName) => memberNode.WithExpression(type.CreateTypeSyntax(true, fullName).WithTriviaFrom(memberNode.Expression))) ); } else if (isAccessor) { newNode = ConvertAccessor(newNode, nameNode, newNameNode, cancellationTokenParamName, bodyFuncReferenceResult, invNode => UpdateTypeAndRunReferenceTransformers(invNode, funcResult, funReferenceResult, namespaceMetadata, (memberNode, type, fullName) => memberNode.WithExpression(type.CreateTypeSyntax(true, fullName).WithTriviaFrom(memberNode.Expression)))); } else { newNode = newNode.ReplaceNestedNodes( nameNode.Parent as MemberAccessExpressionSyntax, nameNode, newNameNode, rootNode => UpdateTypeAndRunReferenceTransformers(rootNode, funcResult, funReferenceResult, namespaceMetadata, (type, fullName) => rootNode.WithExpression(type.CreateTypeSyntax(false, fullName).WithTriviaFrom(rootNode.Expression))), childNode => RunReferenceTransformers(childNode, funcResult, funReferenceResult, namespaceMetadata) ); } if (statement != null && !statement.IsKind(SyntaxKind.LocalFunctionStatement)) { // Skip adding return statement for arrow functions if (bodyFuncReferenceResult.UseAsReturnValue && !statementInParentFunction) { newNode = ((StatementSyntax)newNode).ToReturnStatement(); } node = node .ReplaceNode(statement, newNode); } else { node = (T)newNode; } } else { // We need to annotate the invocation node because of the AddAwait method as it needs the parent node var invokeAnnotation = Guid.NewGuid().ToString(); if (isAccessor) { node = ConvertAccessor(node, nameNode, newNameNode, cancellationTokenParamName, bodyFuncReferenceResult, invNode => UpdateTypeAndRunReferenceTransformers(invNode, funcResult, funReferenceResult, namespaceMetadata, (memberNode, type, fullName) => memberNode.WithExpression(type.CreateTypeSyntax(true, fullName).WithTriviaFrom(memberNode.Expression))) .WithAdditionalAnnotations(new SyntaxAnnotation(invokeAnnotation)) ); } else { node = node.ReplaceNestedNodes( invokeNode, nameNode, newNameNode, rootNode => UpdateTypeAndRunReferenceTransformers(rootNode .AddCancellationTokenArgumentIf(cancellationTokenParamName, bodyFuncReferenceResult), funcResult, funReferenceResult, namespaceMetadata, (memberNode, type, fullName) => memberNode.WithExpression(type.CreateTypeSyntax(true, fullName).WithTriviaFrom(memberNode.Expression))) .WithAdditionalAnnotations(new SyntaxAnnotation(invokeAnnotation)) ); } invokeNode = node.GetAnnotatedNodes(invokeAnnotation).OfType <InvocationExpressionSyntax>().First(); // Check if the invocation has a ?. var conditionalAccessNode = invokeNode.Ancestors() .TakeWhile(o => !(o is StatementSyntax)) .OfType <ConditionalAccessExpressionSyntax>() .FirstOrDefault(o => o.WhenNotNull.Contains(invokeNode)); if (conditionalAccessNode != null) // ?. syntax { // We have to find out which strategy to use, if we have a non assignable expression, we are force to use if statements // otherwise a ternary condition will be used if (!conditionalAccessNode.Parent.IsKind(SyntaxKind.ExpressionStatement) || !invokeNode.Equals(conditionalAccessNode.WhenNotNull)) { node = TransformConditionalAccessToConditionalExpressions(node, nameNode, funReferenceResult, typeMetadata, conditionalAccessNode, invokeNode); } else { node = TransformConditionalAccessToIfStatements(node, nameNode, typeMetadata, conditionalAccessNode, invokeNode); } } else { node = node.ReplaceNode(invokeNode, invokeNode.AddAwait(_configuration.ConfigureAwaitArgument)); } } return(node); }
internal FieldTransformationResult TransformField( BaseFieldDeclarationSyntax fieldNode, bool canCopy, FieldTransformationResult result, ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata) { var analyzeResult = result.AnalyzationResult; var startRootSpan = analyzeResult.Node.SpanStart; // Calculate whitespace method trivias result.EndOfLineTrivia = fieldNode.GetEndOfLine(); result.LeadingWhitespaceTrivia = fieldNode.GetLeadingWhitespace(); result.IndentTrivia = fieldNode.GetIndent(result.LeadingWhitespaceTrivia, typeMetadata.LeadingWhitespaceTrivia); if (analyzeResult.Variables.All(o => o.Conversion == FieldVariableConversion.Ignore || o.Conversion == FieldVariableConversion.Copy && !canCopy)) { return(result); } fieldNode = fieldNode.WithAdditionalAnnotations(new SyntaxAnnotation(result.Annotation)); startRootSpan -= fieldNode.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 analyzeResult.TypeReferences.Where(o => o.TypeAnalyzationResult.Conversion == TypeConversion.NewType)) { var reference = typeReference.ReferenceLocation; var refSpanStart = reference.Location.SourceSpan.Start - startRootSpan; var nameNode = fieldNode.GetSimpleName(refSpanStart, reference.Location.SourceSpan.Length, typeReference.IsCref); var annotation = Guid.NewGuid().ToString(); fieldNode = fieldNode.ReplaceNode(nameNode, nameNode.WithAdditionalAnnotations(new SyntaxAnnotation(annotation))); typeReferencesAnnotations.Add(annotation); } foreach (var variableResult in analyzeResult.Variables) { var spanStart = variableResult.Node.SpanStart - startRootSpan; var spanLength = variableResult.Node.Span.Length; var accessorNode = fieldNode.DescendantNodes() .First(o => o.SpanStart == spanStart && o.Span.Length == spanLength); var transformedNode = new FieldVariableTransformationResult(variableResult); result.TransformedVariables.Add(transformedNode); fieldNode = fieldNode.ReplaceNode(accessorNode, accessorNode.WithAdditionalAnnotations(new SyntaxAnnotation(transformedNode.Annotation))); } // Modify references foreach (var refAnnotation in typeReferencesAnnotations) { var nameNode = fieldNode.GetAnnotatedNodes(refAnnotation).OfType <SimpleNameSyntax>().First(); fieldNode = fieldNode .ReplaceNode(nameNode, nameNode.WithIdentifier(Identifier(nameNode.Identifier.Value + "Async").WithTriviaFrom(nameNode.Identifier))); } foreach (var fieldVariableResult in result.TransformedVariables.OrderByDescending(o => o.OriginalStartSpan)) { var variableNode = (VariableDeclaratorSyntax)fieldNode.GetAnnotatedNodes(fieldVariableResult.Annotation) .First(); var variableTransformResult = TransformFieldVariable(variableNode, fieldVariableResult, canCopy); fieldNode = variableTransformResult.Transformed == null ? fieldNode.RemoveNode(variableNode, SyntaxRemoveOptions.KeepUnbalancedDirectives) : fieldNode.ReplaceNode(variableNode, variableTransformResult.Transformed); } result.Transformed = fieldNode; return(result); }
private T TransformFunctionReference <T>(T node, IFunctionAnalyzationResult funcResult, FunctionReferenceTransformationResult transfromReference, ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata) where T : SyntaxNode { var nameNode = node.GetAnnotatedNodes(transfromReference.Annotation).OfType <SimpleNameSyntax>().First(); var funReferenceResult = transfromReference.AnalyzationResult; var bodyFuncReferenceResult = funReferenceResult as IBodyFunctionReferenceAnalyzationResult; var newNameNode = nameNode .WithIdentifier(Identifier(funReferenceResult.AsyncCounterpartName)) .WithTriviaFrom(nameNode); transfromReference.Transformed = newNameNode; var cancellationTokenParamName = funcResult.GetMethodOrAccessor().CancellationTokenRequired ? "cancellationToken" : null; // TODO: remove // If we have a cref change the name to the async counterpart and add/update arguments if (bodyFuncReferenceResult == null) { if (funReferenceResult.IsCref) { var crefNode = (NameMemberCrefSyntax)nameNode.Parent; var paramList = new List <CrefParameterSyntax>(); // If the cref has already the parameters set then use them if (crefNode.Parameters != null) { paramList.AddRange(crefNode.Parameters.Parameters); // If the external async counterpart has a cancellation token, add it if (funReferenceResult.AsyncCounterpartFunction == null && funReferenceResult.ReferenceSymbol.Parameters.Length < funReferenceResult.AsyncCounterpartSymbol.Parameters.Length) { paramList.Add(CrefParameter(IdentifierName(nameof(CancellationToken)))); } } else { // We have to add the parameters to avoid ambiguity var asyncSymbol = funReferenceResult.AsyncCounterpartSymbol; paramList.AddRange(asyncSymbol.Parameters .Select(o => CrefParameter(o.Type .CreateTypeSyntax(true, namespaceMetadata.AnalyzationResult.IsIncluded(o.Type.ContainingNamespace?.ToString()))))); } // If the async counterpart is internal and a token is required add a token parameter if (funReferenceResult.AsyncCounterpartFunction?.GetMethodOrAccessor()?.CancellationTokenRequired == true) { paramList.Add(CrefParameter(IdentifierName(nameof(CancellationToken)))); } node = node.ReplaceNestedNodes( crefNode.Parent as QualifiedCrefSyntax, crefNode, crefNode .ReplaceNode(nameNode, newNameNode) .WithParameters(CrefParameterList(SeparatedList(paramList))), rootNode => UpdateTypeAndRunReferenceTransformers(rootNode, funcResult, funReferenceResult, namespaceMetadata, (type, fullName) => rootNode.WithContainer(type.CreateTypeSyntax(true, fullName).WithTriviaFrom(rootNode.Container))), childNode => RunReferenceTransformers(childNode, funcResult, funReferenceResult, namespaceMetadata) ); } else if (funReferenceResult.IsNameOf) { node = node.ReplaceNestedNodes( nameNode.Parent as MemberAccessExpressionSyntax, nameNode, newNameNode, rootNode => UpdateTypeAndRunReferenceTransformers(rootNode, funcResult, funReferenceResult, namespaceMetadata, (type, fullName) => rootNode.WithExpression(type.CreateTypeSyntax(false, fullName).WithTriviaFrom(rootNode.Expression))), childNode => RunReferenceTransformers(childNode, funcResult, funReferenceResult, namespaceMetadata) ); } return(node); } // If we have a method passed as an argument we need to check if we have to wrap it inside a function if (bodyFuncReferenceResult.AsyncDelegateArgument != null) { if (bodyFuncReferenceResult.WrapInsideFunction) { // TODO: move to analyze step var argumentNode = nameNode.Ancestors().OfType <ArgumentSyntax>().First(); var delReturnType = (INamedTypeSymbol)bodyFuncReferenceResult.AsyncDelegateArgument.ReturnType; var returnType = bodyFuncReferenceResult.AsyncCounterpartSymbol.ReturnType; bool returnTypeMismatch; if (bodyFuncReferenceResult.ReferenceFunction != null) { var refMethod = bodyFuncReferenceResult.ReferenceFunction as IMethodAnalyzationResult; if (refMethod != null && refMethod.PreserveReturnType) { returnTypeMismatch = !delReturnType.Equals(returnType); // TODO Generics } else if (delReturnType.IsGenericType) // Generic Task { returnTypeMismatch = delReturnType.TypeArguments.First().IsAwaitRequired(returnType); } else { returnTypeMismatch = delReturnType.IsAwaitRequired(returnType); } } else { returnTypeMismatch = !delReturnType.Equals(returnType); // TODO Generics } var newArgumentExpression = argumentNode.Expression .ReplaceNestedNodes( nameNode.Parent as MemberAccessExpressionSyntax, nameNode, newNameNode, rootNode => UpdateTypeAndRunReferenceTransformers(rootNode, funcResult, funReferenceResult, namespaceMetadata, (type, fullName) => rootNode.WithExpression(type.CreateTypeSyntax(false, fullName))), childNode => RunReferenceTransformers(childNode, funcResult, funReferenceResult, namespaceMetadata) ) .WrapInsideFunction(bodyFuncReferenceResult.AsyncDelegateArgument, returnTypeMismatch, namespaceMetadata.TaskConflict, invocation => invocation.AddCancellationTokenArgumentIf(cancellationTokenParamName, bodyFuncReferenceResult)); node = node .ReplaceNode(argumentNode.Expression, newArgumentExpression); } else { node = node.ReplaceNestedNodes( nameNode.Parent as MemberAccessExpressionSyntax, nameNode, newNameNode, rootNode => UpdateTypeAndRunReferenceTransformers(rootNode, funcResult, funReferenceResult, namespaceMetadata, (type, fullName) => rootNode.WithExpression(type.CreateTypeSyntax(false, fullName))), childNode => RunReferenceTransformers(childNode, funcResult, funReferenceResult, namespaceMetadata) ); } return(node); } InvocationExpressionSyntax invokeNode = null; var isAccessor = bodyFuncReferenceResult.ReferenceSymbol.IsAccessor(); if (!isAccessor && funReferenceResult.ReferenceNode.IsKind(SyntaxKind.InvocationExpression)) { invokeNode = nameNode.Ancestors().OfType <InvocationExpressionSyntax>().First(); } if (!bodyFuncReferenceResult.AwaitInvocation) { // An arrow method does not have a statement var statement = nameNode.Ancestors().OfType <StatementSyntax>().FirstOrDefault(); var newNode = (SyntaxNode)statement ?? node; if (invokeNode != null) { newNode = newNode.ReplaceNestedNodes( invokeNode, nameNode, newNameNode, rootNode => UpdateTypeAndRunReferenceTransformers(rootNode .AddCancellationTokenArgumentIf(cancellationTokenParamName, bodyFuncReferenceResult), funcResult, funReferenceResult, namespaceMetadata, (memberNode, type, fullName) => memberNode.WithExpression(type.CreateTypeSyntax(true, fullName).WithTriviaFrom(memberNode.Expression))) ); } else if (isAccessor) { newNode = ConvertAccessor(newNode, nameNode, newNameNode, cancellationTokenParamName, bodyFuncReferenceResult, invNode => UpdateTypeAndRunReferenceTransformers(invNode, funcResult, funReferenceResult, namespaceMetadata, (memberNode, type, fullName) => memberNode.WithExpression(type.CreateTypeSyntax(true, fullName).WithTriviaFrom(memberNode.Expression)))); } else { newNode = newNode.ReplaceNestedNodes( nameNode.Parent as MemberAccessExpressionSyntax, nameNode, newNameNode, rootNode => UpdateTypeAndRunReferenceTransformers(rootNode, funcResult, funReferenceResult, namespaceMetadata, (type, fullName) => rootNode.WithExpression(type.CreateTypeSyntax(false, fullName).WithTriviaFrom(rootNode.Expression))), childNode => RunReferenceTransformers(childNode, funcResult, funReferenceResult, namespaceMetadata) ); } if (statement != null && !statement.IsKind(SyntaxKind.LocalFunctionStatement)) { if (bodyFuncReferenceResult.UseAsReturnValue) { newNode = ((StatementSyntax)newNode).ToReturnStatement(); } node = node .ReplaceNode(statement, newNode); } else { node = (T)newNode; } } else { // We need to annotate the invocation node because of the AddAwait method as it needs the parent node var invokeAnnotation = Guid.NewGuid().ToString(); if (isAccessor) { node = ConvertAccessor(node, nameNode, newNameNode, cancellationTokenParamName, bodyFuncReferenceResult, invNode => UpdateTypeAndRunReferenceTransformers(invNode, funcResult, funReferenceResult, namespaceMetadata, (memberNode, type, fullName) => memberNode.WithExpression(type.CreateTypeSyntax(true, fullName).WithTriviaFrom(memberNode.Expression))) .WithAdditionalAnnotations(new SyntaxAnnotation(invokeAnnotation)) ); } else { node = node.ReplaceNestedNodes( invokeNode, nameNode, newNameNode, rootNode => UpdateTypeAndRunReferenceTransformers(rootNode .AddCancellationTokenArgumentIf(cancellationTokenParamName, bodyFuncReferenceResult), funcResult, funReferenceResult, namespaceMetadata, (memberNode, type, fullName) => memberNode.WithExpression(type.CreateTypeSyntax(true, fullName).WithTriviaFrom(memberNode.Expression))) .WithAdditionalAnnotations(new SyntaxAnnotation(invokeAnnotation)) ); } invokeNode = node.GetAnnotatedNodes(invokeAnnotation).OfType <InvocationExpressionSyntax>().First(); var conditionalAccessNode = invokeNode.Ancestors() .TakeWhile(o => !(o is StatementSyntax)) .OfType <ConditionalAccessExpressionSyntax>() .FirstOrDefault(); if (conditionalAccessNode != null) // ?. syntax { var statement = (StatementSyntax)invokeNode.Ancestors().FirstOrDefault(o => o is StatementSyntax); var block = statement?.Parent as BlockSyntax; if (statement == null || block == null) { // TODO: convert arrow method/property/function to a normal one // TODO: convert to block if there is no block node = node.ReplaceNode(conditionalAccessNode, conditionalAccessNode.AddAwait(_configuration.ConfigureAwaitArgument)); } else { var fnName = nameNode.Identifier.ValueText; // TODO: handle name collisions var variableName = $"{char.ToLowerInvariant(fnName[0])}{fnName.Substring(1)}Task"; var leadingTrivia = statement.GetLeadingTrivia(); var newConditionalAccessNode = ConditionalAccessExpression( conditionalAccessNode.Expression, invokeNode) .WithTriviaFrom(conditionalAccessNode); var localVar = LocalDeclarationStatement( VariableDeclaration( IdentifierName(Identifier(leadingTrivia, "var", TriviaList(Space))), SingletonSeparatedList( VariableDeclarator( Identifier(TriviaList(), variableName, TriviaList(Space))) .WithInitializer( EqualsValueClause(newConditionalAccessNode.WithoutTrivia()) .WithEqualsToken(Token(TriviaList(), SyntaxKind.EqualsToken, TriviaList(Space))) ) ))) .WithSemicolonToken(Token(TriviaList(), SyntaxKind.SemicolonToken, TriviaList(typeMetadata.EndOfLineTrivia))); var index = block.Statements.IndexOf(statement); var lastReturnNode = block.DescendantNodes() .Where(o => o.SpanStart >= statement.SpanStart) .OfType <ReturnStatementSyntax>() .LastOrDefault(); var variableAnnotation = Guid.NewGuid().ToString(); var newBlock = block.ReplaceNode(conditionalAccessNode, conditionalAccessNode.WhenNotNull.ReplaceNode(invokeNode, IdentifierName(variableName) .WithAdditionalAnnotations(new SyntaxAnnotation(variableAnnotation)) .WithLeadingTrivia(conditionalAccessNode.GetLeadingTrivia()) .WithTrailingTrivia(conditionalAccessNode.GetTrailingTrivia()) )); var variable = newBlock.GetAnnotatedNodes(variableAnnotation).OfType <IdentifierNameSyntax>().First(); newBlock = newBlock.ReplaceNode(variable, variable.AddAwait(_configuration.ConfigureAwaitArgument)); var ifBlock = Block() .WithOpenBraceToken( Token(TriviaList(leadingTrivia), SyntaxKind.OpenBraceToken, TriviaList(typeMetadata.EndOfLineTrivia))) .WithCloseBraceToken( Token(TriviaList(leadingTrivia), SyntaxKind.CloseBraceToken, TriviaList(typeMetadata.EndOfLineTrivia))) .WithStatements(new SyntaxList <StatementSyntax>() .AddRange(newBlock.AppendIndent(typeMetadata.IndentTrivia.ToFullString()).Statements.Skip(index))); var ifStatement = IfStatement( BinaryExpression( SyntaxKind.NotEqualsExpression, IdentifierName(Identifier(TriviaList(), variableName, TriviaList(Space))), LiteralExpression(SyntaxKind.NullLiteralExpression)) .WithOperatorToken( Token(TriviaList(), SyntaxKind.ExclamationEqualsToken, TriviaList(Space))), ifBlock ) .WithIfKeyword( Token(TriviaList(leadingTrivia), SyntaxKind.IfKeyword, TriviaList(Space))) .WithCloseParenToken( Token(TriviaList(), SyntaxKind.CloseParenToken, TriviaList(typeMetadata.EndOfLineTrivia))); var statements = new SyntaxList <StatementSyntax>() .AddRange(newBlock.Statements.Take(index)) .Add(localVar) .Add(ifStatement); if (lastReturnNode?.Expression != null) { // Check if the variable is defined otherwise return default return type value if (lastReturnNode.Expression is IdentifierNameSyntax idNode && statements.OfType <VariableDeclaratorSyntax>().All(o => o.Identifier.ToString() != idNode.Identifier.ValueText)) { lastReturnNode = lastReturnNode.WithExpression(DefaultExpression(funcResult.GetNode().GetReturnType().WithoutTrivia())); } statements = statements.Add(lastReturnNode); } node = node.ReplaceNode(block, newBlock.WithStatements(statements)); } } else { node = node.ReplaceNode(invokeNode, invokeNode.AddAwait(_configuration.ConfigureAwaitArgument)); } } return(node); }
// TODO: should we always add guards in anonymous methods? //public SyntaxNode Transform(IFunctionTransformationResult functionTransformResult, ITypeTransformationMetadata typeMetadata, // INamespaceTransformationMetadata namespaceMetadata) //{ // var methodResult = functionTransformResult.AnalyzationResult.GetMethodOrAccessor(); // if (methodResult == null || !methodResult.CancellationTokenRequired) // { // return null; // } // var functionNode = functionTransformResult.Transformed; // var functionBody = functionNode.GetFunctionBody() as BlockSyntax; // TODO: support expressions // if (functionBody != null) // { // functionNode = functionNode.ReplaceNode(functionBody, // AddGuards(functionTransformResult, functionBody, functionTransformResult.AnalyzationResult, CancellationTokenParamName)); // } // return functionNode; //} public MethodTransformerResult Transform(IMethodOrAccessorTransformationResult transformResult, ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata) { var methodResult = transformResult.AnalyzationResult; if (!methodResult.CancellationTokenRequired) { return(MethodTransformerResult.Skip); } var originalNode = methodResult.GetNode(); var generationOptions = methodResult.MethodCancellationToken.GetValueOrDefault(); var methodNode = transformResult.Transformed; methodNode = methodNode .AddCancellationTokenParameter(CancellationTokenParamName, generationOptions.HasFlag(MethodCancellationToken.Optional), transformResult.LeadingWhitespaceTrivia, transformResult.EndOfLineTrivia); var methodBody = methodNode.Body; methodNode = methodNode.WithBody(AddGuards(transformResult, methodBody, methodResult, CancellationTokenParamName)); var originalMethodNode = originalNode as MethodDeclarationSyntax; // Add an additional overload if specified if (originalMethodNode == null || ( !generationOptions.HasFlag(MethodCancellationToken.ForwardNone) && !generationOptions.HasFlag(MethodCancellationToken.SealedForwardNone) )) { return(MethodTransformerResult.Update(methodNode)); } var overloadNode = originalMethodNode .ReturnAsTask(namespaceMetadata.TaskConflict) .WithTriviaFrom(transformResult.Transformed) // We want to have the sumamry of the transformed node but not the parameter list .WithoutAnnotations(transformResult.Annotation) .WithIdentifier(Identifier(methodNode.Identifier.ValueText)); // We can have abstract methods that don't have a body if (methodResult.Symbol.IsAbstract) { // Add the trailing trivia from the semicolon to close paren overloadNode = overloadNode .WithParameterList( overloadNode.ParameterList .WithCloseParenToken( overloadNode.ParameterList.CloseParenToken.WithTrailingTrivia(overloadNode.SemicolonToken.TrailingTrivia)) ) .WithSemicolonToken(default(SyntaxToken)); methodBody = Block() .WithOpenBraceToken( Token(TriviaList(transformResult.LeadingWhitespaceTrivia), SyntaxKind.OpenBraceToken, TriviaList(transformResult.EndOfLineTrivia))) .WithCloseBraceToken( Token(TriviaList(transformResult.LeadingWhitespaceTrivia), SyntaxKind.CloseBraceToken, TriviaList(transformResult.EndOfLineTrivia))); } var tokenArg = Argument( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName("CancellationToken"), IdentifierName("None"))); overloadNode = overloadNode .WithBody(methodBody .WithStatements( SingletonList <StatementSyntax>( ReturnStatement(originalMethodNode.ForwardCall(methodResult.Symbol, methodNode.Identifier.ValueText, tokenArg)) .WithReturnKeyword( Token(TriviaList(transformResult.BodyLeadingWhitespaceTrivia), SyntaxKind.ReturnKeyword, TriviaList(Space)) ) .WithSemicolonToken(Token(TriviaList(), SyntaxKind.SemicolonToken, TriviaList(transformResult.EndOfLineTrivia))) ) )); if (generationOptions.HasFlag(MethodCancellationToken.SealedForwardNone)) { if (methodResult.Symbol.IsVirtual) { // For virtual methods we need to remove the virtual keyword overloadNode = overloadNode .WithModifiers(TokenList(originalMethodNode.Modifiers.Where(o => !o.IsKind(SyntaxKind.VirtualKeyword)))); } else if (methodResult.Symbol.OverriddenMethod != null) { // For overrides we need to add the sealed keyword overloadNode = overloadNode .WithModifiers(originalMethodNode.Modifiers.Add(Token(TriviaList(), SyntaxKind.SealedKeyword, TriviaList(Space)))); } else if (methodResult.Symbol.IsAbstract) { // For abstract we need to remove the abstract keyword overloadNode = overloadNode .WithModifiers(TokenList(originalMethodNode.Modifiers.Where(o => !o.IsKind(SyntaxKind.AbstractKeyword)))); } } // We need to remove all directives while (overloadNode.ContainsDirectives) { overloadNode = overloadNode.RemoveNode(overloadNode.GetFirstDirective(), SyntaxRemoveOptions.KeepNoTrivia); } return(MethodTransformerResult.Update(methodNode) .AddMethod(overloadNode)); }
public MethodTransformerResult Transform(IMethodOrAccessorTransformationResult transformResult, ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata) { var methodResult = transformResult.AnalyzationResult; if (!methodResult.Missing || !methodResult.Conversion.HasFlag(MethodConversion.ToAsync) || methodResult.Symbol.IsObsolete()) { return(MethodTransformerResult.Skip); } var methodNode = transformResult.Transformed; var baseMethod = methodResult.RelatedMethods .Where(o => (methodResult.BaseOverriddenMethod != null && o.Symbol.Equals(methodResult.BaseOverriddenMethod)) || methodResult.ImplementedInterfaces.Any(i => o.Symbol.Equals(i))) .FirstOrDefault(o => o.AsyncCounterpartSymbol?.IsObsolete() == true); if (baseMethod == null) { return(MethodTransformerResult.Skip); } namespaceMetadata.AddUsing("System"); AttributeListSyntax obsoleteAttribute = null; var syntaxReference = baseMethod.AsyncCounterpartSymbol.DeclaringSyntaxReferences.SingleOrDefault(); SyntaxTrivia?documentationTrivia = null; if (syntaxReference != null) { var baseMethodNode = syntaxReference.GetSyntax() as MethodDeclarationSyntax; obsoleteAttribute = baseMethodNode?.AttributeLists.FirstOrDefault(o => o.Attributes.Count == 1 && o.Attributes.First().Name.ToString() == "Obsolete"); obsoleteAttribute = (AttributeListSyntax)_directiveRemover.VisitAttributeList(obsoleteAttribute); documentationTrivia = obsoleteAttribute.GetLeadingTrivia() .Select(o => o.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia) ? o : (SyntaxTrivia?)null) .FirstOrDefault(o => o.HasValue); } if (obsoleteAttribute == null) { obsoleteAttribute = AttributeList(SingletonSeparatedList(Attribute(IdentifierName("Obsolete")))) .WithOpenBracketToken(Token(TriviaList(transformResult.LeadingWhitespaceTrivia), SyntaxKind.OpenBracketToken, TriviaList())) .WithCloseBracketToken(Token(TriviaList(), SyntaxKind.CloseBracketToken, TriviaList(transformResult.EndOfLineTrivia))); } var inheritDocTrivia = Trivia(GetInheritdoc(transformResult.EndOfLineTrivia.ToFullString())); if (documentationTrivia.HasValue) { obsoleteAttribute = obsoleteAttribute.WithLeadingTrivia(obsoleteAttribute.GetLeadingTrivia() .Replace(documentationTrivia.Value, inheritDocTrivia)); } else { // Append <inheritdoc /> var leadingTrivia = obsoleteAttribute.GetLeadingTrivia(); var trivias = new List <SyntaxTrivia>(); if (leadingTrivia.Count == 0 || !leadingTrivia.Last().IsKind(SyntaxKind.WhitespaceTrivia)) { trivias.Add(transformResult.LeadingWhitespaceTrivia); } trivias.Add(inheritDocTrivia); trivias.Add(transformResult.LeadingWhitespaceTrivia); obsoleteAttribute = obsoleteAttribute.WithLeadingTrivia(leadingTrivia.AddRange(trivias)); } methodNode = methodNode .WithLeadingTrivia(TriviaList(transformResult.LeadingWhitespaceTrivia)) .WithAttributeLists(methodNode.AttributeLists.Add(obsoleteAttribute)); return(MethodTransformerResult.Update(methodNode)); }
private void TransformPropertyAccessor(SyntaxNode node, PropertyTransformationResult propertyResult, AccessorTransformationResult result, ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata) { var methodResult = result.AnalyzationResult; result.BodyLeadingWhitespaceTrivia = propertyResult.BodyLeadingWhitespaceTrivia; result.LeadingWhitespaceTrivia = propertyResult.LeadingWhitespaceTrivia; result.EndOfLineTrivia = propertyResult.EndOfLineTrivia; result.IndentTrivia = propertyResult.IndentTrivia; var methodConversion = methodResult.Conversion; if (!methodConversion.HasFlag(MethodConversion.ToAsync)) { return; } var methodNode = MethodDeclaration( methodResult.Symbol.MethodKind != MethodKind.PropertySet ? propertyResult.OriginalNode.Type : IdentifierName(nameof(Task)).WithTriviaFrom(propertyResult.OriginalNode.Type), methodResult.AsyncCounterpartName ) .WithModifiers(propertyResult.OriginalNode.Modifiers) .WithLeadingTrivia(propertyResult.OriginalNode.GetLeadingTrivia()); var methodBodyNode = methodResult.GetBodyNode(); if (methodBodyNode == null) { methodNode = methodNode .WithSemicolonToken(Token(TriviaList(), SyntaxKind.SemicolonToken, TriviaList(propertyResult.EndOfLineTrivia))); result.Transformed = methodNode; return; } var startMethodSpan = methodResult.Node.Span.Start; node = node.WithAdditionalAnnotations(new SyntaxAnnotation(result.Annotation)); startMethodSpan -= node.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 = node.GetSimpleName(startSpan, reference.Location.SourceSpan.Length); var annotation = Guid.NewGuid().ToString(); node = node.ReplaceNode(nameNode, nameNode.WithAdditionalAnnotations(new SyntaxAnnotation(annotation))); typeReferencesAnnotations.Add(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 = node.DescendantNodesAndSelf() .First(o => o.IsKind(functionKind) && o.SpanStart == typeSpanStart && o.Span.Length == typeSpanLength); var transformFuncResult = TransformFunction(childFunction, result, typeMetadata, namespaceMetadata); result.TransformedFunctions.Add(transformFuncResult); node = node.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 = node.GetSimpleName(startSpan, reference.Location.SourceSpan.Length, isCref); node = node.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 = node.DescendantNodes().First(o => o.SpanStart == startSpan && o.Span.Length == refNode.Span.Length); node = node.ReplaceNode(referenceNode, referenceNode.WithAdditionalAnnotations(new SyntaxAnnotation(Annotations.TaskReturned))); } } // Modify references foreach (var refAnnotation in typeReferencesAnnotations) { var nameNode = node.GetAnnotatedNodes(refAnnotation).OfType <SimpleNameSyntax>().First(); node = node .ReplaceNode(nameNode, nameNode.WithIdentifier(Identifier(nameNode.Identifier.Value + "Async"))); } foreach (var transformFunction in result.TransformedFunctions) { var funcNode = node.GetAnnotatedNodes(transformFunction.Annotation).First(); node = node .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)) { node = TransformFunctionReference(node, methodResult, transfromReference, typeMetadata, namespaceMetadata); } if (methodResult.Symbol.MethodKind == MethodKind.PropertySet) { methodNode = methodNode.WithParameterList( methodNode.ParameterList.WithParameters( SingletonSeparatedList( Parameter(Identifier(TriviaList(), "value", TriviaList())) .WithType(propertyResult.OriginalNode.Type.WithoutTrivia().WithTrailingTrivia(TriviaList(Space))) ) ) ); } if (node is ArrowExpressionClauseSyntax arrowNode) { methodNode = methodNode .WithExpressionBody(arrowNode) .WithSemicolonToken(Token(TriviaList(), SyntaxKind.SemicolonToken, TriviaList(propertyResult.EndOfLineTrivia))); } else if (node is AccessorDeclarationSyntax accessorNode) { if (accessorNode.ExpressionBody != null) { methodNode = methodNode .WithExpressionBody(accessorNode.ExpressionBody) .WithSemicolonToken(Token(TriviaList(), SyntaxKind.SemicolonToken, TriviaList(propertyResult.EndOfLineTrivia))); } else { methodNode = methodNode .WithBody(accessorNode.Body); } } methodNode = FixupBodyFormatting(methodNode, result); result.Transformed = methodNode; }
private PropertyTransformationResult TransformProperty(PropertyDeclarationSyntax propertyNode, bool canCopy, PropertyTransformationResult result, ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata) { var analyzeResult = result.AnalyzationResult; var startRootSpan = analyzeResult.Node.SpanStart; // Calculate whitespace method trivias result.EndOfLineTrivia = propertyNode.GetEndOfLine(); result.LeadingWhitespaceTrivia = propertyNode.GetLeadingWhitespace(); result.IndentTrivia = propertyNode.GetIndent(result.LeadingWhitespaceTrivia, typeMetadata.LeadingWhitespaceTrivia); result.BodyLeadingWhitespaceTrivia = Whitespace(result.LeadingWhitespaceTrivia.ToFullString() + result.IndentTrivia.ToFullString()); if (analyzeResult.Conversion == PropertyConversion.Ignore && analyzeResult.GetAccessors().All(o => o.Conversion == MethodConversion.Ignore)) { return(result); } propertyNode = propertyNode.WithAdditionalAnnotations(new SyntaxAnnotation(result.Annotation)); startRootSpan -= propertyNode.SpanStart; foreach (var accessorResult in analyzeResult.GetAccessors().Where(o => o.Conversion != MethodConversion.Ignore)) { var spanStart = accessorResult.Node.SpanStart - startRootSpan; var spanLength = accessorResult.Node.Span.Length; var accessorNode = propertyNode.DescendantNodes() .First(o => o.SpanStart == spanStart && o.Span.Length == spanLength); var transformedNode = new AccessorTransformationResult(accessorResult); result.TransformedAccessors.Add(transformedNode); propertyNode = propertyNode.ReplaceNode(accessorNode, accessorNode.WithAdditionalAnnotations(new SyntaxAnnotation(transformedNode.Annotation))); } if (canCopy && result.AnalyzationResult.Conversion == PropertyConversion.Copy) { result.Transformed = result.OriginalNode; } foreach (var accessorResult in result.TransformedAccessors.OrderByDescending(o => o.OriginalStartSpan)) { var accessorNode = propertyNode.GetAnnotatedNodes(accessorResult.Annotation) .First(); TransformPropertyAccessor(accessorNode, result, accessorResult, typeMetadata, namespaceMetadata); } return(result); }
public MethodTransformerResult Transform(IMethodOrAccessorTransformationResult transformResult, ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata) { var methodResult = transformResult.AnalyzationResult; if (!methodResult.CancellationTokenRequired) { return(MethodTransformerResult.Skip); } var originalNode = methodResult.GetNode(); var cancellationTokenParamName = "cancellationToken"; // TODO: handle variable collision for token var generationOptions = methodResult.MethodCancellationToken.GetValueOrDefault(); var methodNode = transformResult.Transformed; methodNode = methodNode .AddCancellationTokenParameter(cancellationTokenParamName, generationOptions.HasFlag(MethodCancellationToken.Optional), transformResult.LeadingWhitespaceTrivia, transformResult.EndOfLineTrivia); var methodBody = methodNode.Body; if (_configuration.Guards && methodBody != null && !methodResult.Faulted) { var startGuard = methodResult.OmitAsync ? GetSyncGuard(methodResult, cancellationTokenParamName, transformResult.BodyLeadingWhitespaceTrivia, transformResult.EndOfLineTrivia, transformResult.IndentTrivia) : GetAsyncGuard(cancellationTokenParamName, transformResult.BodyLeadingWhitespaceTrivia, transformResult.EndOfLineTrivia); methodNode = methodNode.WithBody( methodBody.WithStatements( methodBody.Statements.Insert(methodResult.Preconditions.Count, startGuard)) ); // We need to get all statements that have at least one async invocation without a cancellation token argument, to prepend an extra guard var statements = new Dictionary <int, string>(); foreach (var functionReference in transformResult.TransformedFunctionReferences) { if (!(functionReference.AnalyzationResult is IBodyFunctionReferenceAnalyzationResult bodyFunctionReference)) { continue; } if (bodyFunctionReference.GetConversion() != ReferenceConversion.ToAsync || bodyFunctionReference.PassCancellationToken) { continue; } var statement = methodNode .GetAnnotatedNodes(functionReference.Annotation) .First().Ancestors().OfType <StatementSyntax>().First(); if (statements.ContainsKey(statement.SpanStart)) { continue; } var annotation = Guid.NewGuid().ToString(); methodNode = methodNode .ReplaceNode(statement, statement.WithAdditionalAnnotations(new SyntaxAnnotation(annotation))); statements.Add(statement.SpanStart, annotation); } // For each statement we need to find the index where is located in the block. // TODO: Add support when the parent is not a block syntax foreach (var pair in statements) { var statement = methodNode.GetAnnotatedNodes(pair.Value).OfType <StatementSyntax>().First(); var parentBlock = statement.Parent as BlockSyntax; if (parentBlock == null) { continue; // Currently not supported } var index = parentBlock.Statements.IndexOf(statement); var newParentBlock = parentBlock .WithStatements(parentBlock.Statements .Insert(index, GetAsyncGuard(cancellationTokenParamName, statement.GetLeadingWhitespace(), transformResult.EndOfLineTrivia))); methodNode = methodNode .ReplaceNode(parentBlock, newParentBlock); } } var originalMethodNode = originalNode as MethodDeclarationSyntax; // Add an additional overload if specified if (originalMethodNode == null || ( !generationOptions.HasFlag(MethodCancellationToken.ForwardNone) && !generationOptions.HasFlag(MethodCancellationToken.SealedForwardNone) )) { return(MethodTransformerResult.Update(methodNode)); } var overloadNode = originalMethodNode .ReturnAsTask(namespaceMetadata.TaskConflict) .WithTriviaFrom(transformResult.Transformed) // We want to have the sumamry of the transformed node but not the parameter list .WithoutAnnotations(transformResult.Annotation) .WithIdentifier(Identifier(methodNode.Identifier.ValueText)); // We can have abstract methods that don't have a body if (methodResult.Symbol.IsAbstract) { // Add the trailing trivia from the semicolon to close paren overloadNode = overloadNode .WithParameterList( overloadNode.ParameterList .WithCloseParenToken( overloadNode.ParameterList.CloseParenToken.WithTrailingTrivia(overloadNode.SemicolonToken.TrailingTrivia)) ) .WithSemicolonToken(default(SyntaxToken)); methodBody = Block() .WithOpenBraceToken( Token(TriviaList(transformResult.LeadingWhitespaceTrivia), SyntaxKind.OpenBraceToken, TriviaList(transformResult.EndOfLineTrivia))) .WithCloseBraceToken( Token(TriviaList(transformResult.LeadingWhitespaceTrivia), SyntaxKind.CloseBraceToken, TriviaList(transformResult.EndOfLineTrivia))); } var tokenArg = Argument( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName("CancellationToken"), IdentifierName("None"))); overloadNode = overloadNode .WithBody(methodBody .WithStatements( SingletonList <StatementSyntax>( ReturnStatement(originalMethodNode.ForwardCall(methodResult.Symbol, methodNode.Identifier.ValueText, tokenArg)) .WithReturnKeyword( Token(TriviaList(transformResult.BodyLeadingWhitespaceTrivia), SyntaxKind.ReturnKeyword, TriviaList(Space)) ) .WithSemicolonToken(Token(TriviaList(), SyntaxKind.SemicolonToken, TriviaList(transformResult.EndOfLineTrivia))) ) )); if (generationOptions.HasFlag(MethodCancellationToken.SealedForwardNone)) { if (methodResult.Symbol.IsVirtual) { // For virtual methods we need to remove the virtual keyword overloadNode = overloadNode .WithModifiers(TokenList(originalMethodNode.Modifiers.Where(o => !o.IsKind(SyntaxKind.VirtualKeyword)))); } else if (methodResult.Symbol.OverriddenMethod != null) { // For overrides we need to add the sealed keyword overloadNode = overloadNode .WithModifiers(originalMethodNode.Modifiers.Add(Token(TriviaList(), SyntaxKind.SealedKeyword, TriviaList(Space)))); } else if (methodResult.Symbol.IsAbstract) { // For abstract we need to remove the abstract keyword overloadNode = overloadNode .WithModifiers(TokenList(originalMethodNode.Modifiers.Where(o => !o.IsKind(SyntaxKind.AbstractKeyword)))); } } // We need to remove all directives while (overloadNode.ContainsDirectives) { overloadNode = overloadNode.RemoveNode(overloadNode.GetFirstDirective(), SyntaxRemoveOptions.KeepNoTrivia); } return(MethodTransformerResult.Update(methodNode) .AddMethod(overloadNode)); }
public SyntaxNode Transform(IFunctionTransformationResult transformResult, ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata) { return(transformResult.AnalyzationResult.OmitAsync ? null : _rewriter.VisitFunction(transformResult.Transformed)); }
private RootFunctionTransformationResult TransformFunction(IFunctionAnalyzationResult rootFuncResult, ITransformationTrivia parentTransformTrivia, ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata) { var rootFuncNode = rootFuncResult.GetNode(); var startRootFuncSpan = rootFuncNode.SpanStart; var rootTransformResult = new RootFunctionTransformationResult(rootFuncResult) { }; // We do this here because we want that the root node has span start equal to 0 rootFuncNode = rootFuncNode.WithAdditionalAnnotations(new SyntaxAnnotation(rootTransformResult.Annotation)); startRootFuncSpan -= rootFuncNode.SpanStart; // Before any modification we need to annotate nodes that will be transformed in order to find them later on. foreach (var funcResult in rootFuncResult.GetSelfAndDescendantsFunctions()) { var origFuncNode = funcResult.GetNode(); var funcSpanStart = origFuncNode.SpanStart - startRootFuncSpan; var funcSpanLength = origFuncNode.Span.Length; var funcNode = rootFuncNode.DescendantNodesAndSelf() .Where(o => !o.IsKind(SyntaxKind.Argument)) // An argument can have the same span as the function .First(o => o.SpanStart == funcSpanStart && o.Span.Length == funcSpanLength); FunctionTransformationResult transformResult; if (funcNode == rootFuncNode) { transformResult = rootTransformResult; transformResult.IndentTrivia = parentTransformTrivia.IndentTrivia; transformResult.EndOfLineTrivia = parentTransformTrivia.EndOfLineTrivia; transformResult.LeadingWhitespaceTrivia = Whitespace(parentTransformTrivia.LeadingWhitespaceTrivia.ToFullString() + parentTransformTrivia.IndentTrivia.ToFullString()); transformResult.BodyLeadingWhitespaceTrivia = Whitespace(transformResult.LeadingWhitespaceTrivia.ToFullString() + parentTransformTrivia.IndentTrivia.ToFullString()); } else { transformResult = new FunctionTransformationResult(funcResult); rootFuncNode = rootFuncNode.ReplaceNode(funcNode, funcNode.WithAdditionalAnnotations(new SyntaxAnnotation(transformResult.Annotation))); rootTransformResult.DescendantTransformedFunctions.Add(transformResult); // TODO: calculate trivias } if (funcResult.Conversion == MethodConversion.Ignore) { continue; // Ignored functions shall be only annotated } foreach (var typeReference in funcResult.TypeReferences.Where(o => o.TypeAnalyzationResult.Conversion == TypeConversion.NewType)) { var reference = typeReference.ReferenceLocation; var refSpanStart = reference.Location.SourceSpan.Start - startRootFuncSpan; var refSpanLength = reference.Location.SourceSpan.Length; var nameNode = rootFuncNode.GetSimpleName(refSpanStart, refSpanLength, typeReference.IsCref); var transformedNode = new TransformationResult(nameNode) { Transformed = nameNode.WithIdentifier(Identifier(nameNode.Identifier.ValueText + "Async").WithTriviaFrom(nameNode.Identifier)) }; transformResult.TransformedNodes.Add(transformedNode); rootFuncNode = rootFuncNode.ReplaceNode(nameNode, nameNode.WithAdditionalAnnotations(new SyntaxAnnotation(transformedNode.Annotation))); } // TODO: unify with method in order to avoid duplicate code foreach (var referenceResult in funcResult.FunctionReferences.Where(o => o.GetConversion() == ReferenceConversion.ToAsync)) { var transfromReference = new FunctionReferenceTransformationResult(referenceResult); var reference = referenceResult.ReferenceLocation; var startSpan = reference.Location.SourceSpan.Start - startRootFuncSpan; var nameNode = rootFuncNode.GetSimpleName(startSpan, reference.Location.SourceSpan.Length); rootFuncNode = rootFuncNode.ReplaceNode(nameNode, nameNode.WithAdditionalAnnotations(new SyntaxAnnotation(transfromReference.Annotation))); transformResult.TransformedFunctionReferences.Add(transfromReference); var bodyRef = referenceResult as IBodyFunctionReferenceAnalyzationResult; if (!funcResult.OmitAsync || bodyRef == null) { 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; if (bodyRef.UseAsReturnValue || refNode.IsReturned()) { startSpan = refNode.SpanStart - startRootFuncSpan; var referenceNode = rootFuncNode.DescendantNodes().First(o => o.SpanStart == startSpan && o.Span.Length == refNode.Span.Length); rootFuncNode = rootFuncNode.ReplaceNode(referenceNode, referenceNode.WithAdditionalAnnotations(new SyntaxAnnotation(Annotations.TaskReturned))); } } } foreach (var transformResult in rootTransformResult.GetSelfAndDescendantTransformedFunctions().OrderByDescending(o => o.OriginalStartSpan)) { // We have to order by OriginalStartSpan in order to have consistent formatting when adding awaits foreach (var transfromReference in transformResult.TransformedFunctionReferences.OrderByDescending(o => o.OriginalStartSpan)) { rootFuncNode = TransformFunctionReference(rootFuncNode, rootFuncResult, transfromReference, typeMetadata, namespaceMetadata); } // Replace all rewritten nodes foreach (var rewNode in transformResult.TransformedNodes) { var node = rootFuncNode.GetAnnotatedNodes(rewNode.Annotation).First(); if (rewNode.Transformed == null) { rootFuncNode = rootFuncNode.RemoveNode(node, SyntaxRemoveOptions.KeepUnbalancedDirectives); } else { rootFuncNode = rootFuncNode.ReplaceNode(node, rewNode.Transformed); } } var funcNode = rootFuncNode.GetAnnotatedNodes(transformResult.Annotation).First(); var newFuncNode = funcNode; transformResult.Transformed = newFuncNode; newFuncNode = RunFunctionTransformers(transformResult, typeMetadata, namespaceMetadata); rootFuncNode = rootFuncNode.ReplaceNode(funcNode, newFuncNode); } rootTransformResult.Transformed = rootFuncNode; return(rootTransformResult); }
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").WithTriviaFrom(nameNode.Identifier))); } 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").WithTriviaFrom(nameNode.Identifier))); } 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); }
public MethodTransformerResult Transform(IMethodOrAccessorTransformationResult result, ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata) { if (!result.TransformedLocks.Any() && !result.AnalyzationResult.MustRunSynchronized) { return(MethodTransformerResult.Skip); } var newFields = new Dictionary <string, FieldDeclarationSyntax>(); var node = result.Transformed; // Transform all lock statements that contains at least one async invocation. foreach (var lockResult in result.TransformedLocks) { var lockNode = node.GetAnnotatedNodes(lockResult.Annotation).OfType <LockStatementSyntax>().First(); if (result.AnalyzationResult.FunctionReferences .Where(r => lockResult.AnalyzationResult.Node.Span.Contains(r.ReferenceNameNode.Span)) .All(r => r.GetConversion() != ReferenceConversion.ToAsync)) { continue; } var lockSymbol = lockResult.AnalyzationResult.Symbol; var typeLock = lockSymbol is INamedTypeSymbol; var isStatic = lockSymbol.IsStatic || typeLock; var lockFieldName = GetLockFieldName(lockResult.AnalyzationResult.Symbol, isStatic, typeLock ? "Lock" : "", "Async"); // TODO: handle name collisions if (!typeMetadata.MemberNames.Contains(lockFieldName) && !newFields.ContainsKey(lockFieldName)) { newFields.Add(lockFieldName, GetAsyncLockField(lockFieldName, isStatic, result)); } var usingNode = GetUsingAsyncLock(lockFieldName, lockNode.GetLeadingTrivia(), lockNode.CloseParenToken.TrailingTrivia, lockNode.Statement); node = node.ReplaceNode(lockNode, usingNode); } if (result.AnalyzationResult.MustRunSynchronized) { var methodSymbol = result.AnalyzationResult.Symbol; var fieldName = GetLockFieldName(methodSymbol, methodSymbol.IsStatic, typeMetadata); var newBody = GetUsingAsyncLock(fieldName, TriviaList(result.LeadingWhitespaceTrivia), TriviaList(result.EndOfLineTrivia), node.Body) .AppendIndent(result.IndentTrivia.ToFullString()); node = node.WithBody(node.Body .WithStatements(SingletonList <StatementSyntax>(newBody))); // Remove the Synchronized option from the MethodImpl attribute var methodImplAttr = node.AttributeLists.SelectMany(o => o.Attributes).First(o => o.Name.ToString() == "MethodImpl"); var newMethodImplAttr = (AttributeSyntax)VisitAttribute(methodImplAttr); node = node.ReplaceNode(methodImplAttr, newMethodImplAttr); newFields.Add(fieldName, GetAsyncLockField(fieldName, methodSymbol.IsStatic, result)); } var transformResult = MethodTransformerResult.Update(node); foreach (var newField in newFields.Values) { transformResult.AddField(newField); } return(transformResult); }
public MethodTransformerResult Transform(IMethodOrAccessorTransformationResult methodTransformResult, ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata) { return(MethodTransformerResult.Update((MethodDeclarationSyntax)VisitMethodDeclaration(methodTransformResult.Transformed))); }
private T TransformConditionalAccessToConditionalExpressions <T>( T node, SimpleNameSyntax nameNode, IFunctionReferenceAnalyzationResult funReferenceResult, ITypeTransformationMetadata typeMetadata, ConditionalAccessExpressionSyntax conditionalAccessNode, InvocationExpressionSyntax invokeNode) where T : SyntaxNode { // TODO: we should check the async symbol instead var returnType = funReferenceResult.ReferenceSymbol.ReturnType; var type = returnType.CreateTypeSyntax(); var canSkipCast = !returnType.IsValueType && !returnType.IsNullable(); if (returnType.IsValueType && !returnType.IsNullable()) { type = NullableType(type); } ExpressionSyntax whenNotNullNode = null; if (invokeNode.Parent is MemberAccessExpressionSyntax memberAccessParent) { whenNotNullNode = conditionalAccessNode.WhenNotNull .ReplaceNode(memberAccessParent, MemberBindingExpression(Token(SyntaxKind.DotToken), memberAccessParent.Name)); } else if (invokeNode.Parent is ElementAccessExpressionSyntax elementAccessParent) { whenNotNullNode = conditionalAccessNode.WhenNotNull .ReplaceNode(elementAccessParent, ElementBindingExpression(elementAccessParent.ArgumentList)); } var valueNode = conditionalAccessNode.Expression.WithoutTrivia(); StatementSyntax variableStatement = null; BlockSyntax statementBlock = null; var statementIndex = 0; // We have to save the value in a variable when the expression is an invocation, index accessor or property in // order to prevent double calls // TODO: find a more robust solution if (!(conditionalAccessNode.Expression is SimpleNameSyntax simpleName) || char.IsUpper(simpleName.ToString()[0])) { var statement = (StatementSyntax)conditionalAccessNode.Ancestors().FirstOrDefault(o => o is StatementSyntax); if (statement == null || !(statement.Parent is BlockSyntax block)) { // TODO: convert arrow method/property/function to a normal one // TODO: convert to block if there is no block throw new NotSupportedException( $"Arrow method with null-conditional is not supported. Node: {conditionalAccessNode}"); } var leadingTrivia = statement.GetLeadingTrivia(); var fnName = nameNode.Identifier.ValueText; statementIndex = block.Statements.IndexOf(statement); statementBlock = block; // TODO: handle name collisions var variableName = $"{char.ToLowerInvariant(fnName[0])}{fnName.Substring(1)}{statementIndex}"; variableStatement = LocalDeclarationStatement( VariableDeclaration( IdentifierName(Identifier(leadingTrivia, "var", TriviaList(Space))), SingletonSeparatedList( VariableDeclarator( Identifier(TriviaList(), variableName, TriviaList(Space))) .WithInitializer( EqualsValueClause(valueNode) .WithEqualsToken(Token(TriviaList(), SyntaxKind.EqualsToken, TriviaList(Space))) ) ))) .WithSemicolonToken(Token(TriviaList(), SyntaxKind.SemicolonToken, TriviaList(typeMetadata.EndOfLineTrivia))); valueNode = IdentifierName(variableName); } var invocationAnnotation = Guid.NewGuid().ToString(); var nullNode = LiteralExpression( SyntaxKind.NullLiteralExpression, Token(TriviaList(), SyntaxKind.NullKeyword, TriviaList(Space))); var ifNullCondition = BinaryExpression( SyntaxKind.EqualsExpression, valueNode.WithTrailingTrivia(TriviaList(Space)), nullNode) .WithOperatorToken( Token(TriviaList(), SyntaxKind.EqualsEqualsToken, TriviaList(Space))); ExpressionSyntax wrappedNode = ParenthesizedExpression( ConditionalExpression( ifNullCondition, canSkipCast ? (ExpressionSyntax)nullNode : CastExpression( Token(SyntaxKind.OpenParenToken), type, Token(TriviaList(), SyntaxKind.CloseParenToken, TriviaList(Space)), nullNode), InvocationExpression( MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, valueNode, ((MemberBindingExpressionSyntax)invokeNode.Expression).Name)) .WithAdditionalAnnotations(new SyntaxAnnotation(invocationAnnotation)) .WithArgumentList(invokeNode.ArgumentList.WithoutTrailingTrivia()) ) .WithColonToken(Token(TriviaList(), SyntaxKind.ColonToken, TriviaList(Space))) .WithQuestionToken(Token(TriviaList(), SyntaxKind.QuestionToken, TriviaList(Space))) ); if (whenNotNullNode != null) { wrappedNode = conditionalAccessNode .WithExpression(wrappedNode) .WithWhenNotNull(whenNotNullNode); } wrappedNode = wrappedNode.WithTriviaFrom(conditionalAccessNode); invokeNode = (InvocationExpressionSyntax)wrappedNode.GetAnnotatedNodes(invocationAnnotation).First(); wrappedNode = wrappedNode.ReplaceNode(invokeNode, invokeNode.AddAwait(_configuration.ConfigureAwaitArgument)); if (statementBlock != null) { var newBlock = statementBlock.ReplaceNode(conditionalAccessNode, wrappedNode); newBlock = newBlock.WithStatements(newBlock.Statements.Insert(statementIndex, variableStatement)); return(node.ReplaceNode(statementBlock, newBlock)); } return(node.ReplaceNode(conditionalAccessNode, wrappedNode)); }