Пример #1
0
        internal static void AddField(this DocumentEditor editor, TypeDeclarationSyntax containingType, FieldDeclarationSyntax field)
        {
            FieldDeclarationSyntax existing = null;

            foreach (var member in containingType.Members)
            {
                if (member is FieldDeclarationSyntax fieldDeclaration)
                {
                    if (IsInsertBefore(fieldDeclaration))
                    {
                        editor.InsertBefore(fieldDeclaration, field);
                        return;
                    }

                    existing = fieldDeclaration;
                    continue;
                }

                editor.InsertBefore(member, field);
                return;
            }

            if (existing != null)
            {
                editor.InsertAfter(existing, field);
            }
            else
            {
                editor.AddMember(containingType, field);
            }
        }
Пример #2
0
        public void AddNSubstituteUsing()
        {
            CompilationUnitSyntax root = _editor.GetChangedRoot() as CompilationUnitSyntax;

            if (!root.Usings.Any(x => x.Name.GetText().ToString() == "NSubstitute"))
            {
                var newUsing = SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName("NSubstitute"));
                _editor.InsertBefore((_editor.OriginalRoot as CompilationUnitSyntax).Usings.FirstOrDefault(), newUsing);
            }
        }
        /// <inheritdoc />
        protected override void ChangePropertyCore(DocumentEditor editor, PropertyDeclarationSyntax propertySyntax)
        {
            var ownerType = (propertySyntax.Parent as ClassDeclarationSyntax)?.Identifier.ValueText;

            if (propertySyntax.AccessorList is null || ownerType is null)
            {
                return;
            }

            // 生成可通知属性的类型/名称/字段名称。
            var propertyType = propertySyntax.Type;

            propertyType = propertyType is NullableTypeSyntax nullableTypeSyntax ? nullableTypeSyntax.ElementType : propertyType;
            var propertyName         = propertySyntax.Identifier.ValueText;
            var attachedPropertyName = $"{propertyName}Property";

            // 增加字段。
            editor.InsertBefore(propertySyntax, new SyntaxNode[]
            {
                // private Type _field;
                SyntaxFactory.FieldDeclaration(
                    new SyntaxList <AttributeListSyntax>(),
                    new SyntaxTokenList(
                        SyntaxFactory.Token(SyntaxKind.PrivateKeyword),
                        SyntaxFactory.Token(SyntaxKind.StaticKeyword),
                        SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)),
                    SyntaxFactory.VariableDeclaration(
                        SyntaxFactory.ParseTypeName("System.Windows.DependencyProperty"),
                        SyntaxFactory.SeparatedList(new[]
        private void InsertNewVariableDeclaration(
            BinaryExpressionSyntax asExpression,
            SyntaxToken newIdentifier,
            SyntaxNode nodeLocation,
            DocumentEditor editor,
            ref bool variableAlreadyExtracted)
        {
            if (variableAlreadyExtracted)
            {
                return;
            }

            var newEqualsClause = SyntaxFactory.EqualsValueClause(asExpression);
            var newDeclarator   = SyntaxFactory.VariableDeclarator(newIdentifier.WithAdditionalAnnotations(RenameAnnotation.Create()), null, newEqualsClause);
            var newDeclaration  = SyntaxFactory.VariableDeclaration(SyntaxFactory.IdentifierName("var"), SyntaxFactory.SeparatedList(new[] { newDeclarator }));
            var newLocal        = SyntaxFactory.LocalDeclarationStatement(newDeclaration).WithAdditionalAnnotations(Formatter.Annotation);

            // If we are in an else statement, we have to add the new local before the initial if-statement. e.g.:
            //   if(o is int) { }
            //   else if(o is string) { }
            // If we are currently handling the second statement, we have to add the local before the first
            // However because there can be multiple chained if-else statements, we have to go up to the first one and add it there.
            nodeLocation = GetOuterIfStatement(nodeLocation);

            editor.InsertBefore(nodeLocation, newLocal);
            variableAlreadyExtracted = true;
        }
        /// <summary>
        /// Move <paramref name="toMove"></paramref> before <paramref name="statement">.</paramref>.
        /// </summary>
        /// <param name="editor">The <see cref="DocumentEditor"/>.</param>
        /// <param name="toMove">The <see cref="StatementSyntax"/> to move.</param>
        /// <param name="statement">The <see cref="StatementSyntax"/>.</param>
        /// <returns>The <see cref="DocumentEditor"/> that was passed in.</returns>
        public static DocumentEditor MoveBefore(this DocumentEditor editor, StatementSyntax toMove, StatementSyntax statement)
        {
            if (editor is null)
            {
                throw new ArgumentNullException(nameof(editor));
            }

            if (toMove is null)
            {
                throw new ArgumentNullException(nameof(toMove));
            }

            if (statement is null)
            {
                throw new ArgumentNullException(nameof(statement));
            }

            editor.RemoveNode(toMove);
            editor.InsertBefore(statement, ToMove());
            return(editor);

            StatementSyntax ToMove()
            {
                if (statement.GetLastToken().IsKind(SyntaxKind.CloseBraceToken))
                {
                    return(toMove.WithoutLeadingLineFeed());
                }

                return(toMove);
            }
        }
        private static void AddCheckIfDifferent(DocumentEditor editor, AssignmentExpressionSyntax assignment, CancellationToken cancellationToken)
        {
            var statementSyntax = assignment.FirstAncestorOrSelf <ExpressionStatementSyntax>();

            if (statementSyntax == null)
            {
                return;
            }

            var type = editor.SemanticModel.GetTypeInfoSafe(assignment.Left, cancellationToken).Type;
            var code = StringBuilderPool.Borrow()
                       .AppendLine($"if ({Snippet.EqualityCheck(type, "value", assignment.Left.ToString(), editor.SemanticModel)})")
                       .AppendLine("{")
                       .AppendLine("   return;")
                       .AppendLine("}")
                       .AppendLine()
                       .Return();
            var ifReturn = SyntaxFactory.ParseStatement(code)
                           .WithSimplifiedNames()
                           .WithLeadingElasticLineFeed()
                           .WithTrailingElasticLineFeed()
                           .WithAdditionalAnnotations(Formatter.Annotation);

            editor.InsertBefore(statementSyntax, ifReturn);
        }
        private static void DisposeInVirtualDisposeMethod(DocumentEditor editor, ISymbol memberSymbol, MethodDeclarationSyntax disposeMethodDeclaration, CancellationToken cancellationToken)
        {
            var disposeStatement = Snippet.DisposeStatement(memberSymbol, editor.SemanticModel, cancellationToken);

            if (TryFindIfDisposing(disposeMethodDeclaration, out var ifDisposing))
            {
                if (ifDisposing.Statement is BlockSyntax block)
                {
                    var statements = block.Statements.Add(disposeStatement);
                    var newBlock   = block.WithStatements(statements);
                    editor.ReplaceNode(block, newBlock);
                }
                else if (ifDisposing.Statement is StatementSyntax statement)
                {
                    editor.ReplaceNode(
                        ifDisposing,
                        ifDisposing.WithStatement(SyntaxFactory.Block(statement, disposeStatement)));
                }
                else
                {
                    editor.ReplaceNode(
                        ifDisposing,
                        ifDisposing.WithStatement(SyntaxFactory.Block(disposeStatement)));
                }
            }
            else if (disposeMethodDeclaration.Body is BlockSyntax block)
            {
                ifDisposing = SyntaxFactory.IfStatement(
                    SyntaxFactory.IdentifierName(disposeMethodDeclaration.ParameterList.Parameters[0].Identifier),
                    SyntaxFactory.Block(disposeStatement));

                if (DisposeMethod.TryFindBaseCall(disposeMethodDeclaration, editor.SemanticModel, cancellationToken, out var baseCall))
                {
                    if (baseCall.TryFirstAncestor(out ExpressionStatementSyntax expressionStatement))
                    {
                        editor.InsertBefore(expressionStatement, ifDisposing);
                    }
                }
                else
                {
                    editor.ReplaceNode(
                        block,
                        block.AddStatements(ifDisposing));
                }
            }
        }
        /// <inheritdoc />
        protected override void ChangePropertyCore(DocumentEditor editor, PropertyDeclarationSyntax propertySyntax)
        {
            if (propertySyntax.AccessorList is null)
            {
                return;
            }

            // 生成可通知属性的类型/名称/字段名称。
            var propertyType = propertySyntax.Type;
            var propertyName = propertySyntax.Identifier.ValueText;
            var fieldName    = $"_{char.ToLower(propertyName[0], CultureInfo.InvariantCulture)}{propertyName.Substring(1)}";

            // 增加字段。
            editor.InsertBefore(propertySyntax, new SyntaxNode[]
            {
                // private Type _field;
                SyntaxFactory.FieldDeclaration(
                    new SyntaxList <AttributeListSyntax>(),
                    new SyntaxTokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword)),
                    SyntaxFactory.VariableDeclaration(
                        propertyType,
                        SyntaxFactory.SeparatedList(new[]
                {
                    SyntaxFactory.VariableDeclarator(
                        SyntaxFactory.Identifier(fieldName)
                        )
                })
                        ),
                    SyntaxFactory.Token(SyntaxKind.SemicolonToken))
            });

            // 替换 get/set。
            editor.ReplaceNode(
                propertySyntax,
                SyntaxFactory.ParseMemberDeclaration(
                    $@"{propertySyntax.AttributeLists.ToFullString()}{propertySyntax.Modifiers.ToFullString()}{propertySyntax.Type.ToFullString()}{propertySyntax.Identifier.ToFullString()}
{{
    get => {fieldName};
    set => SetValue(ref {fieldName}, value);
}}") !
                .WithAdditionalAnnotations(new SyntaxAnnotation[] { Simplifier.Annotation, Formatter.Annotation })
                );
        }
Пример #9
0
        public static async Task AddMissingNamespaces(this DocumentEditor editor, NamespaceSet namespaces, IReadOnlyList <ImportedNamespace> importedNamespaces,
                                                      Func <SyntaxNode, IEnumerable <SyntaxNode>, SyntaxNode> addUsings, CancellationToken ct)
        {
            var orderedNamespaces = namespaces.OrderBy((x => x.ToDisplayString())).ToArray();

            if (orderedNamespaces.Length == 0)
            {
                return;
            }

            var g = editor.Generator;

            if (importedNamespaces == null || importedNamespaces.Count == 0)
            {
                var root = await editor.GetChangedDocument().GetSyntaxRootAsync(ct);

                var newRoot = addUsings(root, orderedNamespaces.Select(x => g.GenerateNamespaceImportDeclaration(x)));
                editor.ReplaceNode(root, newRoot);
                return;
            }

            for (int i = 0; i < orderedNamespaces.Length; i++)
            {
                var namespaceSymbol = orderedNamespaces[i];
                var index           = namespaceSymbol.GetIndexToInsertBefore(importedNamespaces);
                if (index < 0)
                {
                    continue;
                }

                if (index == importedNamespaces.Count)
                {
                    var lastNode = importedNamespaces.LastOrDefault().SyntaxNode;
                    Append(editor, lastNode, orderedNamespaces, i);
                    return;
                }

                var newNode = g.GenerateNamespaceImportDeclaration(namespaceSymbol);
                editor.InsertBefore(importedNamespaces[index].SyntaxNode, newNode);
            }
        }
        /// <summary>
        /// Move <paramref name="toMove"></paramref> before <paramref name="member">.</paramref>.
        /// </summary>
        /// <param name="editor">The <see cref="DocumentEditor"/>.</param>
        /// <param name="toMove">The <see cref="MemberDeclarationSyntax"/> to move.</param>
        /// <param name="member">The <see cref="MemberDeclarationSyntax"/>.</param>
        /// <returns>The <see cref="DocumentEditor"/> that was passed in.</returns>
        public static DocumentEditor MoveBefore(this DocumentEditor editor, MemberDeclarationSyntax toMove, MemberDeclarationSyntax member)
        {
            if (editor is null)
            {
                throw new ArgumentNullException(nameof(editor));
            }

            if (toMove is null)
            {
                throw new ArgumentNullException(nameof(toMove));
            }

            if (member is null)
            {
                throw new ArgumentNullException(nameof(member));
            }

            editor.RemoveNode(toMove);
            editor.InsertBefore(member, ToMove());
            editor.ReplaceNode(member, Member());
            return(editor);

            MemberDeclarationSyntax ToMove()
            {
                if (toMove.Parent is TypeDeclarationSyntax typeDeclaration)
                {
                    return(toMove.AdjustLeadingNewLine(typeDeclaration.Members.ElementAtOrDefault(typeDeclaration.Members.IndexOf(member) - 1)));
                }

                return(toMove);
            }

            MemberDeclarationSyntax Member()
            {
                return(member.AdjustLeadingNewLine(toMove));
            }
        }
Пример #11
0
        private async Task <Document> ConvertToGeneratedDllImport(
            Document doc,
            MethodDeclarationSyntax methodSyntax,
            IMethodSymbol methodSymbol,
            AttributeData dllImportAttr,
            INamedTypeSymbol generatedDllImportAttrType,
            bool usePreprocessorDefines,
            CancellationToken cancellationToken)
        {
            DocumentEditor editor = await DocumentEditor.CreateAsync(doc, cancellationToken).ConfigureAwait(false);

            SyntaxGenerator generator = editor.Generator;

            var dllImportSyntax = (AttributeSyntax)dllImportAttr !.ApplicationSyntaxReference !.GetSyntax(cancellationToken);

            // Create GeneratedDllImport attribute based on the DllImport attribute
            SyntaxNode generatedDllImportSyntax = GetGeneratedDllImportAttribute(
                editor,
                generator,
                dllImportSyntax,
                methodSymbol.GetDllImportData() !,
                generatedDllImportAttrType,
                out SyntaxNode? unmanagedCallConvAttributeMaybe);

            // Add annotation about potential behavioural and compatibility changes
            generatedDllImportSyntax = generatedDllImportSyntax.WithAdditionalAnnotations(
                WarningAnnotation.Create(string.Format(Resources.ConvertToGeneratedDllImportWarning, "[TODO] Documentation link")));

            // Replace DllImport with GeneratedDllImport
            SyntaxNode generatedDeclaration = generator.ReplaceNode(methodSyntax, dllImportSyntax, generatedDllImportSyntax);

            if (unmanagedCallConvAttributeMaybe is not null)
            {
                generatedDeclaration = generator.AddAttributes(generatedDeclaration, unmanagedCallConvAttributeMaybe);
            }

            // Replace extern keyword with partial keyword
            generatedDeclaration = generator.WithModifiers(
                generatedDeclaration,
                generator.GetModifiers(methodSyntax)
                .WithIsExtern(false)
                .WithPartial(true));

            if (!usePreprocessorDefines)
            {
                // Replace the original method with the updated one
                editor.ReplaceNode(methodSyntax, generatedDeclaration);
            }
            else
            {
                // #if DLLIMPORTGENERATOR_ENABLED
                generatedDeclaration = generatedDeclaration.WithLeadingTrivia(
                    generatedDeclaration.GetLeadingTrivia()
                    .AddRange(new[] {
                    SyntaxFactory.Trivia(SyntaxFactory.IfDirectiveTrivia(SyntaxFactory.IdentifierName("DLLIMPORTGENERATOR_ENABLED"), isActive: true, branchTaken: true, conditionValue: true)),
                    SyntaxFactory.ElasticMarker
                }));

                // #else
                generatedDeclaration = generatedDeclaration.WithTrailingTrivia(
                    generatedDeclaration.GetTrailingTrivia()
                    .AddRange(new[] {
                    SyntaxFactory.Trivia(SyntaxFactory.ElseDirectiveTrivia(isActive: false, branchTaken: false)),
                    SyntaxFactory.ElasticMarker
                }));

                // Remove existing leading trivia - it will be on the GeneratedDllImport method
                MethodDeclarationSyntax updatedDeclaration = methodSyntax.WithLeadingTrivia();

                // #endif
                updatedDeclaration = updatedDeclaration.WithTrailingTrivia(
                    methodSyntax.GetTrailingTrivia()
                    .AddRange(new[] {
                    SyntaxFactory.Trivia(SyntaxFactory.EndIfDirectiveTrivia(isActive: true)),
                    SyntaxFactory.ElasticMarker
                }));

                // Add the GeneratedDllImport method
                editor.InsertBefore(methodSyntax, generatedDeclaration);

                // Replace the original method with the updated DllImport method
                editor.ReplaceNode(methodSyntax, updatedDeclaration);
            }

            return(editor.GetChangedDocument());
        }
Пример #12
0
        private static void MakeWithBackingFieldNotifyWhenValueChanges(DocumentEditor editor, PropertyDeclarationSyntax propertyDeclaration, IMethodSymbol invoker, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            var classDeclaration = propertyDeclaration.FirstAncestorOrSelf <ClassDeclarationSyntax>();

            if (classDeclaration == null)
            {
                return;
            }

            if (propertyDeclaration.TryGetSetter(out var setter))
            {
                if (setter.ExpressionBody != null &&
                    IsSimpleAssignmentOnly(propertyDeclaration, out _, out _, out var assignment, out _))
                {
                    var property         = semanticModel.GetDeclaredSymbolSafe(propertyDeclaration, cancellationToken);
                    var underscoreFields = CodeStyle.UnderscoreFields(semanticModel);
                    var code             = StringBuilderPool.Borrow()
                                           .AppendLine($"public Type PropertyName")
                                           .AppendLine("{")
                                           .AppendLine($"    get => {assignment.Left};")
                                           .AppendLine()
                                           .AppendLine("    set")
                                           .AppendLine("    {")
                                           .AppendLine($"        if ({Snippet.EqualityCheck(property.Type, "value", assignment.Left.ToString(), semanticModel)})")
                                           .AppendLine("        {")
                                           .AppendLine($"           return;")
                                           .AppendLine("        }")
                                           .AppendLine()
                                           .AppendLine($"        {assignment};")
                                           .AppendLine($"        {Snippet.OnPropertyChanged(invoker, property.Name, underscoreFields)}")
                                           .AppendLine("    }")
                                           .AppendLine("}")
                                           .Return();
                    var template = ParseProperty(code);
                    editor.ReplaceNode(
                        setter,
                        (x, _) =>
                    {
                        var old = (AccessorDeclarationSyntax)x;
                        return(old.WithBody(template.Setter().Body)
                               .WithExpressionBody(null)
                               .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None)));
                    });
                    editor.FormatNode(propertyDeclaration);
                }

                if (setter.Body?.Statements.Count == 1 &&
                    IsSimpleAssignmentOnly(propertyDeclaration, out _, out var statement, out assignment, out _))
                {
                    var property = semanticModel.GetDeclaredSymbolSafe(propertyDeclaration, cancellationToken);
                    var code     = StringBuilderPool.Borrow()
                                   .AppendLine($"        if ({Snippet.EqualityCheck(property.Type, "value", assignment.Left.ToString(), semanticModel)})")
                                   .AppendLine("        {")
                                   .AppendLine($"           return;")
                                   .AppendLine("        }")
                                   .AppendLine()
                                   .Return();
                    var ifStatement = SyntaxFactory.ParseStatement(code)
                                      .WithSimplifiedNames()
                                      .WithLeadingElasticLineFeed()
                                      .WithTrailingElasticLineFeed()
                                      .WithAdditionalAnnotations(Formatter.Annotation);
                    editor.InsertBefore(
                        statement,
                        ifStatement);
                    var underscoreFields = CodeStyle.UnderscoreFields(semanticModel);
                    var notifyStatement  = SyntaxFactory
                                           .ParseStatement(
                        Snippet.OnPropertyChanged(invoker, property.Name, underscoreFields))
                                           .WithSimplifiedNames()
                                           .WithLeadingElasticLineFeed()
                                           .WithTrailingElasticLineFeed()
                                           .WithAdditionalAnnotations(Formatter.Annotation);
                    editor.InsertAfter(statement, notifyStatement);
                    editor.FormatNode(propertyDeclaration);
                }
            }
        }