コード例 #1
0
        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()));
            }
        }
コード例 #4
0
        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()));
            }
        }
コード例 #5
0
        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);
        }
コード例 #6
0
        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()));
            }
        }
コード例 #7
0
        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));
        }
コード例 #8
0
        /// <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);
            }
        }