See More

using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using NETMetaCoder.Abstractions; namespace NETMetaCoder { ///

/// This type is responsible for rewriting a compilation unit's syntax. /// public static class SyntaxRewriter { /// /// Constructs a new instance. /// /// /// /// /// public static SyntaxRewriteResult RewriteSyntaxTree( SyntaxTree tree, IEnumerable attributeNames, SyntaxEnvelope.SyntaxEnvelope syntaxEnvelope) => new SyntaxRewriterWithContext(tree, attributeNames, syntaxEnvelope).RewriteSyntaxTree(); private sealed class SyntaxRewriterWithContext : CSharpSyntaxRewriter { private readonly SyntaxTree _tree; private readonly IEnumerable _attributeNames; private readonly HashSet _targetNodeIndices; private ushort _nodeIndex; private bool _hasChanges; public SyntaxRewriterWithContext(SyntaxTree tree, IEnumerable attributeNames, SyntaxEnvelope.SyntaxEnvelope syntaxEnvelope) { _tree = tree; _attributeNames = attributeNames.ToList(); _targetNodeIndices = syntaxEnvelope.GatherNodeIndices(); } public SyntaxRewriteResult RewriteSyntaxTree() { if (_tree.GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error)) { throw new NETMetaCoderException("The syntax tree has errors and no rewriting will take place."); } if (!_targetNodeIndices.Any()) { return new SyntaxRewriteResult { SyntaxTree = _tree, HasChanges = false }; } var newTree = _tree.WithRootAndOptions(Visit(_tree.GetCompilationUnitRoot()), _tree.Options); return new SyntaxRewriteResult { SyntaxTree = newTree, HasChanges = _hasChanges }; } public override SyntaxNode VisitNamespaceDeclaration(NamespaceDeclarationSyntax node) { _nodeIndex++; return base.VisitNamespaceDeclaration(node); } public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node) { _nodeIndex++; if (!_targetNodeIndices.Contains(_nodeIndex)) { return base.VisitClassDeclaration(node); } node = node.WithPartialKeywordPrefix(); return base.VisitClassDeclaration(node); } public override SyntaxNode VisitStructDeclaration(StructDeclarationSyntax node) { _nodeIndex++; if (!_targetNodeIndices.Contains(_nodeIndex)) { return base.VisitStructDeclaration(node); } node = node.WithPartialKeywordPrefix(); return base.VisitStructDeclaration(node); } public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node) { _nodeIndex++; if (!_targetNodeIndices.Contains(_nodeIndex)) { return node; } var attributeNamesFound = node.FindAttributes(_attributeNames, null, null); var isTarget = attributeNamesFound.Any(); _hasChanges = isTarget; if (isTarget) { var attributeNameNeedles = attributeNamesFound .Select(StringExtensions.ToAttributeNameNeedle) .Distinct() .ToList(); attributeNameNeedles.Sort(); var newMethodName = node.Identifier.ToString(); newMethodName = attributeNameNeedles.Aggregate(newMethodName, (methodName, attributeNameNeedle) => methodName.Replace(attributeNameNeedle, "")); newMethodName = attributeNameNeedles.Aggregate(newMethodName, (methodName, attributeNameNeedle) => methodName + attributeNameNeedle); var attributeLists = new SyntaxList(node.AttributeLists.Select(al => { var attributes = al.Attributes.Select(a => a.IsMethodObsoletionAttribute() ? a.WithArgumentList(SyntaxFactory.AttributeArgumentList()) : a); return al.WithAttributes(SyntaxFactory.SeparatedList(attributes)); }).Where(al => al.Attributes.Any())); var newModifiers = new SyntaxTokenList(node.Modifiers.Where(modifier => !modifier.IsKind(SyntaxKind.OverrideKeyword) && !modifier.IsKind(SyntaxKind.SealedKeyword))); node = node .WithAttributeLists(attributeLists) .WithModifiers(newModifiers) .WithExplicitInterfaceSpecifier(null) .WithIdentifier(SyntaxFactory.Identifier(newMethodName)); } return base.VisitMethodDeclaration(node); } } } }