public void AnalyzersProject(DiagnosticAnalyzer analyzer) { AnalyzerAssert.Valid(analyzer, AnalyzersProjectSln); }
public void IgnoreUsageInThrowTwoLevels() { var fooCode = @" namespace RoslynSandbox { using System; using System.ComponentModel; using System.Runtime.CompilerServices; public sealed class Foo : INotifyPropertyChanged, IDisposable { private bool disposed; private Bar bar; public event PropertyChangedEventHandler PropertyChanged; public Bar Bar { get { ThrowIfDisposed(); return this.bar; } set { ThrowIfDisposed(); if (ReferenceEquals(value, this.bar)) { return; } this.bar = value; this.OnPropertyChanged(); } } public void Dispose() { if (this.disposed) { return; } this.disposed = true; } private void OnPropertyChanged([CallerMemberName] string propertyName = null) { this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } private void ThrowIfDisposed() { if (this.disposed) { throw new ObjectDisposedException(this.GetType().FullName); } } } }"; var barCode = @" namespace RoslynSandbox { using System; using System.ComponentModel; using System.Runtime.CompilerServices; public class Bar : INotifyPropertyChanged, IDisposable { private int value; private bool disposed; public event PropertyChangedEventHandler PropertyChanged; public int Value { get { ThrowIfDisposed(); return this.value; } set { ThrowIfDisposed(); if (value == this.value) { return; } this.value = value; this.OnPropertyChanged(); } } public void Dispose() { } private void OnPropertyChanged([CallerMemberName] string propertyName = null) { this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } private void ThrowIfDisposed() { if (this.disposed) { throw new ObjectDisposedException(this.GetType().FullName); } } } }"; var testCode = @" namespace RoslynSandbox { using Gu.Reactive; public class FooCondition : Condition { public FooCondition(Foo foo) : base( foo.ObservePropertyChangedSlim(x => x.Bar.Value), () => foo.Bar?.Value == 2) { } } }"; AnalyzerAssert.Valid(Analyzer, fooCode, barCode, testCode); }
public void IgnoreRegex() { var fooCode = @" namespace RoslynSandbox { using System.ComponentModel; using System.Runtime.CompilerServices; public class Foo : INotifyPropertyChanged { private string text; public event PropertyChangedEventHandler PropertyChanged; public string Text { get => this.text; set { if (value == this.text) { return; } this.text = value; this.OnPropertyChanged(); } } protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } }"; var testCode = @" namespace RoslynSandbox { using System.Text.RegularExpressions; using Gu.Reactive; public class FooCondition : Condition { public FooCondition(Foo foo) : base( foo.ObservePropertyChanged(x => x.Text), () => Criteria(foo)) { } private static bool? Criteria(Foo foo) { if (foo.Text is string text) { var match = Regex.Match(text, string.Empty); return match.Success && int.TryParse(match.Groups[string.Empty].Value, out var value) && value > 10; } return false; } } }"; AnalyzerAssert.Valid(Analyzer, fooCode, testCode); }
public void ReproIssue71() { var code = @" using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TaxonomyWpf { public class IndexedList<T> : IList<KeyValuePair<int, T>> { protected IList<T> decorated; public IndexedList(IList<T> decorated) { if(decorated == null) throw new ArgumentNullException(nameof(decorated)); this.decorated = decorated; } public IEnumerator<KeyValuePair<int, T>> GetEnumerator() { return decorated.Select((element, index) => new KeyValuePair<int, T>(index, element)).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } void ICollection<KeyValuePair<int, T>>.Add(KeyValuePair<int, T> item) { Add(item.Value); } public void Add(T item) { decorated.Add(item); } public void Clear() { decorated.Clear(); } bool ICollection<KeyValuePair<int, T>>.Contains(KeyValuePair<int, T> item) { return Contains(item.Value); } public bool Contains(T item) { return decorated.Contains(item); } public void CopyTo(KeyValuePair<int, T>[] array, int arrayIndex) { throw new NotImplementedException(); } public bool Remove(KeyValuePair<int, T> item) { return decorated.Remove(item.Value); } public int Count => decorated.Count; public bool IsReadOnly => decorated.IsReadOnly; public int IndexOf(KeyValuePair<int, T> item) { return decorated.IndexOf(item.Value); } void IList<KeyValuePair<int, T>>.Insert(int index, KeyValuePair<int, T> item) { Insert(index, item.Value); } public void Insert(int index, T item) { decorated.Insert(index, item); } public void RemoveAt(int index) { decorated.RemoveAt(index); } public KeyValuePair<int, T> this[int index] { get { return new KeyValuePair<int, T>(index, decorated[index]); } set { decorated[index] = value.Value; } } } public class ObservableIndexedList<T> : IndexedList<T>, INotifyCollectionChanged { public ObservableIndexedList(ObservableCollection<T> decorated) : base(decorated) { } public event NotifyCollectionChangedEventHandler CollectionChanged { add { ((ObservableCollection<T>)decorated).CollectionChanged += value; } remove { ((ObservableCollection<T>)decorated).CollectionChanged -= value; } } } }"; AnalyzerAssert.Valid <IDISP003DisposeBeforeReassigning>(code); }
public void WhenUsingPrivateSetPropertyOnlyAssignedInCtor() { var fooCode = @" namespace RoslynSandbox { using System.ComponentModel; using System.Runtime.CompilerServices; public sealed class Foo : INotifyPropertyChanged { private Bar bar; public event PropertyChangedEventHandler PropertyChanged; public Bar Bar { get => this.bar; set { if (ReferenceEquals(value, this.bar)) { return; } this.bar = value; this.OnPropertyChanged(); } } private void OnPropertyChanged([CallerMemberName] string propertyName = null) { this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } }"; var barCode = @" namespace RoslynSandbox { public class Bar { public Bar(int value) { Value = value; } public int Value { get; private set; } } }"; var testCode = @" namespace RoslynSandbox { using Gu.Reactive; public class FooCondition : Condition { public FooCondition(Foo foo) : base( foo.ObservePropertyChangedSlim(x => x.Bar), () => foo.Bar.Value == 2) { } } }"; AnalyzerAssert.Valid(Analyzer, fooCode, barCode, testCode); }
public void TwoLevels() { var fooCode = @" namespace RoslynSandbox { using System.ComponentModel; using System.Runtime.CompilerServices; public class Foo : INotifyPropertyChanged { private Bar bar; public Bar Bar { get { return this.bar; } set { if (value == this.bar) { return; } this.bar = value; this.OnPropertyChanged(); } } } "; var barCode = @" namespace RoslynSandbox { using System.ComponentModel; using System.Runtime.CompilerServices; public class Bar : INotifyPropertyChanged { private int value; public event PropertyChangedEventHandler PropertyChanged; public int Value { get { return this.value; } set { if (value == this.value) { return; } this.value = value; this.OnPropertyChanged(); } } protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } }"; var testCode = @" namespace RoslynSandbox { using System; using Gu.Reactive; public class Baz { public Baz() { var foo = new Foo(); foo.ObservePropertyChanged(x => x.Bar.Value) .Subscribe(x => Console.WriteLine(x)); } } }"; AnalyzerAssert.Valid(Analyzer, fooCode, barCode, testCode); }
public void SomewhatRealisticSample(DiagnosticAnalyzer analyzer) { var disposableCode = @" namespace RoslynSandbox { using System; internal class Disposable : IDisposable { public Disposable(string meh) : this() { if (meh == null) throw new ArgumentNullException(nameof(meh)); } public Disposable() { } public void Dispose() { } } }"; var fooListCode = @" namespace RoslynSandbox { using System.Collections; using System.Collections.Generic; internal class FooList<T> : IReadOnlyList<T> { private readonly List<T> inner = new List<T>(); public int Count => this.inner.Count; public T this[int index] => this.inner[index]; public IEnumerator<T> GetEnumerator() { return this.inner.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this.inner).GetEnumerator(); } } }"; var fooCode = @" namespace RoslynSandbox { using System; using System.ComponentModel; using System.IO; using System.Reactive.Disposables; internal class Foo1 : IDisposable { private static readonly PropertyChangedEventArgs IsDirtyPropertyChangedEventArgs = new PropertyChangedEventArgs(nameof(IsDirty)); private readonly SingleAssignmentDisposable subscription = new SingleAssignmentDisposable(); private IDisposable meh1; private IDisposable meh2; private bool isDirty; public Foo1() { this.meh1 = this.RecursiveProperty; this.meh2 = this.RecursiveMethod(); this.subscription.Disposable = File.OpenRead(string.Empty); } public event PropertyChangedEventHandler PropertyChanged { add { this.PropertyChangedCore += value; } remove { this.PropertyChangedCore -= value; } } private event PropertyChangedEventHandler PropertyChangedCore; public Disposable RecursiveProperty => RecursiveProperty; public IDisposable Disposable => subscription.Disposable; public bool IsDirty { get { return this.isDirty; } private set { if (value == this.isDirty) { return; } this.isDirty = value; this.PropertyChangedCore?.Invoke(this, IsDirtyPropertyChangedEventArgs); } } public Disposable RecursiveMethod() => RecursiveMethod(); public void Meh() { using (var item = new Disposable()) { } using (var item = RecursiveProperty) { } using (RecursiveProperty) { } using (var item = RecursiveMethod()) { } using (RecursiveMethod()) { } } public void Dispose() { this.subscription.Dispose(); } } }"; var fooBaseCode = @" namespace RoslynSandbox { using System; using System.IO; internal abstract class FooBase : IDisposable { private readonly Stream stream = File.OpenRead(string.Empty); private bool disposed = false; public void Dispose() { this.Dispose(true); } protected virtual void Dispose(bool disposing) { if (this.disposed) { return; } this.disposed = true; if (disposing) { this.stream.Dispose(); } } } }"; var fooImplCode = @" namespace RoslynSandbox { using System; using System.IO; internal class FooImpl : FooBase { private readonly Stream stream = File.OpenRead(string.Empty); private bool disposed; protected override void Dispose(bool disposing) { if (this.disposed) { return; } this.disposed = true; if (disposing) { this.stream.Dispose(); } base.Dispose(disposing); } } }"; var withOptionalParameterCode = @" namespace RoslynSandbox { using System; using System.Collections.Generic; internal class Foo { private IDisposable disposable; public Foo(IDisposable disposable) { if (disposable == null) throw new ArgumentNullException(nameof(disposable)); this.disposable = Bar(disposable); } private static IDisposable Bar(IDisposable disposable, IEnumerable<IDisposable> disposables = null) { if (disposables == null) { return Bar(disposable, new[] { disposable }); } return disposable; } } }"; var reactiveCode = @" namespace RoslynSandbox { using System; using System.IO; using System.Reactive.Disposables; using System.Reactive.Linq; internal abstract class RxFoo : IDisposable { private readonly IDisposable subscription; private readonly SingleAssignmentDisposable singleAssignmentDisposable = new SingleAssignmentDisposable(); public RxFoo(int no) : this(Create(no)) { } public RxFoo(IObservable<object> observable) { if (observable == null) throw new ArgumentNullException(nameof(observable)); this.subscription = observable.Subscribe(_ => { }); this.singleAssignmentDisposable.Disposable = observable.Subscribe(_ => { }); } public void Dispose() { this.subscription.Dispose(); this.singleAssignmentDisposable.Dispose(); } private static IObservable<object> Create(int i) { return Observable.Empty<object>(); } } }"; var sources = new[] { disposableCode, fooListCode, fooCode, fooBaseCode, fooImplCode, withOptionalParameterCode, reactiveCode }; AnalyzerAssert.Valid(analyzer, sources); }
public void ValidCodeProject(DiagnosticAnalyzer analyzer) { AnalyzerAssert.Valid(analyzer, ValidCodeProjectSln); }
public void AnalyzersSolution(DiagnosticAnalyzer analyzer) { AnalyzerAssert.Valid(analyzer, AnalyzersProjectSolution); }
public void RecursiveSample(DiagnosticAnalyzer analyzer) { var testCode = @" namespace RoslynSandbox { using System.Collections.Generic; internal abstract class Foo { public Foo() : this() { var value = this.RecursiveExpressionBodyProperty; value = this.RecursiveStatementBodyProperty; value = this.RecursiveExpressionBodyMethod(); value = this.RecursiveExpressionBodyMethod(1); value = this.RecursiveStatementBodyMethod(); value = this.RecursiveStatementBodyMethod(1); value = RecursiveStatementBodyMethodWithOptionalParameter(1); // value = value; } public int RecursiveExpressionBodyProperty => this.RecursiveExpressionBodyProperty; public int RecursiveStatementBodyProperty { get { return this.RecursiveStatementBodyProperty; } } public int RecursiveExpressionBodyMethod() => this.RecursiveExpressionBodyMethod(); public int RecursiveExpressionBodyMethod(int value) => this.RecursiveExpressionBodyMethod(value); public int RecursiveStatementBodyMethod() { return this.RecursiveStatementBodyMethod(); } public int RecursiveStatementBodyMethod(int value) { return this.RecursiveStatementBodyMethod(value); } public void Meh() { var value = this.RecursiveExpressionBodyProperty; value = this.RecursiveStatementBodyProperty; value = this.RecursiveExpressionBodyMethod(); value = this.RecursiveExpressionBodyMethod(1); value = this.RecursiveStatementBodyMethod(); value = this.RecursiveStatementBodyMethod(1); value = RecursiveStatementBodyMethodWithOptionalParameter(1); // value = value; } private static int RecursiveStatementBodyMethodWithOptionalParameter(int value, IEnumerable<int> values = null) { if (values == null) { return RecursiveStatementBodyMethodWithOptionalParameter(value, new[] { value }); } return value; } } }"; AnalyzerAssert.Valid(analyzer, testCode); }
public void RecursiveSample(DiagnosticAnalyzer analyzer) { var testCode = @" namespace RoslynSandbox { using System.Collections.Generic; internal abstract class Foo { public Foo() : this() { var value = this.RecursiveExpressionBodyProperty; value = this.RecursiveStatementBodyProperty; value = this.RecursiveExpressionBodyMethod(); value = this.RecursiveExpressionBodyMethod(1); value = this.RecursiveStatementBodyMethod(); value = this.RecursiveStatementBodyMethod(1); value = RecursiveStatementBodyMethodWithOptionalParameter(1); // value = value; } public int RecursiveExpressionBodyProperty => this.RecursiveExpressionBodyProperty; public int RecursiveStatementBodyProperty { get { return this.RecursiveStatementBodyProperty; } } public int RecursiveExpressionBodyMethod() => this.RecursiveExpressionBodyMethod(); public int RecursiveExpressionBodyMethod(int value) => this.RecursiveExpressionBodyMethod(value); public int RecursiveStatementBodyMethod() { return this.RecursiveStatementBodyMethod(); } public int RecursiveStatementBodyMethod(int value) { return this.RecursiveStatementBodyMethod(value); } public void Meh() { var value = this.RecursiveExpressionBodyProperty; value = this.RecursiveStatementBodyProperty; value = this.RecursiveExpressionBodyMethod(); value = this.RecursiveExpressionBodyMethod(1); value = this.RecursiveStatementBodyMethod(); value = this.RecursiveStatementBodyMethod(1); value = RecursiveStatementBodyMethodWithOptionalParameter(1); // value = value; } private static int RecursiveStatementBodyMethodWithOptionalParameter(int value, IEnumerable<int> values = null) { if (values == null) { return RecursiveStatementBodyMethodWithOptionalParameter(value, new[] { value }); } return value; } } }"; var converterCode = @" namespace RoslynSandbox { using System; using System.Globalization; using System.Windows.Controls; using System.Windows.Data; internal class ValidationErrorToStringConverter : IValueConverter { /// <summary> Gets the default instance </summary> public static readonly ValidationErrorToStringConverter Default = new ValidationErrorToStringConverter(); #pragma warning disable GU0012 /// <inheritdoc /> public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is string text) { return text; } if (value is ValidationResult result) { return this.Convert(result.ErrorContent, targetType, parameter, culture); } if (value is ValidationError error) { return this.Convert(error.ErrorContent, targetType, parameter, culture); } return value; } /// <inheritdoc /> object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotSupportedException($""{this.GetType().Name} only supports one-way conversion.""); } #pragma warning restore GU0012 } }"; AnalyzerAssert.Valid(analyzer, testCode, converterCode); }
public void RealisticExtensionMethodClass() { var testCode = @" namespace RoslynSandbox { using System; using System.Collections.Generic; internal static class EnumerableExt { internal static bool TryElementAt<TCollection, TItem>(this TCollection source, int index, out TItem result) where TCollection : IReadOnlyList<TItem> { result = default(TItem); if (source == null) { return false; } if (source.Count <= index) { return false; } result = source[index]; return true; } internal static bool TrySingle<TCollection, TItem>(this TCollection source, out TItem result) where TCollection : IReadOnlyList<TItem> { if (source.Count == 1) { result = source[0]; return true; } result = default(TItem); return false; } internal static bool TrySingle<TCollection, TItem>(this TCollection source, Func<TItem, bool> selector, out TItem result) where TCollection : IReadOnlyList<TItem> { foreach (var item in source) { if (selector(item)) { result = item; return true; } } result = default(TItem); return false; } internal static bool TryFirst<TCollection, TItem>(this TCollection source, out TItem result) where TCollection : IReadOnlyList<TItem> { if (source.Count == 0) { result = default(TItem); return false; } result = source[0]; return true; } internal static bool TryFirst<TCollection, TItem>(this TCollection source, Func<TItem, bool> selector, out TItem result) where TCollection : IReadOnlyList<TItem> { foreach (var item in source) { if (selector(item)) { result = item; return true; } } result = default(TItem); return false; } internal static bool TryLast<TCollection, TItem>(this TCollection source, out TItem result) where TCollection : IReadOnlyList<TItem> { if (source.Count == 0) { result = default(TItem); return false; } result = source[source.Count - 1]; return true; } internal static bool TryLast<TCollection, TItem>(this TCollection source, Func<TItem, bool> selector, out TItem result) where TCollection : IReadOnlyList<TItem> { for (var i = source.Count - 1; i >= 0; i--) { var item = source[i]; if (selector(item)) { result = item; return true; } } result = default(TItem); return false; } } }"; AnalyzerAssert.Valid(Analyzer, testCode); }
public void WhenInjectingCondition() { var fooCode = @" namespace RoslynSandbox { using System.ComponentModel; using System.Runtime.CompilerServices; public class Foo : INotifyPropertyChanged { private int value; public event PropertyChangedEventHandler PropertyChanged; public int Value { get { return this.value; } set { if (value == this.value) { return; } this.value = value; this.OnPropertyChanged(); } } protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } }"; var conditionCode = @" namespace RoslynSandbox { using System.Reactive.Linq; using Gu.Reactive; public class FooCondition : Condition { public FooCondition(Foo foo) : base( foo.ObservePropertyChangedSlim(x => x.Value), () => foo.Value == 2) { } } }"; var testCode = @" namespace RoslynSandbox { using System; using Gu.Reactive; public class Bar { private readonly ICondition condition; public Bar(FooCondition condition) { var foo = new Foo(); this.condition = condition; } } }"; AnalyzerAssert.Valid(Analyzer, fooCode, conditionCode, testCode); }
public void WhenCallingBaseDisposeAfterCheckDisposeAndIfDisposing() { var fooBaseCode = @" namespace RoslynSandbox { using System; public abstract class FooBase : IDisposable { private readonly IDisposable disposable = new Disposable(); private bool disposed; /// <inheritdoc/> public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (this.disposed) { return; } this.disposed = true; if (disposing) { this.disposable.Dispose(); } } } }"; var testCode = @" namespace RoslynSandbox { using System; public class Foo : FooBase { private readonly IDisposable disposable = new Disposable(); private bool disposed; protected override void Dispose(bool disposing) { if (this.disposed) { return; } this.disposed = true; if (disposing) { this.disposable.Dispose(); } base.Dispose(disposing); } } }"; AnalyzerAssert.Valid(Analyzer, DisposableCode, fooBaseCode, testCode); }
public void SomewhatRealisticSample(DiagnosticAnalyzer analyzer) { var disposableCode = @" namespace RoslynSandbox { using System; public class Disposable : IDisposable { public Disposable(string meh) : this() { } public Disposable() { } public void Dispose() { } } }"; var fooListCode = @" namespace RoslynSandbox { using System.Collections; using System.Collections.Generic; internal class FooList<T> : IReadOnlyList<T> { private readonly List<T> inner = new List<T>(); public int Count => this.inner.Count; public T this[int index] => this.inner[index]; public IEnumerator<T> GetEnumerator() { return this.inner.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this.inner).GetEnumerator(); } } }"; var foo1Code = @" namespace RoslynSandbox { using System; using System.ComponentModel; using System.IO; using System.Reactive.Disposables; public class Foo1 : IDisposable { private static readonly PropertyChangedEventArgs IsDirtyPropertyChangedEventArgs = new PropertyChangedEventArgs(nameof(IsDirty)); private readonly SingleAssignmentDisposable subscription = new SingleAssignmentDisposable(); private readonly CompositeDisposable compositeDisposable = new CompositeDisposable(); private IDisposable meh1; private IDisposable meh2; private bool isDirty; public Foo() { this.meh1 = this.RecursiveProperty; this.meh2 = this.RecursiveMethod(); this.subscription.Disposable = File.OpenRead(string.Empty); } public event PropertyChangedEventHandler PropertyChanged { add { this.PropertyChangedCore += value; } remove { this.PropertyChangedCore -= value; } } private event PropertyChangedEventHandler PropertyChangedCore; public Disposable RecursiveProperty => RecursiveProperty; public IDisposable Disposable => subscription.Disposable; public bool IsDirty { get { return this.isDirty; } private set { if (value == this.isDirty) { return; } this.isDirty = value; this.PropertyChangedCore?.Invoke(this, IsDirtyPropertyChangedEventArgs); } } public Disposable RecursiveMethod() => RecursiveMethod(); public void Meh() { using (var item = new Disposable()) { } using (var item = RecursiveProperty) { } using (RecursiveProperty) { } using (var item = RecursiveMethod()) { } using (RecursiveMethod()) { } } public void Dispose() { this.subscription.Dispose(); this.compositeDisposable.Dispose(); } internal string AddAndReturnToString() { return this.compositeDisposable.AddAndReturn(new Disposable()).ToString(); } } }"; var fooBaseCode = @" namespace RoslynSandbox { using System; using System.IO; public abstract class FooBase : IDisposable { private readonly Stream stream = File.OpenRead(string.Empty); private bool disposed = false; public void Dispose() { this.Dispose(true); } protected virtual void Dispose(bool disposing) { if (this.disposed) { return; } this.disposed = true; if (disposing) { this.stream.Dispose(); } } } }"; var fooImplCode = @" namespace RoslynSandbox { using System; using System.IO; public class FooImpl : FooBase { private readonly Stream stream = File.OpenRead(string.Empty); private bool disposed; protected override void Dispose(bool disposing) { if (this.disposed) { return; } this.disposed = true; if (disposing) { this.stream.Dispose(); } base.Dispose(disposing); } } }"; var foo2Code = @" namespace RoslynSandbox { using System; using System.Collections.Generic; public class Foo2 { private IDisposable disposable; public Foo(IDisposable disposable) { this.disposable = Bar(disposable); } private static IDisposable Bar(IDisposable disposable, IEnumerable<IDisposable> disposables = null) { if (disposables == null) { return Bar(disposable, new[] { disposable }); } return disposable; } } }"; var reactiveCode = @" namespace RoslynSandbox { using System; using System.IO; using System.Reactive.Disposables; using System.Reactive.Linq; public abstract class RxFoo : IDisposable { private readonly IDisposable subscription; private readonly SingleAssignmentDisposable singleAssignmentDisposable = new SingleAssignmentDisposable(); public RxFoo(int no) : this(Create(no)) { } public RxFoo(IObservable<object> observable) { this.subscription = observable.Subscribe(_ => { }); this.singleAssignmentDisposable.Disposable = observable.Subscribe(_ => { }); } public void Dispose() { this.subscription.Dispose(); this.singleAssignmentDisposable.Dispose(); } private static IObservable<object> Create(int i) { return Observable.Empty<object>(); } } }"; var lazyCode = @" namespace RoslynSandbox { using System; public sealed class LazyFoo : IDisposable { private readonly IDisposable created; private bool disposed; private IDisposable lazyDisposable; public LazyFoo(IDisposable injected) { this.Disposable = injected ?? (this.created = new Disposable()); } public IDisposable Disposable { get; } public IDisposable LazyDisposable => this.lazyDisposable ?? (this.lazyDisposable = new Disposable()); public void Dispose() { if (this.disposed) { return; } this.disposed = true; this.created?.Dispose(); this.lazyDisposable?.Dispose(); } } }"; var asyncCode = @" namespace RoslynSandbox { using System.IO; using System.Threading.Tasks; public static class FooAsync { public static async Task<string> Bar1Async() { using (var stream = await ReadAsync(string.Empty)) { using (var reader = new StreamReader(stream)) { return reader.ReadLine(); } } } public static async Task<string> Bar2Async() { using (var stream = await ReadAsync(string.Empty).ConfigureAwait(false)) { using (var reader = new StreamReader(stream)) { return reader.ReadLine(); } } } private static async Task<Stream> ReadAsync(this string fileName) { var stream = new MemoryStream(); using (var fileStream = File.OpenRead(fileName)) { await fileStream.CopyToAsync(stream) .ConfigureAwait(false); } stream.Position = 0; return stream; } } }"; var compositeDisposableExtCode = @" namespace RoslynSandbox { using System; 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; } } }"; var foo3Code = @" namespace RoslynSandbox { using System.IO; public class FooOut { public static bool TryGetStream(out Stream stream) { return TryGetStreamCore(out stream); } public void Bar() { IDisposable disposable; using (disposable = new Disposable()) { } } public void Baz() { IDisposable disposable; if (TryGet(out disposable)) { using (disposable) { } } } private static bool TryGetStreamCore(out Stream stream) { stream = File.OpenRead(string.Empty); return true; } } }"; var sources = new[] { disposableCode, fooListCode, foo1Code, foo2Code, foo3Code, fooBaseCode, fooImplCode, reactiveCode, lazyCode, asyncCode, compositeDisposableExtCode, }; AnalyzerAssert.Valid(analyzer, sources); }
public void NotifyingMutables() { var testCode = @" namespace RoslynSandbox { using System.ComponentModel; using System.Runtime.CompilerServices; public class Foo : INotifyPropertyChanged { private int _a; private int _b; private int _c; private int _d; public Foo(int a, int b, int c, int d) { this.A = a; this.B = b; this.C = c; this.D = d; } public event PropertyChangedEventHandler PropertyChanged; public int A { get { return _a; } set { if (value == _a) return; _a = value; OnPropertyChanged(); } } public int B { get { return _b; } set { if (value == _b) return; _b = value; OnPropertyChanged(); } } public int C { get { return _c; } set { if (value == _c) return; _c = value; OnPropertyChanged(); } } public int D { get { return _d; } set { if (value == _d) return; _d = value; OnPropertyChanged(); } } protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } }"; AnalyzerAssert.Valid(Analyzer, testCode); }