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); }
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); }
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); }
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); }
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); }
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()); }
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()); }
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); }
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); }
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); }
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); } }