private static bool TryCreateDisposeStatement(ArgumentSyntax argument, SemanticModel semanticModel, CancellationToken cancellationToken, out StatementSyntax result) { var symbol = semanticModel.GetSymbolSafe(argument.Expression, cancellationToken); if (symbol == null) { result = null; return(false); } if (!Disposable.IsAssignableTo(MemberType(symbol))) { result = SyntaxFactory.ParseStatement($"({argument.Expression} as System.IDisposable)?.Dispose();") .WithLeadingTrivia(SyntaxFactory.ElasticMarker) .WithTrailingTrivia(SyntaxFactory.ElasticMarker) .WithSimplifiedNames(); return(true); } if (IsAlwaysAssigned(symbol)) { result = SyntaxFactory.ParseStatement($"{argument.Expression}.Dispose();") .WithLeadingTrivia(SyntaxFactory.ElasticMarker) .WithTrailingTrivia(SyntaxFactory.ElasticMarker); return(true); } result = SyntaxFactory.ParseStatement($"{argument.Expression}?.Dispose();") .WithLeadingTrivia(SyntaxFactory.ElasticMarker) .WithTrailingTrivia(SyntaxFactory.ElasticMarker); return(true); }
private static bool IsDisposableReturnTypeOrIgnored(ITypeSymbol type) { if (type == null || type == KnownSymbol.Void) { return(true); } if (Disposable.IsAssignableTo(type)) { return(true); } if (type == KnownSymbol.IEnumerator) { return(true); } if (type == KnownSymbol.Task) { var namedType = type as INamedTypeSymbol; return(namedType?.IsGenericType == true && Disposable.IsAssignableTo(namedType.TypeArguments[0])); } if (type == KnownSymbol.Func) { var namedType = type as INamedTypeSymbol; return(namedType?.IsGenericType == true && Disposable.IsAssignableTo(namedType.TypeArguments[namedType.TypeArguments.Length - 1])); } return(false); }
private static void HandleAssignment(SyntaxNodeAnalysisContext context) { if (context.IsExcludedFromAnalysis()) { return; } var assignment = context.Node as AssignmentExpressionSyntax; if (assignment == null) { return; } var parameter = context.SemanticModel.GetSymbolSafe(assignment.Left, context.CancellationToken) as IParameterSymbol; if (parameter == null || parameter.ContainingSymbol.DeclaredAccessibility == Accessibility.Private || parameter.RefKind != RefKind.Ref) { return; } if (Disposable.IsAssignableTo(context.SemanticModel.GetTypeInfoSafe(assignment.Right, context.CancellationToken).Type)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation())); } }
private static void CheckThatIDisposalbeIsImplemented(SyntaxNodeAnalysisContext context) { var containingType = context.ContainingSymbol.ContainingType; if (!Disposable.IsAssignableTo(containingType) || !Disposable.TryGetDisposeMethod(containingType, Search.TopLevel, out IMethodSymbol _)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation())); } }
private static bool TryCreateDisposeStatement(AssignmentExpressionSyntax assignment, SemanticModel semanticModel, CancellationToken cancellationToken, out StatementSyntax result) { result = null; if (!(assignment.Parent is StatementSyntax && assignment.Parent.Parent is BlockSyntax)) { return(false); } if (Disposable.IsAssignedWithCreated(assignment.Left, semanticModel, cancellationToken, out var assignedSymbol) .IsEither(Result.No, Result.Unknown)) { return(false); } var prefix = (assignedSymbol is IPropertySymbol || assignedSymbol is IFieldSymbol) && !assignment.UsesUnderscore(semanticModel, cancellationToken) ? "this." : string.Empty; if (!Disposable.IsAssignableTo(MemberType(assignedSymbol))) { result = SyntaxFactory.ParseStatement($"({prefix}{assignment.Left} as System.IDisposable)?.Dispose();") .WithLeadingTrivia(SyntaxFactory.ElasticMarker) .WithTrailingTrivia(SyntaxFactory.ElasticMarker) .WithSimplifiedNames(); return(true); } if (IsAlwaysAssigned(assignedSymbol)) { result = SyntaxFactory.ParseStatement($"{prefix}{assignedSymbol.Name}.Dispose();") .WithLeadingTrivia(SyntaxFactory.ElasticMarker) .WithTrailingTrivia(SyntaxFactory.ElasticMarker); return(true); } result = SyntaxFactory.ParseStatement($"{prefix}{assignedSymbol.Name}?.Dispose();") .WithLeadingTrivia(SyntaxFactory.ElasticMarker) .WithTrailingTrivia(SyntaxFactory.ElasticMarker); return(true); }
private static void HandleMethod(SyntaxNodeAnalysisContext context) { if (context.IsExcludedFromAnalysis()) { return; } var method = (IMethodSymbol)context.ContainingSymbol; if (method.IsStatic || method.Name != "Dispose" || !method.ReturnsVoid || method.Parameters.Length != 0) { return; } var containingType = method.ContainingType; if (!Disposable.IsAssignableTo(containingType)) { context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation())); } }
private static StatementSyntax CreateDisposeStatement(ISymbol member, SemanticModel semanticModel, CancellationToken cancellationToken, bool usesUnderScoreNames) { var prefix = usesUnderScoreNames ? string.Empty : "this."; if (!Disposable.IsAssignableTo(MemberType(member))) { return(SyntaxFactory.ParseStatement($"({prefix}{member.Name} as System.IDisposable)?.Dispose();") .WithLeadingTrivia(SyntaxFactory.ElasticMarker) .WithTrailingTrivia(SyntaxFactory.ElasticMarker) .WithSimplifiedNames()); } if (IsReadOnly(member) && IsNeverNull(member, semanticModel, cancellationToken)) { return(SyntaxFactory.ParseStatement($"{prefix}{member.Name}.Dispose();") .WithLeadingTrivia(SyntaxFactory.ElasticMarker) .WithTrailingTrivia(SyntaxFactory.ElasticMarker)); } return(SyntaxFactory.ParseStatement($"{prefix}{member.Name}?.Dispose();") .WithLeadingTrivia(SyntaxFactory.ElasticMarker) .WithTrailingTrivia(SyntaxFactory.ElasticMarker)); }
/// <inheritdoc/> public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken) .ConfigureAwait(false); if (syntaxRoot == null) { return; } var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken) .ConfigureAwait(false); foreach (var diagnostic in context.Diagnostics) { if (!IsSupportedDiagnostic(diagnostic)) { continue; } var token = syntaxRoot.FindToken(diagnostic.Location.SourceSpan.Start); if (string.IsNullOrEmpty(token.ValueText) || token.IsMissing) { continue; } var classDeclaration = syntaxRoot.FindNode(diagnostic.Location.SourceSpan).FirstAncestorOrSelf <ClassDeclarationSyntax>(); if (classDeclaration == null) { continue; } var type = semanticModel.GetDeclaredSymbolSafe(classDeclaration, context.CancellationToken); if (diagnostic.Id == IDISP009IsIDisposable.DiagnosticId) { context.RegisterCodeFix( CodeAction.Create( "Add IDisposable interface", cancellationToken => AddInterfaceAsync( context, cancellationToken, classDeclaration), nameof(ImplementIDisposableCodeFixProvider) + "add interface"), diagnostic); continue; } if (Disposable.IsAssignableTo(type) && Disposable.BaseTypeHasVirtualDisposeMethod(type)) { context.RegisterCodeFix( CodeAction.Create( "override Dispose(bool)", cancellationToken => OverrideDisposeAsync( context, semanticModel, cancellationToken, classDeclaration), nameof(ImplementIDisposableCodeFixProvider) + "override"), diagnostic); continue; } if (type.TryGetMethod("Dispose", out var disposeMethod) && !disposeMethod.IsStatic && disposeMethod.ReturnsVoid && disposeMethod.Parameters.Length == 0) { continue; } if (type.TryGetField("disposed", out _) || type.TryGetField("_disposed", out _)) { return; } if (type.IsSealed) { context.RegisterCodeFix( CodeAction.Create( "Implement IDisposable.", cancellationToken => ImplementIDisposableSealedAsync( context, semanticModel, cancellationToken, classDeclaration), nameof(ImplementIDisposableCodeFixProvider) + "Sealed"), diagnostic); continue; } if (type.IsAbstract) { context.RegisterCodeFix( CodeAction.Create( "Implement IDisposable with virtual dispose method.", cancellationToken => ImplementIDisposableVirtualAsync( context, semanticModel, cancellationToken, classDeclaration), nameof(ImplementIDisposableCodeFixProvider) + "Virtual"), diagnostic); continue; } context.RegisterCodeFix( CodeAction.Create( "Implement IDisposable and make class sealed.", cancellationToken => ImplementIDisposableSealedAsync( context, semanticModel, cancellationToken, classDeclaration), nameof(ImplementIDisposableCodeFixProvider) + "Sealed"), diagnostic); context.RegisterCodeFix( CodeAction.Create( "Implement IDisposable with virtual dispose method.", cancellationToken => ImplementIDisposableVirtualAsync( context, semanticModel, cancellationToken, classDeclaration), nameof(ImplementIDisposableCodeFixProvider) + "Virtual"), diagnostic); } }