Example #1
0
            public static void InListOfTAdd(string expression)
            {
                var code          = @"
namespace N
{
    using System;
    using System.Collections.Generic;

    internal class C
    {
        private List<IDisposable> disposables = new List<IDisposable>();

        internal C(IDisposable disposable)
        {
            this.disposables.Add(disposable);
        }
    }
}".AssertReplace("Add(disposable)", expression);
                var syntaxTree    = CSharpSyntaxTree.ParseText(code);
                var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
                var semanticModel = compilation.GetSemanticModel(syntaxTree);
                var value         = syntaxTree.FindParameter("IDisposable disposable");

                Assert.AreEqual(true, semanticModel.TryGetSymbol(value, CancellationToken.None, out var symbol));
                Assert.AreEqual(true, LocalOrParameter.TryCreate(symbol, out var localOrParameter));
                Assert.AreEqual(true, Disposable.Stores(localOrParameter, semanticModel, CancellationToken.None, out var container));
                Assert.AreEqual("N.C.disposables", container.ToString());
            }
Example #2
0
            public static void Recursive()
            {
                var code          = @"
namespace N
{
    using System.IO;

    public class C
    {
        public C(Stream stream)
        {
            this.disposable = GetReader(stream);
        }

        private static StreamReader GetReader(Stream arg) => GetReader(arg);
    }
}";
                var syntaxTree    = CSharpSyntaxTree.ParseText(code);
                var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
                var semanticModel = compilation.GetSemanticModel(syntaxTree);
                var value         = syntaxTree.FindParameter("Stream stream");

                Assert.AreEqual(true, semanticModel.TryGetSymbol(value, CancellationToken.None, out var symbol));
                Assert.AreEqual(true, LocalOrParameter.TryCreate(symbol, out var localOrParameter));
                Assert.AreEqual(false, Disposable.Stores(localOrParameter, semanticModel, CancellationToken.None, out _));
            }
Example #3
0
            public static void InLeaveOpen(string expression, bool stores)
            {
                var code          = @"
namespace N
{
    using System;
    using System.IO;
    using System.IO.Compression;
    using System.Security.Cryptography;
    using System.Text;

    public class C
    {
        private readonly IDisposable disposable;

        public C(Stream stream)
        {
            this.disposable = new BinaryReader(stream);
        }
    }
}".AssertReplace("new BinaryReader(stream)", expression);
                var syntaxTree    = CSharpSyntaxTree.ParseText(code);
                var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
                var semanticModel = compilation.GetSemanticModel(syntaxTree);
                var value         = syntaxTree.FindParameter("stream");

                Assert.AreEqual(true, semanticModel.TryGetSymbol(value, CancellationToken.None, out var symbol));
                Assert.AreEqual(true, LocalOrParameter.TryCreate(symbol, out var localOrParameter));
                Assert.AreEqual(stores, Disposable.Stores(localOrParameter, semanticModel, CancellationToken.None, out var container));
                Assert.AreEqual(stores, Disposable.DisposedByReturnValue(syntaxTree.FindArgument("stream"), semanticModel, CancellationToken.None, out _));
                if (stores)
                {
                    Assert.AreEqual("N.C.disposable", container.ToString());
                }
            }
        private static void Handle(SyntaxNodeAnalysisContext context)
        {
            if (!context.IsExcludedFromAnalysis() &&
                context.Node is ArgumentSyntax argument &&
                argument.Parent is ArgumentListSyntax argumentList &&
                argumentList.Parent is InvocationExpressionSyntax invocation &&
                argument.RefOrOutKeyword.IsEither(SyntaxKind.RefKeyword, SyntaxKind.OutKeyword) &&
                context.SemanticModel.TryGetSymbol(invocation, context.CancellationToken, out var method) &&
                method.TrySingleDeclaration(context.CancellationToken, out BaseMethodDeclarationSyntax declaration) &&
                method.TryFindParameter(argument, out var parameter) &&
                Disposable.IsPotentiallyAssignableFrom(parameter.Type, context.Compilation))
            {
                if (Disposable.IsCreation(argument, context.SemanticModel, context.CancellationToken).IsEither(Result.Yes, Result.AssumeYes) &&
                    !Disposable.IsAddedToFieldOrProperty(parameter, declaration, context.SemanticModel, context.CancellationToken) &&
                    context.SemanticModel.TryGetSymbol(argument.Expression, context.CancellationToken, out ISymbol symbol))
                {
                    if (LocalOrParameter.TryCreate(symbol, out var localOrParameter) &&
                        Disposable.ShouldDispose(localOrParameter, argument.Expression, context.SemanticModel, context.CancellationToken))
                    {
                        context.ReportDiagnostic(Diagnostic.Create(IDISP001DisposeCreated.Descriptor, argument.GetLocation()));
                    }

                    if (Disposable.IsAssignedWithCreated(symbol, invocation, context.SemanticModel, context.CancellationToken).IsEither(Result.Yes, Result.AssumeYes) &&
                        !Disposable.IsDisposedBefore(symbol, invocation, context.SemanticModel, context.CancellationToken))
                    {
                        context.ReportDiagnostic(Diagnostic.Create(IDISP003DisposeBeforeReassigning.Descriptor, argument.GetLocation()));
                    }
                }
            }
        }
Example #5
0
            public static void ArrayFieldAssignedInCtor()
            {
                var code          = @"
namespace N
{
    using System;

    internal class C
    {
        private IDisposable[] disposables = new IDisposable[1];

        internal C(IDisposable disposable)
        {
            this.disposables[0] = disposable;
        }
    }
}";
                var syntaxTree    = CSharpSyntaxTree.ParseText(code);
                var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
                var semanticModel = compilation.GetSemanticModel(syntaxTree);
                var value         = syntaxTree.FindParameter("IDisposable disposable");

                Assert.AreEqual(true, semanticModel.TryGetSymbol(value, CancellationToken.None, out var symbol));
                Assert.AreEqual(true, LocalOrParameter.TryCreate(symbol, out var localOrParameter));
                Assert.AreEqual(true, Disposable.Stores(localOrParameter, semanticModel, CancellationToken.None, out var container));
                Assert.AreEqual("N.C.disposables", container.ToString());
            }
Example #6
0
            public static void WhenNotUsed(string type, string expression)
            {
                var code = @"
namespace N
{
    using System;

    internal class C
    {
        private readonly bool value;

        internal C(IDisposable disposable)
        {
            this.value = Equals(disposable, null);
        }
    }
}".AssertReplace("bool", type)
                           .AssertReplace("Equals(disposable, null)", expression);
                var syntaxTree    = CSharpSyntaxTree.ParseText(code);
                var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
                var semanticModel = compilation.GetSemanticModel(syntaxTree);
                var value         = syntaxTree.FindParameter("IDisposable disposable");

                Assert.AreEqual(true, semanticModel.TryGetSymbol(value, CancellationToken.None, out var symbol));
                Assert.AreEqual(true, LocalOrParameter.TryCreate(symbol, out var localOrParameter));
                Assert.AreEqual(false, Disposable.Stores(localOrParameter, semanticModel, CancellationToken.None, out _));
            }
Example #7
0
            public static void DisposableMixinsDisposeWith()
            {
                var code          = @"
namespace N
{
    using System;
    using System.IO;
    using System.Reactive.Disposables;

    sealed class C : IDisposable
    {
        private readonly CompositeDisposable compositeDisposable = new CompositeDisposable();

        public DisposeWith(Stream stream)
        {
            this.disposable = stream.DisposeWith(this.compositeDisposable);
        }

        public void Dispose()
        {
            this.compositeDisposable.Dispose();
        }
    }
}";
                var syntaxTree    = CSharpSyntaxTree.ParseText(code);
                var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
                var semanticModel = compilation.GetSemanticModel(syntaxTree);
                var value         = syntaxTree.FindParameter("Stream stream");

                Assert.AreEqual(true, semanticModel.TryGetSymbol(value, CancellationToken.None, out var symbol));
                Assert.AreEqual(true, LocalOrParameter.TryCreate(symbol, out var localOrParameter));
                Assert.AreEqual(true, Disposable.Stores(localOrParameter, semanticModel, CancellationToken.None, out _));
            }
Example #8
0
            public static void ListOfTAddInInitializeParameter(string call)
            {
                var code          = @"
namespace N
{
    using System;
    using System.Collections.Generic;

    internal class C
    {
        internal C(List<IDisposable> disposables, IDisposable disposable)
        {
            this.Initialize(disposables, disposable);
        }

        private void Initialize(List<IDisposable> disposables, IDisposable disposable)
        {
            disposables.Add(disposable);
        }
    }
}".AssertReplace("this.Initialize(disposables, disposable)", call);
                var syntaxTree    = CSharpSyntaxTree.ParseText(code);
                var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
                var semanticModel = compilation.GetSemanticModel(syntaxTree);
                var value         = syntaxTree.FindParameter("IDisposable disposable");

                Assert.AreEqual(true, semanticModel.TryGetSymbol(value, CancellationToken.None, out var symbol));
                Assert.AreEqual(true, LocalOrParameter.TryCreate(symbol, out var localOrParameter));
                Assert.AreEqual(true, Disposable.Stores(localOrParameter, semanticModel, CancellationToken.None, out var container));
                Assert.AreEqual(SymbolKind.Parameter, container.Kind);
                Assert.AreEqual("disposables", container.Name);
            }
            public static void FieldAssignedViaCalledMethodParameter()
            {
                var code          = @"
namespace N
{
    using System;

    internal class C
    {
        private IDisposable disposable;

        internal C(IDisposable disposable)
        {
            this.M(disposable);
        }

        private void M(IDisposable disposable)
        {
            this.disposable = disposable;
        }
    }
}";
                var syntaxTree    = CSharpSyntaxTree.ParseText(code);
                var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
                var semanticModel = compilation.GetSemanticModel(syntaxTree);
                var value         = syntaxTree.FindParameter("IDisposable disposable");
                var symbol        = semanticModel.GetDeclaredSymbol(value, CancellationToken.None);

                Assert.AreEqual(true, LocalOrParameter.TryCreate(symbol, out var localOrParameter));
                Assert.AreEqual(true, Disposable.Assigns(localOrParameter, semanticModel, CancellationToken.None, out var field));
                Assert.AreEqual("N.C.disposable", field.Symbol.ToString());
            }
Example #10
0
        private static void Handle(SyntaxNodeAnalysisContext context)
        {
            if (!context.IsExcludedFromAnalysis() &&
                context.Node is AssignmentExpressionSyntax {
                Left : { } left, Right : { } right
            } assignment&&
                !left.IsKind(SyntaxKind.ElementAccessExpression) &&
                context.SemanticModel.TryGetSymbol(left, context.CancellationToken, out var assignedSymbol))
            {
                if (LocalOrParameter.TryCreate(assignedSymbol, out var localOrParameter) &&
                    Disposable.IsCreation(right, context.SemanticModel, context.CancellationToken).IsEither(Result.Yes, Result.AssumeYes) &&
                    Disposable.ShouldDispose(localOrParameter, context.SemanticModel, context.CancellationToken))
                {
                    context.ReportDiagnostic(Diagnostic.Create(Descriptors.IDISP001DisposeCreated, assignment.GetLocation()));
                }

                if (IsReassignedWithCreated(assignment, context))
                {
                    context.ReportDiagnostic(Diagnostic.Create(Descriptors.IDISP003DisposeBeforeReassigning, assignment.GetLocation()));
                }

                if (assignedSymbol is IParameterSymbol {
                    RefKind : RefKind.Ref
                } refParameter&&
                    refParameter.ContainingSymbol.DeclaredAccessibility != Accessibility.Private &&
                    context.SemanticModel.TryGetType(right, context.CancellationToken, out var type) &&
                    Disposable.IsAssignableFrom(type, context.Compilation))
                {
                    context.ReportDiagnostic(Diagnostic.Create(Descriptors.IDISP008DoNotMixInjectedAndCreatedForMember, context.Node.GetLocation()));
                }
            }
        }
Example #11
0
        private static void Handle(SyntaxNodeAnalysisContext context)
        {
            if (!context.IsExcludedFromAnalysis() &&
                context.Node is ArgumentSyntax {
                Parent : ArgumentListSyntax {
                    Parent : InvocationExpressionSyntax invocation
                }
            } argument&&
                argument.RefOrOutKeyword.IsEither(SyntaxKind.RefKeyword, SyntaxKind.OutKeyword) &&
                IsCreation(argument, context.SemanticModel, context.CancellationToken) &&
                context.SemanticModel.TryGetSymbol(argument.Expression, context.CancellationToken, out var symbol))
            {
                if (symbol.Kind == SymbolKind.Discard ||
                    (LocalOrParameter.TryCreate(symbol, out var localOrParameter) &&
                     Disposable.ShouldDispose(localOrParameter, context.SemanticModel, context.CancellationToken)))
                {
                    context.ReportDiagnostic(Diagnostic.Create(Descriptors.IDISP001DisposeCreated, argument.GetLocation()));
                }

                if (Disposable.IsAssignedWithCreated(symbol, invocation, context.SemanticModel, context.CancellationToken).IsEither(Result.Yes, Result.AssumeYes) &&
                    !Disposable.IsDisposedBefore(symbol, invocation, context.SemanticModel, context.CancellationToken))
                {
                    context.ReportDiagnostic(Diagnostic.Create(Descriptors.IDISP003DisposeBeforeReassigning, argument.GetLocation()));
                }
            }
        }
Example #12
0
            public static void InHttpClient(string expression, bool stores)
            {
                var code          = @"
namespace N
{
    using System.Net.Http;

    public class C
    {
        private readonly IDisposable disposable;

        public C(HttpClientHandler handler)
        {
            this.disposable = new HttpClient(handler);
        }
    }
}".AssertReplace("new HttpClient(handler)", expression);
                var syntaxTree    = CSharpSyntaxTree.ParseText(code);
                var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
                var semanticModel = compilation.GetSemanticModel(syntaxTree);
                var value         = syntaxTree.FindParameter("handler");

                Assert.AreEqual(true, semanticModel.TryGetSymbol(value, CancellationToken.None, out var symbol));
                Assert.AreEqual(true, LocalOrParameter.TryCreate(symbol, out var localOrParameter));
                Assert.AreEqual(stores, Disposable.Stores(localOrParameter, semanticModel, CancellationToken.None, out var container));
                Assert.AreEqual(stores, Disposable.DisposedByReturnValue(syntaxTree.FindArgument("handler"), semanticModel, CancellationToken.None, out _));
                if (stores)
                {
                    Assert.AreEqual("N.C.disposable", container.ToString());
                }
            }
Example #13
0
            public static void InDiscardedTuple(string expression)
            {
                var code          = @"
namespace N
{
    using System;
    using System.Collections.Generic;

    internal class C
    {
        internal C(IDisposable disposable)
        {
            _ = Tuple.Create(disposable, 1);
        }
    }
}".AssertReplace("Tuple.Create(disposable, 1)", expression);
                var syntaxTree    = CSharpSyntaxTree.ParseText(code);
                var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
                var semanticModel = compilation.GetSemanticModel(syntaxTree);
                var value         = syntaxTree.FindParameter("IDisposable disposable");

                Assert.AreEqual(true, semanticModel.TryGetSymbol(value, CancellationToken.None, out var symbol));
                Assert.AreEqual(true, LocalOrParameter.TryCreate(symbol, out var localOrParameter));
                Assert.AreEqual(false, Disposable.Stores(localOrParameter, semanticModel, CancellationToken.None, out _));
            }
        internal static bool ShouldDispose(LocalOrParameter localOrParameter, ExpressionSyntax location, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            switch (localOrParameter.Symbol)
            {
            case ILocalSymbol local:
                return(ShouldDispose(local, location, semanticModel, cancellationToken));

            case IParameterSymbol parameter:
                return(ShouldDispose(parameter, location, semanticModel, cancellationToken));
            }

            throw new InvalidOperationException("Should never get here.");
        }
Example #15
0
            public static void CompositeDisposableExtAddAndReturn(string expression)
            {
                var code = @"
namespace N
{
    using System;
    using System.IO;
    using System.Reactive.Disposables;

    public static class CompositeDisposableExt
    {
        public static T AddAndReturn<T>(this CompositeDisposable disposable, T item)
            where T : IDisposable
        {
            if (item != null)
            {
                disposable.Add(item);
            }

            return item;
        }
    }

    public sealed class C : IDisposable
    {
        private readonly CompositeDisposable disposable = new CompositeDisposable();

        public void Dispose()
        {
            this.disposable.Dispose();
        }

        internal object M(Stream stream)
        {
            return this.disposable.AddAndReturn(stream);
        }
    }
}".AssertReplace("disposable.AddAndReturn(stream)", expression);

                var syntaxTree    = CSharpSyntaxTree.ParseText(code);
                var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
                var semanticModel = compilation.GetSemanticModel(syntaxTree);
                var value         = syntaxTree.FindParameter("Stream stream");

                Assert.AreEqual(true, semanticModel.TryGetSymbol(value, CancellationToken.None, out var symbol));
                Assert.AreEqual(true, LocalOrParameter.TryCreate(symbol, out var localOrParameter));
                Assert.AreEqual(true, Disposable.Stores(localOrParameter, semanticModel, CancellationToken.None, out var container));
                Assert.AreEqual("disposable", container.Name);
                Assert.AreEqual(SymbolKind.Parameter, container.Kind);
            }
        internal static bool Returns(LocalOrParameter localOrParameter, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            using var recursion = Recursion.Borrow(localOrParameter.Symbol.ContainingType, semanticModel, cancellationToken);
            using var walker    = UsagesWalker.Borrow(localOrParameter, recursion.SemanticModel, recursion.CancellationToken);
            foreach (var usage in walker.Usages)
            {
                if (Returns(usage, recursion))
                {
                    return(true);
                }
            }

            return(false);
        }
        internal static bool Stores(LocalOrParameter localOrParameter, SemanticModel semanticModel, CancellationToken cancellationToken, [NotNullWhen(true)] out ISymbol?container)
        {
            using var recursion = Recursion.Borrow(localOrParameter.Symbol.ContainingType, semanticModel, cancellationToken);
            using var walker    = UsagesWalker.Borrow(localOrParameter, semanticModel, cancellationToken);
            foreach (var usage in walker.Usages)
            {
                if (Stores(usage, recursion, out container))
                {
                    return(true);
                }
            }

            container = null;
            return(false);
        }
Example #18
0
            public static void InDisposingPairWhenFactoryMethod(string parameter)
            {
                var code          = @"
namespace N
{
    using System;

    internal class C
    {
        private readonly Pair<IDisposable> pair;

        internal C(IDisposable disposable1, IDisposable disposable2)
        {
            this.pair = Create<IDisposable>(disposable1, disposable2);
        }

        private static Pair<T> Create<T>(T x, T y) where T : IDisposable => new Pair<T>(x, y);

        private class Pair<T> : IDisposable
            where T : IDisposable
        {
            private readonly T item1;
            private readonly T item2;

            public Pair(T item1, T item2)
            {
                this.item1 = item1;
                this.item2 = item2;
            }

            public void Dispose()
            {
                this.item1.Dispose();
                (this.item2 as IDisposable)?.Dispose();
            }
        }
    }
}";
                var syntaxTree    = CSharpSyntaxTree.ParseText(code);
                var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
                var semanticModel = compilation.GetSemanticModel(syntaxTree);
                var value         = syntaxTree.FindParameter(parameter);

                Assert.AreEqual(true, semanticModel.TryGetSymbol(value, CancellationToken.None, out var symbol));
                Assert.AreEqual(true, LocalOrParameter.TryCreate(symbol, out var localOrParameter));
                Assert.AreEqual(true, Disposable.Stores(localOrParameter, semanticModel, CancellationToken.None, out var container));
                Assert.AreEqual("N.C.pair", container.ToString());
            }
Example #19
0
            public static void InPairWhenNew(string parameter)
            {
                var code          = @"
namespace N
{
    using System;
    using System.Collections.Generic;

    internal class C
    {
        private readonly Pair<IDisposable> pair;

        internal C(IDisposable disposable1, IDisposable disposable2)
        {
            this.pair = new Pair<IDisposable>(disposable1, disposable2);
        }

        public class Pair<T>
        {
            public Pair(T item1, T item2)
            {
                this.Item1 = item1;
                this.Item2 = item2;
            }

            public T Item1 { get; }

            public T Item2 { get; }
        }
    }
}";
                var syntaxTree    = CSharpSyntaxTree.ParseText(code);
                var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
                var semanticModel = compilation.GetSemanticModel(syntaxTree);
                var value         = syntaxTree.FindParameter(parameter);

                Assert.AreEqual(true, semanticModel.TryGetSymbol(value, CancellationToken.None, out var symbol));
                Assert.AreEqual(true, LocalOrParameter.TryCreate(symbol, out var localOrParameter));
                Assert.AreEqual(true, Disposable.Stores(localOrParameter, semanticModel, CancellationToken.None, out var container));
                Assert.AreEqual("N.C.pair", container.ToString());
            }
        internal static bool ShouldDispose(LocalOrParameter localOrParameter, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            if (localOrParameter.Symbol is IParameterSymbol parameter &&
                parameter.RefKind != RefKind.None)
            {
                return(false);
            }

            using var recursion = Recursion.Borrow(localOrParameter.Symbol.ContainingType, semanticModel, cancellationToken);
            using var walker    = UsagesWalker.Borrow(localOrParameter, semanticModel, cancellationToken);
            foreach (var usage in walker.Usages)
            {
                if (Returns(usage, recursion))
                {
                    return(false);
                }

                if (Assigns(usage, recursion, out _))
                {
                    return(false);
                }

                if (Stores(usage, recursion, out _))
                {
                    return(false);
                }

                if (Disposes(usage, recursion))
                {
                    return(false);
                }

                if (DisposedByReturnValue(usage, recursion, out _))
                {
                    return(false);
                }
            }

            return(true);
        }
Example #21
0
            public static void WhenNotUsed()
            {
                var code          = @"
namespace N
{
    using System;

    internal class C
    {
        internal C(IDisposable disposable)
        {
        }
    }
}";
                var syntaxTree    = CSharpSyntaxTree.ParseText(code);
                var compilation   = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
                var semanticModel = compilation.GetSemanticModel(syntaxTree);
                var value         = syntaxTree.FindParameter("IDisposable disposable");

                Assert.AreEqual(true, semanticModel.TryGetSymbol(value, CancellationToken.None, out var symbol));
                Assert.AreEqual(true, LocalOrParameter.TryCreate(symbol, out var localOrParameter));
                Assert.AreEqual(false, Disposable.Stores(localOrParameter, semanticModel, CancellationToken.None, out _));
            }
 internal static bool Assigns(LocalOrParameter localOrParameter, SemanticModel semanticModel, CancellationToken cancellationToken, out FieldOrProperty first)
 {
     if (localOrParameter.TryGetScope(cancellationToken, out var scope) &&
         localOrParameter is { ContainingSymbol : { ContainingType : { } containingType } })