/// <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.TryGetSingleMethod("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); } }
private static bool IsReturned(ILocalSymbol symbol, BlockSyntax block, SemanticModel semanticModel, CancellationToken cancellationToken) { using (var pooled = ReturnValueWalker.Create(block, Search.TopLevel, semanticModel, cancellationToken)) { foreach (var value in pooled.Item) { var returnedSymbol = semanticModel.GetSymbolSafe(value, cancellationToken); if (SymbolComparer.Equals(symbol, returnedSymbol)) { return(true); } if (value is ObjectCreationExpressionSyntax objectCreation) { if (objectCreation.ArgumentList != null) { foreach (var argument in objectCreation.ArgumentList.Arguments) { var arg = semanticModel.GetSymbolSafe(argument.Expression, cancellationToken); if (SymbolComparer.Equals(symbol, arg)) { return(true); } } } if (objectCreation.Initializer != null) { foreach (var argument in objectCreation.Initializer.Expressions) { var arg = semanticModel.GetSymbolSafe(argument, cancellationToken); if (SymbolComparer.Equals(symbol, arg)) { return(true); } } } } if (value is InvocationExpressionSyntax invocation) { if (returnedSymbol == KnownSymbol.RxDisposable.Create && invocation.ArgumentList != null && invocation.ArgumentList.Arguments.TryGetSingle(out ArgumentSyntax argument)) { var body = (argument.Expression as ParenthesizedLambdaExpressionSyntax)?.Body; using (var pooledInvocations = InvocationWalker.Create(body)) { foreach (var candidate in pooledInvocations.Item.Invocations) { if (Disposable.IsDisposing(candidate, symbol, semanticModel, cancellationToken)) { return(true); } } } } } } } return(false); }