private MethodTransformerResult Update(MethodDeclarationSyntax methodNode, IMethodOrAccessorAnalyzationResult methodResult, INamespaceTransformationMetadata namespaceMetadata) { methodNode = methodNode.WithIdentifier(Identifier(methodResult.AsyncCounterpartName)); // Remove the new modifier if (methodNode.Modifiers.Any(o => o.IsKind(SyntaxKind.NewKeyword))) { var newMofidier = methodNode.Modifiers.First(o => o.IsKind(SyntaxKind.NewKeyword)); if (methodNode.GetFirstToken(true).IsKind(SyntaxKind.NewKeyword)) { methodNode = methodNode .WithModifiers(TokenList(methodNode.Modifiers.Where(o => !o.IsKind(SyntaxKind.NewKeyword)))) .WithLeadingTrivia(newMofidier.LeadingTrivia); } else { var nextToken = newMofidier.GetNextToken(true); methodNode = methodNode .ReplaceToken(nextToken, nextToken.WithLeadingTrivia(newMofidier.LeadingTrivia)) .WithModifiers(TokenList(methodNode.Modifiers.Where(o => !o.IsKind(SyntaxKind.NewKeyword)))); } } if (!methodResult.PreserveReturnType && methodResult.Symbol.MethodKind != MethodKind.PropertySet) { methodNode = methodNode.ReturnAsTask(namespaceMetadata.TaskConflict); } return(MethodTransformerResult.Update(methodNode)); }
private MethodTransformerResult Update(MethodDeclarationSyntax methodNode, IMethodOrAccessorAnalyzationResult methodResult, INamespaceTransformationMetadata namespaceMetadata) { methodNode = methodNode.WithIdentifier(Identifier(methodResult.AsyncCounterpartName)); if (!methodResult.PreserveReturnType && methodResult.Symbol.MethodKind != MethodKind.PropertySet) { methodNode = methodNode.ReturnAsTask(namespaceMetadata.TaskConflict); } return(MethodTransformerResult.Update(methodNode)); }
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) { 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 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)); }
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 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); }
// 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)); }