Visual Studio 2017 / Roslyn 2.x | Visual Studio 2019 / Roslyn 3.x | |
---|---|---|
Build | ||
Tests | ||
NuGet.org | ||
Visual Studio Markedplace |
This is a small Visual Studio Extension that should identify and fix problems as memleaks while using IDispoables. You can download it via Visual Studio Gallery or using Visual Studio Extension Manager.
Here is a little Preview of the extension in action:
This project is in early stage. Currently it has some known false positive and false negative results. Furthermore there are no code fixes available at the moment.
In order to check for undisposed IDispoables, there is an Roslyn analyser that registers for two syntax node actions:
new MemoryStream()
CreateMemoryStream()
Currently these both are considered the only possibilities where IDispoables can be created.
Every time one of the actions is triggered, the static code analyzer starts analyzing. The first thing the analyzer tries to find out is, whether the given expression created is actually an IDisposables. Otherwise the analysis is aborted. After that, the analyzer tries to find out, whether this IDisposable have to be disposed manually and if so, it checks if it is actually disposed, otherwise an diagnostics is reported.
Generally this extension assumes, that an IDisposable has to be disposed, where it gets created. The class that created an IDisposable is responsible for destroying it.
This sounds simple, but it's pretty complicated to detect, because this simple definition is not satisfied by all your code. Even within the code assemblies there are exceptions, that had to be detected correctly. In the following text, I want explain this in detail.
These are variables, where the result of the ObjectCreation or InvocationExpression is not stored in a named variable, where it is created. Example:
public void SomeMethod(){
new MemoryStream();
//or
var reader = new StreamReader(new MemoryStream());
//or
using(new MemoryStream()){}
}
Anonymous variables are considered disposed if:
new MemoryStream().Dispose())
using(new MemoryStream()))
A tracking type is an IDisposable itself, that keeps track of the disposables given to this
tracking type within the constructor. Example: System.IO.StreamReader.
If you call new StreamReader(new MemoryStream()).Dispose()
the anonymous StreamReader
instance and the anonymous MemoryStream instance are both disposed, because the StreamReader
keeps track of the given MemoryStream instance.
These situation are detected within the extension, because there is a configuration file that defines tracking types. Unfortunately this list is currently hard-coded an not yet editable by the user.
If you now notice, that there are certain constructor calls that does keep track of the given stream, then you are right. These situations are detected, too.
A tracking method is a method, that keeps track of an IDisposable. An Example is the following extension method from Reactive Extensions:
public void AddTo<T>(this T disposable, ICollection<IDispoable> disposables) where T : IDispoable
A tracking factory is a class that created IDisposables for your and it keeps track of these for you.
A field or property is considered disposed, if it gets disposed within a method that is named Dispose and has no parameters.
There should be some way to configure the extensions. There is lots of configuration hard-coded within the extensions:
Most of these pre-configured values are a good start for all projects. But sooner or later you will stuble upon a for instances tracking method of a private API you are using. These will unlikely became part of the extensions itself, but you should have a possiblity to configure these yourself.
Currenly there are three ideas:
If you rename a variable and call Dispose on the renamed variable. E.g.
var mem = new MemoryStream()
var mem2 = mem;
mem2.Dispose()
Pull-Requests | |
Issues | |
Downloads | |
Contribution |