private async Task ApplyRuleNameNoZeroValueAsync(SymbolEditor editor, INamedTypeSymbol enumType, CancellationToken cancellationToken) { // remove any non-zero member named 'None' foreach (IFieldSymbol field in enumType.GetMembers().Where(m => m.Kind == SymbolKind.Field)) { if (CA1008DiagnosticAnalyzer.IsMemberNamedNone(field)) { await editor.EditOneDeclarationAsync(field, (e, d) => e.RemoveNode(d), cancellationToken).ConfigureAwait(false); } } // insert zero-valued member 'None' to top await editor.EditOneDeclarationAsync(enumType, (e, d) => e.InsertMembers(d, 0, new[] { e.Generator.EnumMember("None") }), cancellationToken).ConfigureAwait(false); }
private async Task <Document> AddAccessor(Document document, SyntaxNode parameter, CancellationToken cancellationToken) { SymbolEditor symbolEditor = SymbolEditor.Create(document); SemanticModel model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var parameterSymbol = model.GetDeclaredSymbol(parameter, cancellationToken) as IParameterSymbol; if (parameterSymbol == null) { return(document); } // Make the first character uppercase since we are generating a property. string propName = char.ToUpper(parameterSymbol.Name[0]).ToString() + parameterSymbol.Name.Substring(1); INamedTypeSymbol typeSymbol = parameterSymbol.ContainingType; ISymbol propertySymbol = typeSymbol.GetMembers(propName).Where(m => m.Kind == SymbolKind.Property).FirstOrDefault(); // Add a new property if (propertySymbol == null) { await symbolEditor.EditOneDeclarationAsync(typeSymbol, parameter.GetLocation(), // edit the partial declaration that has this parameter symbol. (editor, typeDeclaration) => { SyntaxNode newProperty = editor.Generator.PropertyDeclaration(propName, editor.Generator.TypeExpression(parameterSymbol.Type), Accessibility.Public, DeclarationModifiers.ReadOnly); newProperty = editor.Generator.WithGetAccessorStatements(newProperty, null); editor.AddMember(typeDeclaration, newProperty); }, cancellationToken).ConfigureAwait(false); } else { await symbolEditor.EditOneDeclarationAsync(propertySymbol, (editor, propertyDeclaration) => { editor.SetGetAccessorStatements(propertyDeclaration, null); editor.SetModifiers(propertyDeclaration, editor.Generator.GetModifiers(propertyDeclaration) - DeclarationModifiers.WriteOnly); }, cancellationToken).ConfigureAwait(false); } return(symbolEditor.GetChangedDocuments().First()); }
public override async Task <CodeAction?> GetFixAsync(FixAllContext fixAllContext) { ImmutableArray <Diagnostic> diagnostics = await GetAllDiagnosticsInScope(fixAllContext).ConfigureAwait(false); Dictionary <(INamedTypeSymbol marshallerType, ITypeSymbol managedType, bool isLinearCollectionMarshaller), HashSet <string> > uniqueMarshallersToFix = new(); // Organize all the diagnostics by marshaller, managed type, and whether or not it's a collection marshaller foreach (Diagnostic diagnostic in diagnostics) { Document doc = fixAllContext.Solution.GetDocument(diagnostic.Location.SourceTree); SemanticModel model = await doc.GetSemanticModelAsync(fixAllContext.CancellationToken).ConfigureAwait(false); var entryPointTypeSymbol = (INamedTypeSymbol)model.GetEnclosingSymbol(diagnostic.Location.SourceSpan.Start, fixAllContext.CancellationToken); ITypeSymbol?managedType = GetManagedTypeInAttributeSyntax(diagnostic.Location, entryPointTypeSymbol); SyntaxNode root = await diagnostic.Location.SourceTree.GetRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false); SyntaxNode node = root.FindNode(diagnostic.Location.SourceSpan); var marshallerType = (INamedTypeSymbol)model.GetSymbolInfo(node, fixAllContext.CancellationToken).Symbol; var uniqueMarshallerFixKey = (marshallerType, managedType, ManualTypeMarshallingHelper.IsLinearCollectionEntryPoint(entryPointTypeSymbol)); if (!uniqueMarshallersToFix.TryGetValue(uniqueMarshallerFixKey, out HashSet <string> membersToAdd)) { uniqueMarshallersToFix[uniqueMarshallerFixKey] = membersToAdd = new HashSet <string>(); } membersToAdd.UnionWith(diagnostic.Properties[MissingMemberNames.Key].Split(MissingMemberNames.Delimiter)); } Dictionary <INamedTypeSymbol, INamedTypeSymbol> partiallyUpdatedSymbols = new(SymbolEqualityComparer.Default); SymbolEditor symbolEditor = SymbolEditor.Create(fixAllContext.Solution); // Apply each fix foreach (var marshallerInfo in uniqueMarshallersToFix) { var(marshallerType, managedType, isLinearCollectionMarshaller) = marshallerInfo.Key; HashSet <string> missingMembers = marshallerInfo.Value; if (!partiallyUpdatedSymbols.TryGetValue(marshallerType, out INamedTypeSymbol newMarshallerType)) { newMarshallerType = marshallerType; } newMarshallerType = (INamedTypeSymbol)await symbolEditor.EditOneDeclarationAsync( marshallerType, (editor, decl) => AddMissingMembers( editor, decl, marshallerType, managedType, missingMembers, isLinearCollectionMarshaller), fixAllContext.CancellationToken).ConfigureAwait(false); partiallyUpdatedSymbols[marshallerType] = newMarshallerType; } return(CodeAction.Create(SR.AddMissingCustomTypeMarshallerMembers, ct => Task.FromResult(symbolEditor.ChangedSolution))); }
private static async Task <Document> ApplyRuleNameMultipleZeroAsync(Document document, INamedTypeSymbol enumType, CancellationToken cancellationToken) { // Diagnostic: Remove all members that have the value zero from '{0}' except for one member that is named 'None'. // Fix: Remove all members that have the value zero except for one member that is named 'None'. SymbolEditor editor = SymbolEditor.Create(document); bool needsNewZeroValuedNoneField = true; ISet <IFieldSymbol> set = EnumsShouldHaveZeroValueAnalyzer.GetZeroValuedFields(enumType).ToSet(); bool makeNextFieldExplicit = false; foreach (IFieldSymbol field in enumType.GetMembers().Where(m => m.Kind == SymbolKind.Field)) { bool isZeroValued = set.Contains(field); bool isZeroValuedNamedNone = isZeroValued && EnumsShouldHaveZeroValueAnalyzer.IsMemberNamedNone(field); if (!isZeroValued || isZeroValuedNamedNone) { if (makeNextFieldExplicit) { await editor.EditOneDeclarationAsync(field, (e, d) => e.ReplaceNode(d, GetExplicitlyAssignedField(field, d, e.Generator)), cancellationToken).ConfigureAwait(false); makeNextFieldExplicit = false; } if (isZeroValuedNamedNone) { needsNewZeroValuedNoneField = false; } } else { await editor.EditOneDeclarationAsync(field, (e, d) => e.RemoveNode(d), cancellationToken).ConfigureAwait(false); // removes the field declaration makeNextFieldExplicit = true; } } if (needsNewZeroValuedNoneField) { await editor.EditOneDeclarationAsync(enumType, (e, d) => e.InsertMembers(d, 0, new[] { e.Generator.EnumMember("None") }), cancellationToken).ConfigureAwait(false); } return(editor.GetChangedDocuments().First()); }
private static async Task <Document> ApplyRuleNameNoZeroValueAsync(Document document, INamedTypeSymbol enumType, CancellationToken cancellationToken) { SymbolEditor editor = SymbolEditor.Create(document); // remove any non-zero member named 'None' foreach (IFieldSymbol field in enumType.GetMembers().Where(m => m.Kind == SymbolKind.Field)) { if (EnumsShouldHaveZeroValueAnalyzer.IsMemberNamedNone(field)) { await editor.EditOneDeclarationAsync(field, (e, d) => e.RemoveNode(d), cancellationToken).ConfigureAwait(false); } } // insert zero-valued member 'None' to top await editor.EditOneDeclarationAsync(enumType, (e, d) => e.InsertMembers(d, 0, new[] { e.Generator.EnumMember("None") }), cancellationToken).ConfigureAwait(false); return(editor.GetChangedDocuments().First()); }
private async Task ApplyRuleNameMultipleZeroAsync(SymbolEditor editor, INamedTypeSymbol enumType, CancellationToken cancellationToken) { // Diagnostic: Remove all members that have the value zero from '{0}' except for one member that is named 'None'. // Fix: Remove all members that have the value zero except for one member that is named 'None'. bool needsNewZeroValuedNoneField = true; var set = CA1008DiagnosticAnalyzer.GetZeroValuedFields(enumType).ToSet(); bool makeNextFieldExplicit = false; foreach (IFieldSymbol field in enumType.GetMembers().Where(m => m.Kind == SymbolKind.Field)) { var isZeroValued = set.Contains(field); var isZeroValuedNamedNone = isZeroValued && CA1008DiagnosticAnalyzer.IsMemberNamedNone(field); if (!isZeroValued || isZeroValuedNamedNone) { if (makeNextFieldExplicit) { await editor.EditOneDeclarationAsync(field, (e, d) => e.ReplaceNode(d, GetExplicitlyAssignedField(field, d, e.Generator)), cancellationToken); makeNextFieldExplicit = false; } if (isZeroValuedNamedNone) { needsNewZeroValuedNoneField = false; } } else { await editor.EditOneDeclarationAsync(field, (e, d) => e.RemoveNode(d), cancellationToken); // removes the field declaration makeNextFieldExplicit = true; } } if (needsNewZeroValuedNoneField) { await editor.EditOneDeclarationAsync(enumType, (e, d) => e.InsertMembers(d, 0, new[] { e.Generator.EnumMember("None") }), cancellationToken); } }
private static async Task <Document> AddSerializableAttributeToType(Document document, ITypeSymbol type, CancellationToken cancellationToken) { SymbolEditor editor = SymbolEditor.Create(document); await editor.EditOneDeclarationAsync(type, (docEditor, declaration) => { SyntaxNode serializableAttr = docEditor.Generator.Attribute(docEditor.Generator.TypeExpression(WellKnownTypes.SerializableAttribute(docEditor.SemanticModel.Compilation))); docEditor.AddAttribute(declaration, serializableAttr); }, cancellationToken).ConfigureAwait(false); return(editor.GetChangedDocuments().First()); }
private static async Task <Document> SetAccessibilityAsync(Document document, IMethodSymbol methodSymbol, CancellationToken cancellationToken) { SymbolEditor editor = SymbolEditor.Create(document); // This would be constructor and can have only one definition. Debug.Assert(methodSymbol.IsConstructor() && methodSymbol.DeclaringSyntaxReferences.HasExactly(1)); await editor.EditOneDeclarationAsync(methodSymbol, (docEditor, declaration) => { Accessibility newAccessibility = methodSymbol.ContainingType.IsSealed ? Accessibility.Private : Accessibility.Protected; docEditor.SetAccessibility(declaration, newAccessibility); }, cancellationToken).ConfigureAwait(false); return(editor.GetChangedDocuments().First()); }
private async Task <Document> AddDisposeCall(Document document, SyntaxNode declaration, IFieldSymbol fieldSymbol, IMethodSymbol disposeMethod, ITypeSymbol iDisposableType, ISymbol iDisposable_Dispose, CancellationToken cancellationToken) { SymbolEditor editor = SymbolEditor.Create(document); await editor.EditOneDeclarationAsync(disposeMethod, (docEditor, disposeMethodNode) => { var generator = docEditor.Generator; // handle the case where a local in the Dispose method exists with the same name by generating this (or ClassName) and simplifying it var path = fieldSymbol.IsStatic ? generator.IdentifierName(fieldSymbol.ContainingType.MetadataName) : generator.ThisExpression(); var disposeMethodOfFieldType = fieldSymbol.Type.FindImplementationForInterfaceMember(iDisposable_Dispose) as IMethodSymbol; // If the original interface was implemented implicitly then we can simply invoke through the name of the implementation. // Note that in VB, the name of the method implementing the interface method can be different from the interface method name. SyntaxNode statement; if (disposeMethodOfFieldType.CanBeReferencedByName) { statement = generator.ExpressionStatement( generator.InvocationExpression( generator.MemberAccessExpression( generator.MemberAccessExpression(path, generator.IdentifierName(fieldSymbol.Name)).WithAdditionalAnnotations(Simplifier.Annotation), generator.IdentifierName(disposeMethodOfFieldType.Name)))); } // Otherwise dispatch through the interface else { statement = generator.ExpressionStatement( generator.InvocationExpression( generator.MemberAccessExpression( generator.CastExpression(iDisposableType, generator.MemberAccessExpression(path, generator.IdentifierName(fieldSymbol.Name)).WithAdditionalAnnotations(Simplifier.Annotation)), generator.IdentifierName(DisposableFieldsShouldBeDisposedAnalyzer.Dispose)))); } var body = generator.GetStatements(disposeMethodNode); docEditor.SetStatements(disposeMethodNode, body.Concat(ImmutableArray.Create(statement))); }).ConfigureAwait(false); return(editor.GetChangedDocuments().First()); }
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> 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); } }