コード例 #1
0
        /// <inheritdoc/>
        public override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken)
                             .ConfigureAwait(false);

            var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken)
                                .ConfigureAwait(false);

            foreach (var diagnostic in context.Diagnostics)
            {
                if (syntaxRoot.TryFindNode <MemberDeclarationSyntax>(diagnostic, out var member) &&
                    semanticModel.TryGetSymbol(member, context.CancellationToken, out ISymbol memberSymbol))
                {
                    if (DisposeMethod.TryFindVirtualDispose(memberSymbol.ContainingType, semanticModel.Compilation, Search.TopLevel, out var disposeMethod) &&
                        disposeMethod.TrySingleDeclaration(context.CancellationToken, out MethodDeclarationSyntax disposeMethodDeclaration))
                    {
                        context.RegisterDocumentEditorFix(
                            "Dispose member.",
                            (editor, cancellationToken) => DisposeInVirtualDisposeMethod(editor, memberSymbol, disposeMethodDeclaration, cancellationToken),
                            diagnostic);
                    }
                    else if (DisposeMethod.TryFindIDisposableDispose(memberSymbol.ContainingType, semanticModel.Compilation, Search.TopLevel, out disposeMethod) &&
                             disposeMethod.TrySingleDeclaration(context.CancellationToken, out disposeMethodDeclaration))
                    {
                        context.RegisterDocumentEditorFix(
                            "Dispose member.",
                            (editor, cancellationToken) => DisposeInDisposeMethod(editor, memberSymbol, disposeMethodDeclaration, cancellationToken),
                            diagnostic);
                    }
                }
            }
        }
コード例 #2
0
        private static bool ShouldCallBase(IMethodSymbol method, MethodDeclarationSyntax methodDeclaration, SyntaxNodeAnalysisContext context)
        {
            if (method.Parameters.TrySingle(out var parameter) &&
                parameter.Type == KnownSymbol.Boolean &&
                method.IsOverride &&
                method.OverriddenMethod is IMethodSymbol overridden &&
                !DisposeMethod.TryFindBaseCall(methodDeclaration, context.SemanticModel, context.CancellationToken, out _))
            {
                if (overridden.DeclaringSyntaxReferences.Length == 0)
                {
                    return(true);
                }
                else
                {
                    using (var disposeWalker = DisposeWalker.Borrow(overridden, context.SemanticModel, context.CancellationToken))
                    {
                        foreach (var disposeCall in disposeWalker)
                        {
                            if (DisposeCall.TryGetDisposed(disposeCall, context.SemanticModel, context.CancellationToken, out var disposed) &&
                                FieldOrProperty.TryCreate(disposed, out var fieldOrProperty) &&
                                !DisposableMember.IsDisposed(fieldOrProperty, method, context.SemanticModel, context.CancellationToken))
                            {
                                return(true);
                            }
                        }
                    }
                }
            }

            return(false);
        }
        private static void HandleFieldOrProperty(SyntaxNodeAnalysisContext context, FieldOrProperty fieldOrProperty)
        {
            using (var assignedValues = AssignedValueWalker.Borrow(fieldOrProperty.Symbol, context.SemanticModel, context.CancellationToken))
            {
                using (var recursive = RecursiveValues.Borrow(assignedValues, context.SemanticModel, context.CancellationToken))
                {
                    if (Disposable.IsAnyCreation(recursive, context.SemanticModel, context.CancellationToken).IsEither(Result.Yes, Result.AssumeYes))
                    {
                        if (Disposable.IsAnyCachedOrInjected(recursive, context.SemanticModel, context.CancellationToken).IsEither(Result.Yes, Result.AssumeYes) ||
                            IsMutableFromOutside(fieldOrProperty))
                        {
                            context.ReportDiagnostic(Diagnostic.Create(IDISP008DontMixInjectedAndCreatedForMember.Descriptor, context.Node.GetLocation()));
                        }
                        else if (context.Node.TryFirstAncestorOrSelf <TypeDeclarationSyntax>(out var typeDeclaration) &&
                                 DisposableMember.IsDisposed(fieldOrProperty, typeDeclaration, context.SemanticModel, context.CancellationToken).IsEither(Result.No, Result.AssumeNo) &&
                                 !TestFixture.IsAssignedAndDisposedInSetupAndTearDown(fieldOrProperty, typeDeclaration, context.SemanticModel, context.CancellationToken))
                        {
                            context.ReportDiagnostic(Diagnostic.Create(IDISP002DisposeMember.Descriptor, context.Node.GetLocation()));

                            if (!DisposeMethod.TryFindFirst(fieldOrProperty.ContainingType, context.Compilation, Search.TopLevel, out _) &&
                                !TestFixture.IsAssignedInSetUp(fieldOrProperty, typeDeclaration, context.SemanticModel, context.CancellationToken, out _))
                            {
                                context.ReportDiagnostic(Diagnostic.Create(IDISP006ImplementIDisposable.Descriptor, context.Node.GetLocation()));
                            }
                        }
                    }
                }
            }
        }
コード例 #4
0
        internal static DisposeWalker Borrow(ITypeSymbol type, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            if (type.IsAssignableTo(KnownSymbol.IDisposable, semanticModel.Compilation) &&
                DisposeMethod.TryFindFirst(type, semanticModel.Compilation, Search.Recursive, out var disposeMethod))
            {
                return(Borrow(disposeMethod, semanticModel, cancellationToken));
            }

            return(Borrow(() => new DisposeWalker()));
        }
コード例 #5
0
        internal static DisposeWalker Borrow(INamedTypeSymbol type, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            if (type.IsAssignableTo(KnownSymbol.IDisposable, semanticModel.Compilation) &&
                DisposeMethod.FindFirst(type, semanticModel.Compilation, Search.Recursive) is { } disposeMethod&&
                disposeMethod.TrySingleDeclaration(cancellationToken, out MethodDeclarationSyntax? declaration))
            {
                return(BorrowAndVisit(declaration, SearchScope.Instance, type, semanticModel, () => new DisposeWalker(), cancellationToken));
            }

            if (type.IsAssignableTo(KnownSymbol.IAsyncDisposable, semanticModel.Compilation) &&
                type.TryFindFirstMethod(x => x is { Parameters: { Length: 0 } } && x == KnownSymbol.IAsyncDisposable.DisposeAsync, out var disposeAsync) &&
コード例 #6
0
        private static void DisposeInVirtualDisposeMethod(DocumentEditor editor, ISymbol memberSymbol, MethodDeclarationSyntax disposeMethodDeclaration, CancellationToken cancellationToken)
        {
            var disposeStatement = Snippet.DisposeStatement(memberSymbol, editor.SemanticModel, cancellationToken);

            if (TryFindIfDisposing(disposeMethodDeclaration, out var ifDisposing))
            {
                if (ifDisposing.Statement is BlockSyntax block)
                {
                    var statements = block.Statements.Add(disposeStatement);
                    var newBlock   = block.WithStatements(statements);
                    editor.ReplaceNode(block, newBlock);
                }
                else if (ifDisposing.Statement is StatementSyntax statement)
                {
                    editor.ReplaceNode(
                        ifDisposing,
                        ifDisposing.WithStatement(SyntaxFactory.Block(statement, disposeStatement)));
                }
                else
                {
                    editor.ReplaceNode(
                        ifDisposing,
                        ifDisposing.WithStatement(SyntaxFactory.Block(disposeStatement)));
                }
            }
            else if (disposeMethodDeclaration.Body is BlockSyntax block)
            {
                ifDisposing = SyntaxFactory.IfStatement(
                    SyntaxFactory.IdentifierName(disposeMethodDeclaration.ParameterList.Parameters[0].Identifier),
                    SyntaxFactory.Block(disposeStatement));

                if (DisposeMethod.TryFindBaseCall(disposeMethodDeclaration, editor.SemanticModel, cancellationToken, out var baseCall))
                {
                    if (baseCall.TryFirstAncestor(out ExpressionStatementSyntax expressionStatement))
                    {
                        editor.InsertBefore(expressionStatement, ifDisposing);
                    }
                }
                else
                {
                    editor.ReplaceNode(
                        block,
                        block.AddStatements(ifDisposing));
                }
            }
        }
コード例 #7
0
 private static void Handle(SyntaxNodeAnalysisContext context)
 {
     if (context.Node is ClassDeclarationSyntax classDeclaration &&
         context.ContainingSymbol is INamedTypeSymbol {
         IsSealed : false
     } type&&
         type.IsAssignableTo(KnownSymbol.IDisposable, context.SemanticModel.Compilation) &&
         DisposeMethod.Find(type, context.Compilation, Search.TopLevel) is { IsVirtual : false, IsAbstract : false, IsOverride : false } disposeMethod&&
         !HasDisposeDisposing(type))
     {
         context.ReportDiagnostic(
             Diagnostic.Create(
                 Descriptors.IDISP025SealDisposable,
                 classDeclaration.Identifier.GetLocation(),
                 additionalLocations : new[] { disposeMethod.Locations[0] }));
     }
 }
コード例 #8
0
        private static void Handle(SyntaxNodeAnalysisContext context)
        {
            if (!context.IsExcludedFromAnalysis() &&
                context.Node is DestructorDeclarationSyntax methodDeclaration)
            {
                if (DisposeMethod.TryFindDisposeBoolCall(methodDeclaration, out _, out var isDisposing) &&
                    isDisposing.Expression?.IsKind(SyntaxKind.FalseLiteralExpression) != true)
                {
                    context.ReportDiagnostic(Diagnostic.Create(Descriptors.IDISP022DisposeFalse, isDisposing.GetLocation()));
                }

                using var walker = FinalizerContextWalker.Borrow(methodDeclaration, context.SemanticModel, context.CancellationToken);
                foreach (var node in walker.UsedReferenceTypes)
                {
                    context.ReportDiagnostic(Diagnostic.Create(Descriptors.IDISP023ReferenceTypeInFinalizerContext, node.GetLocation()));
                }
            }
        }
コード例 #9
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 typeDeclaration = syntaxRoot.FindNode(diagnostic.Location.SourceSpan)
                                      .FirstAncestorOrSelf <TypeDeclarationSyntax>();
                if (diagnostic.Id == IDISP009IsIDisposable.DiagnosticId)
                {
                    context.RegisterCodeFix(
                        CodeAction.Create(
                            "Add IDisposable interface",
                            cancellationToken =>
                            AddInterfaceAsync(
                                context,
                                cancellationToken,
                                typeDeclaration),
                            nameof(ImplementIDisposableFix) + "add interface"),
                        diagnostic);
                    continue;
                }

                if (typeDeclaration is StructDeclarationSyntax structDeclaration)
                {
                    context.RegisterCodeFix(
                        CodeAction.Create(
                            "Implement IDisposable.",
                            cancellationToken =>
                            ImplementIDisposableStructAsync(
                                context,
                                cancellationToken,
                                structDeclaration),
                            nameof(ImplementIDisposableFix) + "Struct"),
                        diagnostic);
                }
                else if (typeDeclaration is ClassDeclarationSyntax classDeclaration)
                {
                    var type = semanticModel.GetDeclaredSymbolSafe(typeDeclaration, context.CancellationToken);
                    if (Disposable.IsAssignableFrom(type, semanticModel.Compilation) &&
                        DisposeMethod.TryFindBaseVirtual(type, out var baseDispose))
                    {
                        context.RegisterCodeFix(
                            CodeAction.Create(
                                "override Dispose(bool)",
                                cancellationToken =>
                                OverrideDisposeAsync(
                                    context,
                                    classDeclaration,
                                    baseDispose,
                                    cancellationToken),
                                nameof(ImplementIDisposableFix) + "override"),
                            diagnostic);
                        continue;
                    }

                    if (type.TryFindSingleMethodRecursive("Dispose", out var disposeMethod) &&
                        !disposeMethod.IsStatic &&
                        disposeMethod.ReturnsVoid &&
                        disposeMethod.Parameters.Length == 0)
                    {
                        continue;
                    }

                    if (type.TryFindFieldRecursive("disposed", out _) ||
                        type.TryFindFieldRecursive("_disposed", out _))
                    {
                        return;
                    }

                    if (type.IsSealed)
                    {
                        context.RegisterCodeFix(
                            CodeAction.Create(
                                "Implement IDisposable.",
                                cancellationToken =>
                                ImplementIDisposableSealedAsync(
                                    context,
                                    cancellationToken,
                                    classDeclaration),
                                nameof(ImplementIDisposableFix) + "Sealed"),
                            diagnostic);
                        continue;
                    }

                    if (type.IsAbstract)
                    {
                        context.RegisterCodeFix(
                            CodeAction.Create(
                                "Implement IDisposable with virtual dispose method.",
                                cancellationToken =>
                                ImplementIDisposableVirtualAsync(
                                    context,
                                    cancellationToken,
                                    classDeclaration),
                                nameof(ImplementIDisposableFix) + "Virtual"),
                            diagnostic);
                        continue;
                    }

                    context.RegisterCodeFix(
                        CodeAction.Create(
                            "Implement IDisposable and make class sealed.",
                            cancellationToken =>
                            ImplementIDisposableSealedAsync(
                                context,
                                cancellationToken,
                                classDeclaration),
                            nameof(ImplementIDisposableFix) + "Sealed"),
                        diagnostic);

                    context.RegisterCodeFix(
                        CodeAction.Create(
                            "Implement IDisposable with virtual dispose method.",
                            cancellationToken =>
                            ImplementIDisposableVirtualAsync(
                                context,
                                cancellationToken,
                                classDeclaration),
                            nameof(ImplementIDisposableFix) + "Virtual"),
                        diagnostic);
                }
            }
        }
コード例 #10
0
        internal static Result IsArgumentDisposedByReturnValue(ArgumentSyntax argument, SemanticModel semanticModel, CancellationToken cancellationToken, PooledSet <SyntaxNode> visited = null)
        {
            if (argument?.Parent is ArgumentListSyntax argumentList)
            {
                if (argumentList.Parent is InvocationExpressionSyntax invocation &&
                    semanticModel.GetSymbolSafe(invocation, cancellationToken) is IMethodSymbol method)
                {
                    if (method.ContainingType.DeclaringSyntaxReferences.Length == 0)
                    {
                        return(method.ReturnsVoid ||
                               !IsAssignableFrom(method.ReturnType, semanticModel.Compilation)
                            ? Result.No
                            : Result.AssumeYes);
                    }

                    if (method.TryFindParameter(argument, out var parameter))
                    {
                        return(CheckReturnValues(parameter, invocation, semanticModel, cancellationToken, visited));
                    }

                    return(Result.Unknown);
                }

                if (argumentList.Parent is ObjectCreationExpressionSyntax ||
                    argumentList.Parent is ConstructorInitializerSyntax)
                {
                    if (TryGetAssignedFieldOrProperty(argument, semanticModel, cancellationToken, out var member, out var ctor) &&
                        FieldOrProperty.TryCreate(member, out var fieldOrProperty))
                    {
                        var initializer = argument.FirstAncestorOrSelf <ConstructorInitializerSyntax>();
                        if (initializer != null)
                        {
                            if (semanticModel.GetDeclaredSymbolSafe(initializer.Parent, cancellationToken) is IMethodSymbol chainedCtor &&
                                chainedCtor.ContainingType != member.ContainingType)
                            {
                                if (DisposeMethod.TryFindFirst(chainedCtor.ContainingType, semanticModel.Compilation, Search.TopLevel, out var disposeMethod))
                                {
                                    return(DisposableMember.IsDisposed(fieldOrProperty, disposeMethod, semanticModel, cancellationToken)
                                        ? Result.Yes
                                        : Result.No);
                                }
                            }
                        }

                        return(DisposableMember.IsDisposed(fieldOrProperty, ctor.ContainingType, semanticModel, cancellationToken));
                    }

                    if (ctor == null)
                    {
                        return(Result.AssumeYes);
                    }

                    if (ctor.ContainingType.DeclaringSyntaxReferences.Length == 0)
                    {
                        return(IsAssignableFrom(ctor.ContainingType, semanticModel.Compilation) ? Result.AssumeYes : Result.No);
                    }

                    if (ctor.ContainingType.IsAssignableTo(KnownSymbol.NinjectStandardKernel, semanticModel.Compilation))
                    {
                        return(Result.Yes);
                    }

                    return(Result.No);
                }
            }

            return(Result.Unknown);
        }
コード例 #11
0
        private static void Handle(SyntaxNodeAnalysisContext context)
        {
            if (!context.IsExcludedFromAnalysis() &&
                context.ContainingSymbol is IMethodSymbol method &&
                context.Node is MethodDeclarationSyntax methodDeclaration &&
                method.Name == "Dispose" &&
                method.ReturnsVoid)
            {
                if (method.Parameters.Length == 0 &&
                    !method.IsStatic &&
                    method.DeclaredAccessibility == Accessibility.Public &&
                    method.ReturnsVoid &&
                    method.OverriddenMethod == null &&
                    method.GetAttributes().Length == 0)
                {
                    if (!method.ExplicitInterfaceImplementations.Any() &&
                        !IsInterfaceImplementation(method))
                    {
                        context.ReportDiagnostic(Diagnostic.Create(IDISP009IsIDisposable.Descriptor, methodDeclaration.Identifier.GetLocation()));
                    }

                    if (DisposeMethod.TryFindSuppressFinalizeCall(methodDeclaration, context.SemanticModel, context.CancellationToken, out var suppressFinalize))
                    {
                        if (suppressFinalize.ArgumentList is ArgumentListSyntax argumentList &&
                            argumentList.Arguments.TrySingle(out var argument) &&
                            !argument.Expression.IsKind(SyntaxKind.ThisExpression))
                        {
                            context.ReportDiagnostic(Diagnostic.Create(IDISP020SuppressFinalizeThis.Descriptor, argument.GetLocation()));
                        }
                    }
                    else if (method.ContainingType.TryFindFirstMethod(x => x.MethodKind == MethodKind.Destructor, out _))
                    {
                        context.ReportDiagnostic(Diagnostic.Create(IDISP018CallSuppressFinalizeWhenFinalizer.Descriptor, methodDeclaration.Identifier.GetLocation()));
                    }
                    else if (method.ContainingType.TryFindFirstMethod(x => DisposeMethod.IsVirtualDispose(x), out _))
                    {
                        context.ReportDiagnostic(Diagnostic.Create(IDISP019CallSuppressFinalizeWhenVirtualDispose.Descriptor, methodDeclaration.Identifier.GetLocation()));
                    }

                    if (DisposeMethod.TryFindDisposeBoolCall(methodDeclaration, context.SemanticModel, context.CancellationToken, out _, out var isDisposing) &&
                        isDisposing.Expression?.IsKind(SyntaxKind.TrueLiteralExpression) != true)
                    {
                        context.ReportDiagnostic(Diagnostic.Create(IDISP021DisposeTrue.Descriptor, isDisposing.GetLocation()));
                    }
                }

                if (method.Parameters.TrySingle(out var parameter) &&
                    parameter.Type == KnownSymbol.Boolean)
                {
                    if (ShouldCallBase(method, methodDeclaration, context))
                    {
                        context.ReportDiagnostic(Diagnostic.Create(IDISP010CallBaseDispose.Descriptor, methodDeclaration.Identifier.GetLocation(), parameter.Name));
                    }

                    using (var walker = FinalizerContextWalker.Borrow(methodDeclaration, context.SemanticModel, context.CancellationToken))
                    {
                        foreach (var node in walker.UsedReferenceTypes)
                        {
                            context.ReportDiagnostic(Diagnostic.Create(IDISP023ReferenceTypeInFinalizerContext.Descriptor, node.GetLocation()));
                        }
                    }
                }
            }
        }