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)); }
private DocumentationCommentTriviaSyntax ProcessTag( DocumentationCommentTriviaSyntax documentationNode, IMethodOrAccessorTransformationResult methodTransformResult, string tagName, Func <IMethodSymbol, string> addOrReplace, Predicate <IMethodSymbol> canRemove, bool prepend) { if (addOrReplace == null && canRemove == null) { return(documentationNode); } var methodSymbol = methodTransformResult.AnalyzationResult.Symbol; var tagContent = addOrReplace?.Invoke(methodSymbol); var removeTag = canRemove?.Invoke(methodSymbol); var tagNode = documentationNode.Content.OfType <XmlElementSyntax>() .FirstOrDefault(o => o.StartTag.Name.ToString() == tagName); if (!string.IsNullOrEmpty(tagContent)) { var indent = methodTransformResult.LeadingWhitespaceTrivia.ToFullString(); var eol = methodTransformResult.EndOfLineTrivia.ToFullString(); var tagComment = CreateTagContent(tagName, tagContent, indent, eol); // When prepending a new tag before an existing tag we have to add the indentation at the end otherwise on start if (prepend && tagNode == null && documentationNode.Content.Any()) { var lastComment = tagComment.Content.Last(); tagComment = tagComment.ReplaceNode(lastComment, lastComment .WithTrailingTrivia(lastComment.GetTrailingTrivia() .Add(methodTransformResult.LeadingWhitespaceTrivia))); } else { var startComment = tagComment.Content.First(); tagComment = tagComment.ReplaceNode(startComment, startComment .WithLeadingTrivia(DocumentationCommentExterior($"{indent}///"))); } if (tagNode != null) { documentationNode = documentationNode.ReplaceNode(tagNode, tagComment.Content.OfType <XmlElementSyntax>().First()); } else { documentationNode = documentationNode.WithContent(prepend ? documentationNode.Content.InsertRange(0, tagComment.Content) : documentationNode.Content.AddRange(tagComment.Content)); } } else if (removeTag == true && tagNode != null) { // We need to remove the "///" XmlText and the tag itself var index = documentationNode.Content.IndexOf(tagNode); documentationNode = documentationNode.RemoveNode(tagNode, SyntaxRemoveOptions.KeepNoTrivia); documentationNode = documentationNode.RemoveNode(documentationNode.Content[index - 1], SyntaxRemoveOptions.KeepNoTrivia); } return(documentationNode); }
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)); }
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)); }
private MethodDeclarationSyntax FixupBodyFormatting(MethodDeclarationSyntax methodNode, IMethodOrAccessorTransformationResult result) { var methodBody = methodNode.Body; if (methodBody == null) { if (methodNode.ExpressionBody == null) { return(methodNode); } // Add space after the close paren token if (string.IsNullOrEmpty(methodNode.ParameterList.CloseParenToken.TrailingTrivia.ToFullString())) { methodNode = methodNode.ReplaceToken(methodNode.ParameterList.CloseParenToken, methodNode.ParameterList.CloseParenToken.WithTrailingTrivia(TriviaList(Space))); } return(methodNode); } // Make a diff by using only the whitespaces var triviaLengthDiff = GetLeadingWhitespaceDifference(methodBody, methodNode); if (triviaLengthDiff > 0) { // Normalize leading trivia methodNode = methodNode.WithBody(methodBody .SubtractIndent(string.Join("", methodBody.GetLeadingTrivia() .Where(o => o.IsKind(SyntaxKind.WhitespaceTrivia)) .Select(o => o.ToFullString())).Substring(0, triviaLengthDiff.Value))); } var eol = result.EndOfLineTrivia.ToFullString(); // Add end of line for the close paren token if missing if (!methodNode.ConstraintClauses.Any() && !methodNode.ParameterList.CloseParenToken.TrailingTrivia.ToFullString().Contains(eol)) { methodNode = methodNode.ReplaceToken(methodNode.ParameterList.CloseParenToken, methodNode.ParameterList.CloseParenToken.WithTrailingTrivia(result.EndOfLineTrivia)); methodNode = methodNode.ReplaceToken(methodNode.Body.OpenBraceToken, methodNode.Body.OpenBraceToken.WithLeadingTrivia(result.LeadingWhitespaceTrivia)); } else if (methodNode.ConstraintClauses.Any() && !methodNode.ConstraintClauses.Last().GetTrailingTrivia().ToFullString().Contains(eol)) { var lastConstraint = methodNode.ConstraintClauses.Last(); methodNode = methodNode.ReplaceNode(lastConstraint, lastConstraint.WithTrailingTrivia(result.EndOfLineTrivia)); methodNode = methodNode.ReplaceToken(methodNode.Body.OpenBraceToken, methodNode.Body.OpenBraceToken.WithLeadingTrivia(result.LeadingWhitespaceTrivia)); } methodBody = methodNode.Body; var getLineSpan = methodBody.GetLocation().GetLineSpan().Span; // Add end of line tokens for open brace and statements when the whole block is written in one line (eg. { DoSomething(); }) if (getLineSpan.End.Line == getLineSpan.Start.Line) { methodBody = methodBody.ReplaceToken(methodBody.OpenBraceToken, methodBody.OpenBraceToken.WithTrailingTrivia(result.EndOfLineTrivia)); // We have to fix also the statements leading trivia for (var i = 0; i < methodBody.Statements.Count; i++) { var statement = methodBody.Statements[i]; methodBody = methodBody.ReplaceNode(statement, statement .WithLeadingTrivia(result.BodyLeadingWhitespaceTrivia) .WithTrailingTrivia(TriviaList(result.EndOfLineTrivia))); } methodBody = methodBody.ReplaceToken(methodBody.CloseBraceToken, methodBody.CloseBraceToken.WithLeadingTrivia(TriviaList(result.LeadingWhitespaceTrivia))); methodNode = methodNode.WithBody(methodBody); } return(methodNode); }
public YieldRewriter(IMethodOrAccessorTransformationResult transformResult) { _transformResult = transformResult; }
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)); }
/// <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); }
public MethodTransformerResult Transform(IMethodOrAccessorTransformationResult methodTransformResult, ITypeTransformationMetadata typeMetadata, INamespaceTransformationMetadata namespaceMetadata) { return(MethodTransformerResult.Update((MethodDeclarationSyntax)VisitMethodDeclaration(methodTransformResult.Transformed))); }
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); }
private FieldDeclarationSyntax GetAsyncLockField(string fieldName, bool isStatic, IMethodOrAccessorTransformationResult result) { var list = TokenList(Token(TriviaList(result.LeadingWhitespaceTrivia), SyntaxKind.PrivateKeyword, TriviaList(Space))); if (isStatic) { list = list.Add(Token(TriviaList(), SyntaxKind.StaticKeyword, TriviaList(Space))); } list = list.Add(Token(TriviaList(), SyntaxKind.ReadOnlyKeyword, TriviaList(Space))); var lockType = SyntaxNodeExtensions.ConstructNameSyntax(_configuration.AsyncLockFullTypeName, Space); return(FieldDeclaration( VariableDeclaration(lockType) .WithVariables( SingletonSeparatedList( VariableDeclarator( Identifier(TriviaList(), fieldName, TriviaList(Space)) ) .WithInitializer( EqualsValueClause( ObjectCreationExpression(SyntaxNodeExtensions.ConstructNameSyntax(_configuration.AsyncLockFullTypeName)) .WithArgumentList(ArgumentList()) .WithNewKeyword(Token(TriviaList(), SyntaxKind.NewKeyword, TriviaList(Space))) ) .WithEqualsToken(Token(TriviaList(), SyntaxKind.EqualsToken, TriviaList(Space))) ) ) ) ) .WithModifiers(list) .WithSemicolonToken(Token(TriviaList(), SyntaxKind.SemicolonToken, TriviaList(result.EndOfLineTrivia)))); }
// 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)); }