Пример #1
0
        public async Task TestSetBaseType_ExistingBase()
        {
            var code =
                @"class C : B
{
}

class A
{
}

class B
{
}";

            var expected =
                @"class C : A
{
}

class A
{
}

class B
{
}";

            var solution = GetSolution(code);
            var symbol   = (INamedTypeSymbol)(await GetSymbolsAsync(solution, "C")).First();

            var editor = SymbolEditor.Create(solution);

            // set base to A
            var newSymbolC = await editor.SetBaseTypeAsync(symbol, g => g.IdentifierName("A"));

            var actual = await GetActualAsync(editor.GetChangedDocuments().First());

            Assert.Equal(expected, actual);
        }
Пример #2
0
        public async Task TestEditAllDeclarations_MultipleFiles()
        {
            var code1 =
                @"class C
{
}";

            var code2 =
                @"class C
{
    void M() {}
}";

            var expected1 =
                @"public class C
{
}";

            var expected2 =
                @"public class C
{
    void M() {}
}";

            var solution = GetSolution(code1, code2);
            var comp     = await solution.Projects.First().GetCompilationAsync();

            var symbol = comp.GlobalNamespace.GetMembers("C").First();

            var editor    = SymbolEditor.Create(solution);
            var newSymbol = (INamedTypeSymbol)await editor.EditAllDeclarationsAsync(symbol, (e, d) => e.SetAccessibility(d, Accessibility.Public));

            var docs    = editor.GetChangedDocuments().ToList();
            var actual1 = await GetActualAsync(docs[0]);

            var actual2 = await GetActualAsync(docs[1]);

            Assert.Equal(expected1, actual1);
            Assert.Equal(expected2, actual2);
        }
Пример #3
0
        public async Task TestSetBaseType_Null_ExistingBaseAndInterface()
        {
            var code =
                @"class C : A, I
{
}

class A
{
}

interface I
{
}";

            var expected =
                @"class C : I
{
}

class A
{
}

interface I
{
}";

            var solution = GetSolution(code);
            var symbol   = (INamedTypeSymbol)(await GetSymbolsAsync(solution, "C")).First();

            var editor = SymbolEditor.Create(solution);

            // set base to null
            var newSymbolC = await editor.SetBaseTypeAsync(symbol, g => null);

            var actual = await GetActualAsync(editor.GetChangedDocuments().First());

            Assert.Equal(expected, actual);
        }
Пример #4
0
        public async Task TestRemovedDeclarationReturnsNull()
        {
            var code =
                @"class C
{
}";

            var expected =
                @"";

            var solution = GetSolution(code);
            var symbol   = (await GetSymbolsAsync(solution, "C")).First();
            var editor   = SymbolEditor.Create(solution);

            var newSymbol = (INamedTypeSymbol)await editor.EditOneDeclarationAsync(symbol, (e, d) => e.RemoveNode(d));

            Assert.Null(newSymbol);

            var actual = await GetActualAsync(editor.GetChangedDocuments().First());

            Assert.Equal(expected, actual);
        }
Пример #5
0
        public async Task TestEditExplicitInterfaceIndexer()
        {
            var code =
                @"public interface I
{
    int this[int item] { get; }
}

public class C  : I
{
    int I.this[int item]
    {
        get
        {
            return item;
        }
    }
}";

            var solution = GetSolution(code);
            var typeC    = (INamedTypeSymbol)(await GetSymbolsAsync(solution, "C")).First();
            var property = typeC.GetMembers().First(m => m.Kind == SymbolKind.Property);

            var editor = SymbolEditor.Create(solution);

            var newProperty = editor.EditOneDeclarationAsync(property, (e, d) =>
            {
                // nothing
            });

            var typeI     = (INamedTypeSymbol)(await GetSymbolsAsync(solution, "I")).First();
            var iproperty = typeI.GetMembers().First(m => m.Kind == SymbolKind.Property);

            var newIProperty = editor.EditOneDeclarationAsync(iproperty, (e, d) =>
            {
                // nothing;
            });
        }
        private async Task <Solution> FixClass(Solution solution, INamedTypeSymbol typeSymbol, CancellationToken cancellationToken)
        {
            var symbolEditor = SymbolEditor.Create(solution);
            await symbolEditor.EditOneDeclarationAsync(typeSymbol, async (editor, declaration, ct) =>
            {
                var classDeclaration = (ClassDeclarationSyntax)declaration;
                var compilation      = editor.SemanticModel.Compilation;
                var generator        = editor.Generator;

                if (typeSymbol.IsAbstract)
                {
                    editor.SetModifiers(declaration, DeclarationModifiers.From(typeSymbol).WithIsAbstract(false));
                }

                var ctor = typeSymbol.InstanceConstructors.FirstOrDefault(c => c.Parameters.Length == 0);
                if (ctor == null)
                {
                    editor.AddMember(classDeclaration, generator.ConstructorDeclaration(accessibility: Accessibility.Public));
                }
                else if (ctor.DeclaredAccessibility != Accessibility.Public)
                {
                    // Make constructor public unless it's implicit and the class was abstract. Making the class non-abstract will make the implicit constructor public
                    if (!(ctor.IsImplicitlyDeclared && typeSymbol.IsAbstract))
                    {
                        var ctorSyntaxRef = ctor.DeclaringSyntaxReferences.FirstOrDefault();
                        editor.SetAccessibility(await ctorSyntaxRef.GetSyntaxAsync(ct).ConfigureAwait(false), Accessibility.Public);
                    }
                }

                var iEnumerableOfObjectArray = TypeSymbolFactory.IEnumerableOfObjectArray(compilation);
                if (!iEnumerableOfObjectArray.IsAssignableFrom(typeSymbol))
                {
                    editor.AddInterfaceType(classDeclaration, generator.TypeExpression(iEnumerableOfObjectArray));
                }
            }, cancellationToken).ConfigureAwait(false);

            return(symbolEditor.ChangedSolution);
        }
Пример #7
0
        private static async Task <Solution> AddMissingMembers(Document doc, SyntaxNode node, HashSet <string> missingMemberNames, CancellationToken ct)
        {
            var model = await doc.GetSemanticModelAsync(ct).ConfigureAwait(false);

            var entryPointTypeSymbol = (INamedTypeSymbol)model.GetEnclosingSymbol(node.SpanStart, ct);

            // TODO: Convert to use the IOperation tree once IAttributeOperation is available
            var managedTypeSymbolInAttribute = GetManagedTypeInAttributeSyntax(node.GetLocation(), entryPointTypeSymbol);

            bool isLinearCollectionMarshaller = ManualTypeMarshallingHelper.IsLinearCollectionEntryPoint(entryPointTypeSymbol);

            // Explicitly ignore the generic arity mismatch diagnostics as we will only reach here if there are no mismatches.
            // The analyzer will not for missing members if the managed type cannot be resolved.
            ManualTypeMarshallingHelper.TryResolveManagedType(entryPointTypeSymbol, ManualTypeMarshallingHelper.ReplaceGenericPlaceholderInType(managedTypeSymbolInAttribute, entryPointTypeSymbol, model.Compilation), isLinearCollectionMarshaller, IgnoreArityMismatch, out ITypeSymbol managedType);

            SymbolEditor editor = SymbolEditor.Create(doc.Project.Solution);

            INamedTypeSymbol marshallerType = (INamedTypeSymbol)model.GetSymbolInfo(node, ct).Symbol;

            await editor.EditOneDeclarationAsync(marshallerType, (editor, decl) => AddMissingMembers(editor, decl, marshallerType, managedType, missingMemberNames, isLinearCollectionMarshaller), ct).ConfigureAwait(false);

            return(editor.ChangedSolution);
        }
        private static async Task <Document> GenerateConstructorAsync(Document document, SyntaxNode node, INamedTypeSymbol typeSymbol, INamedTypeSymbol notImplementedExceptionType, CancellationToken cancellationToken)
        {
            SymbolEditor editor = SymbolEditor.Create(document);

            await editor.EditOneDeclarationAsync(typeSymbol, node.GetLocation(), (docEditor, declaration) =>
            {
                SyntaxGenerator generator = docEditor.Generator;
                SyntaxNode throwStatement = generator.ThrowStatement(generator.ObjectCreationExpression(generator.TypeExpression(notImplementedExceptionType)));
                SyntaxNode ctorDecl       = generator.ConstructorDeclaration(
                    typeSymbol.Name,
                    new[]
                {
                    generator.ParameterDeclaration("serializationInfo", generator.TypeExpression(docEditor.SemanticModel.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeSerializationSerializationInfo))),
                    generator.ParameterDeclaration("streamingContext", generator.TypeExpression(docEditor.SemanticModel.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeSerializationStreamingContext)))
                },
                    typeSymbol.IsSealed ? Accessibility.Private : Accessibility.Protected,
                    statements: new[] { throwStatement });

                docEditor.AddMember(declaration, ctorDecl);
            }, cancellationToken).ConfigureAwait(false);

            return(editor.GetChangedDocuments().First());
        }
Пример #9
0
        private static async Task <Document> MakeProtectedAsync(Document document, ISymbol symbolToChange, bool checkSetter, CancellationToken cancellationToken)
        {
            SymbolEditor editor = SymbolEditor.Create(document);

            ISymbol?getter = null;
            ISymbol?setter = null;

            if (symbolToChange.Kind == SymbolKind.Property)
            {
                var propertySymbol = (IPropertySymbol)symbolToChange;
                getter = propertySymbol.GetMethod;
                setter = propertySymbol.SetMethod;
            }

            await editor.EditAllDeclarationsAsync(symbolToChange, (docEditor, declaration) =>
            {
                docEditor.SetAccessibility(declaration, Accessibility.Protected);
            }, cancellationToken).ConfigureAwait(false);

            if (getter != null && getter.DeclaredAccessibility == Accessibility.Private)
            {
                await editor.EditAllDeclarationsAsync(getter, (docEditor, declaration) =>
                {
                    docEditor.SetAccessibility(declaration, Accessibility.NotApplicable);
                }, cancellationToken).ConfigureAwait(false);
            }

            if (checkSetter && setter != null && setter.DeclaredAccessibility == Accessibility.Private)
            {
                await editor.EditAllDeclarationsAsync(setter, (docEditor, declaration) =>
                {
                    docEditor.SetAccessibility(declaration, Accessibility.NotApplicable);
                }, cancellationToken).ConfigureAwait(false);
            }

            return(editor.GetChangedDocuments().First());
        }
Пример #10
0
        public async Task TestSequentialEdits()
        {
            var code =
                @"class C
{
}";

            var expected =
                @"class C
{
    void m()
    {
    }

    void m2()
    {
    }
}";

            var solution = GetSolution(code);
            var symbol   = (await GetSymbolsAsync(solution, "C")).First();
            var editor   = SymbolEditor.Create(solution);

            var newSymbol = (INamedTypeSymbol)await editor.EditOneDeclarationAsync(symbol, (e, d) => e.AddMember(d, Generator.MethodDeclaration("m")));

            Assert.Equal(1, newSymbol.GetMembers("m").Length);
            Assert.Equal(0, newSymbol.GetMembers("m2").Length);

            newSymbol = (INamedTypeSymbol)await editor.EditOneDeclarationAsync(symbol, (e, d) => e.AddMember(d, Generator.MethodDeclaration("m2")));

            Assert.Equal(1, newSymbol.GetMembers("m").Length);
            Assert.Equal(1, newSymbol.GetMembers("m2").Length);

            var actual = await GetActualAsync(editor.GetChangedDocuments().First());

            Assert.Equal(expected, actual);
        }
Пример #11
0
        public async Task TestChangeLogicalIdentityReturnsCorrectSymbol_AllDeclarations()
        {
            // proves that APIs return the correct new symbol even after a change that changes the symbol's logical identity.
            var code =
                @"partial class C
{
}

partial class C
{
}";

            var expected =
                @"partial class X
{
}

partial class X
{
}";
            var solution = GetSolution(code);
            var symbol   = (await GetSymbolsAsync(solution, "C")).First();
            var editor   = SymbolEditor.Create(solution);

            var newSymbol = (INamedTypeSymbol)await editor.EditAllDeclarationsAsync(symbol, (e, d) => e.SetName(d, "X"));

            Assert.Equal("X", newSymbol.Name);

            // original symbols cannot be rebound after identity change.
            var reboundSymbol = await editor.GetCurrentSymbolAsync(symbol);

            Assert.Null(reboundSymbol);

            var actual = await GetActualAsync(editor.GetChangedDocuments().First());

            Assert.Equal(expected, actual);
        }
Пример #12
0
        public void TestEditDeclarationWithLocation_First()
        {
            var code =
                @"partial class C
{
}

partial class C
{
}";

            var expected =
                @"partial class C
{
    void m()
    {
    }
}

partial class C
{
}";

            var solution = GetSolution(code);
            var symbol   = GetSymbols(solution, "C").First();
            var location = symbol.Locations.First();
            var editor   = SymbolEditor.Create(solution);

            var newSymbol = (INamedTypeSymbol)editor.EditOneDeclarationAsync(symbol, location, (e, d) => e.AddMember(d, e.Generator.MethodDeclaration("m"))).Result;

            Assert.Equal(1, newSymbol.GetMembers("m").Length);

            var actual = GetActual(editor.GetChangedDocuments().First());

            Assert.Equal(expected, actual);
        }
Пример #13
0
        public void TestSetBaseType_Null_UnknownBase()
        {
            var code =
                @"class C : X
{
}";

            var expected =
                @"class C
{
}";

            var solution = GetSolution(code);
            var symbol   = (INamedTypeSymbol)GetSymbols(solution, "C").First();

            var editor = SymbolEditor.Create(solution);

            // set base to null
            var newSymbolC = editor.SetBaseTypeAsync(symbol, g => null).Result;

            var actual = GetActual(editor.GetChangedDocuments().First());

            Assert.Equal(expected, actual);
        }
        private async Task <Document> GenerateConstructor(Document document, SyntaxNode node, ISymbol symbol, CancellationToken cancellationToken)
        {
            var editor     = SymbolEditor.Create(document);
            var typeSymbol = symbol as INamedTypeSymbol;

            await editor.EditOneDeclarationAsync(typeSymbol, node.GetLocation(), (docEditor, declaration) =>
            {
                var generator      = docEditor.Generator;
                var throwStatement = generator.ThrowStatement(generator.ObjectCreationExpression(generator.DottedName("System.NotImplementedException")));
                var ctorDecl       = generator.ConstructorDeclaration(
                    typeSymbol.Name,
                    new[]
                {
                    generator.ParameterDeclaration("serializationInfo", generator.TypeExpression(WellKnownTypes.SerializationInfo(docEditor.SemanticModel.Compilation))),
                    generator.ParameterDeclaration("streamingContext", generator.TypeExpression(WellKnownTypes.StreamingContext(docEditor.SemanticModel.Compilation)))
                },
                    typeSymbol.IsSealed ? Accessibility.Private : Accessibility.Protected,
                    statements: new[] { throwStatement });

                docEditor.AddMember(declaration, ctorDecl);
            }, cancellationToken);

            return(editor.GetChangedDocuments().First());
        }
        private static async Task <Document> FixAsync(CodeFixContext context, CancellationToken cancellationToken)
        {
            var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);

            var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);

            var generator = SyntaxGenerator.GetGenerator(context.Document);

            SyntaxNode node       = root.FindNode(context.Span);
            Diagnostic diagnostic = context.Diagnostics.First();

            switch (diagnostic.Properties[OperatorOverloadsHaveNamedAlternatesAnalyzer.DiagnosticKindText])
            {
            case OperatorOverloadsHaveNamedAlternatesAnalyzer.AddAlternateText:
                SyntaxNode       methodDeclaration      = generator.GetDeclaration(node, DeclarationKind.Operator) ?? generator.GetDeclaration(node, DeclarationKind.ConversionOperator);
                var              operatorOverloadSymbol = (IMethodSymbol)semanticModel.GetDeclaredSymbol(methodDeclaration, cancellationToken);
                INamedTypeSymbol typeSymbol             = operatorOverloadSymbol.ContainingType;

                // For C# the following `typeDeclarationSyntax` and `typeDeclaration` nodes are identical, but for VB they're different so in
                // an effort to keep this as language-agnostic as possible, the heavy-handed approach is used.
                SyntaxNode typeDeclarationSyntax = await typeSymbol.DeclaringSyntaxReferences.First().GetSyntaxAsync(cancellationToken).ConfigureAwait(false);

                SyntaxNode typeDeclaration = generator.GetDeclaration(typeDeclarationSyntax,
                                                                      typeSymbol.TypeKind == TypeKind.Struct ? DeclarationKind.Struct : DeclarationKind.Class);

                SyntaxNode addedMember;
                IEnumerable <SyntaxNode> bodyStatements = generator.DefaultMethodBody(semanticModel.Compilation);
                if (OperatorOverloadsHaveNamedAlternatesAnalyzer.IsPropertyExpected(operatorOverloadSymbol.Name))
                {
                    // add a property
                    addedMember = generator.PropertyDeclaration(
                        name: OperatorOverloadsHaveNamedAlternatesAnalyzer.IsTrueText,
                        type: generator.TypeExpression(SpecialType.System_Boolean),
                        accessibility: Accessibility.Public,
                        modifiers: DeclarationModifiers.ReadOnly,
                        getAccessorStatements: bodyStatements);
                }
                else
                {
                    // add a method
                    ExpectedMethodSignature?expectedSignature = GetExpectedMethodSignature(operatorOverloadSymbol, semanticModel.Compilation);
                    if (expectedSignature == null)
                    {
                        return(context.Document);
                    }

                    if (expectedSignature.Name == "CompareTo" && operatorOverloadSymbol.ContainingType.TypeKind == TypeKind.Class)
                    {
                        var nullCheck = generator.IfStatement(
                            generator.InvocationExpression(
                                generator.IdentifierName("ReferenceEquals"),
                                generator.IdentifierName(expectedSignature.Parameters.First().name),
                                generator.NullLiteralExpression()),
                            new[]
                        {
                            generator.ReturnStatement(generator.LiteralExpression(1))
                        });

                        bodyStatements = new[] { nullCheck }.Concat(bodyStatements);
                    }

                    addedMember = generator.MethodDeclaration(
                        name: expectedSignature.Name,
                        parameters: expectedSignature.Parameters.Select(p => generator.ParameterDeclaration(p.name, generator.TypeExpression(p.typeSymbol))),
                        returnType: generator.TypeExpression(expectedSignature.ReturnType),
                        accessibility: Accessibility.Public,
                        modifiers: expectedSignature.IsStatic ? DeclarationModifiers.Static : DeclarationModifiers.None,
                        statements: bodyStatements);
                }

                SyntaxNode newTypeDeclaration = generator.AddMembers(typeDeclaration, addedMember);
                return(context.Document.WithSyntaxRoot(root.ReplaceNode(typeDeclaration, newTypeDeclaration)));

            case OperatorOverloadsHaveNamedAlternatesAnalyzer.FixVisibilityText:
                SyntaxNode   badVisibilityNode   = generator.GetDeclaration(node, DeclarationKind.Method) ?? generator.GetDeclaration(node, DeclarationKind.Property);
                ISymbol      badVisibilitySymbol = semanticModel.GetDeclaredSymbol(badVisibilityNode, cancellationToken);
                SymbolEditor symbolEditor        = SymbolEditor.Create(context.Document);
                ISymbol      newSymbol           = await symbolEditor.EditOneDeclarationAsync(badVisibilitySymbol,
                                                                                              (documentEditor, syntaxNode) => documentEditor.SetAccessibility(badVisibilityNode, Accessibility.Public), cancellationToken).ConfigureAwait(false);

                Document   newDocument = symbolEditor.GetChangedDocuments().Single();
                SyntaxNode newRoot     = await newDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

                return(context.Document.WithSyntaxRoot(newRoot));

            default:
                return(context.Document);
            }
        }
        private static async Task <Document> Fix(CodeFixContext context, SyntaxNode root, SyntaxGenerator generator, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            SyntaxNode node       = root.FindNode(context.Span);
            Diagnostic diagnostic = context.Diagnostics.First();

            switch (diagnostic.Properties[OperatorOverloadsHaveNamedAlternatesAnalyzer.DiagnosticKindText])
            {
            case OperatorOverloadsHaveNamedAlternatesAnalyzer.AddAlternateText:
                SyntaxNode       methodDeclaration      = generator.GetDeclaration(node, DeclarationKind.Operator) ?? generator.GetDeclaration(node, DeclarationKind.ConversionOperator);
                var              operatorOverloadSymbol = (IMethodSymbol)semanticModel.GetDeclaredSymbol(methodDeclaration, cancellationToken);
                INamedTypeSymbol typeSymbol             = operatorOverloadSymbol.ContainingType;

                // For C# the following `typeDeclarationSyntax` and `typeDeclaration` nodes are identical, but for VB they're different so in
                // an effort to keep this as language-agnostic as possible, the heavy-handed approach is used.
                SyntaxNode typeDeclarationSyntax = typeSymbol.DeclaringSyntaxReferences.First().GetSyntax(cancellationToken);
                SyntaxNode typeDeclaration       = generator.GetDeclaration(typeDeclarationSyntax, DeclarationKind.Class);

                SyntaxNode addedMember;
                ImmutableArray <SyntaxNode> bodyStatements = ImmutableArray.Create(
                    generator.ThrowStatement(generator.ObjectCreationExpression(semanticModel.Compilation.GetTypeByMetadataName("System.NotImplementedException"))));
                if (OperatorOverloadsHaveNamedAlternatesAnalyzer.IsPropertyExpected(operatorOverloadSymbol.Name))
                {
                    // add a property
                    addedMember = generator.PropertyDeclaration(
                        name: OperatorOverloadsHaveNamedAlternatesAnalyzer.IsTrueText,
                        type: generator.TypeExpression(SpecialType.System_Boolean),
                        accessibility: Accessibility.Public,
                        modifiers: DeclarationModifiers.ReadOnly,
                        getAccessorStatements: bodyStatements);
                }
                else
                {
                    // add a method
                    ExpectedMethodSignature expectedSignature = GetExpectedMethodSignature(operatorOverloadSymbol, semanticModel.Compilation);
                    addedMember = generator.MethodDeclaration(
                        name: expectedSignature.Name,
                        parameters: expectedSignature.Parameters.Select(p => generator.ParameterDeclaration(p.Item1, generator.TypeExpression(p.Item2))),
                        returnType: generator.TypeExpression(expectedSignature.ReturnType),
                        accessibility: Accessibility.Public,
                        modifiers: expectedSignature.IsStatic ? DeclarationModifiers.Static : DeclarationModifiers.None,
                        statements: bodyStatements);
                }

                SyntaxNode newTypeDeclaration = generator.AddMembers(typeDeclaration, addedMember);
                return(context.Document.WithSyntaxRoot(root.ReplaceNode(typeDeclaration, newTypeDeclaration)));

            case OperatorOverloadsHaveNamedAlternatesAnalyzer.FixVisibilityText:
                SyntaxNode   badVisibilityNode   = generator.GetDeclaration(node, DeclarationKind.Method) ?? generator.GetDeclaration(node, DeclarationKind.Property);
                ISymbol      badVisibilitySymbol = semanticModel.GetDeclaredSymbol(badVisibilityNode, cancellationToken);
                SymbolEditor symbolEditor        = SymbolEditor.Create(context.Document);
                ISymbol      newSymbol           = await symbolEditor.EditOneDeclarationAsync(badVisibilitySymbol,
                                                                                              (documentEditor, syntaxNode) => documentEditor.SetAccessibility(badVisibilityNode, Accessibility.Public));

                Document   newDocument = symbolEditor.GetChangedDocuments().Single();
                SyntaxNode newRoot     = await newDocument.GetSyntaxRootAsync(cancellationToken);

                return(context.Document.WithSyntaxRoot(newRoot));

            default:
                return(context.Document);
            }
        }