Skip to content

Commit 4962177

Browse files
authored
Merge pull request #195 from DotNetAnalyzers/winforms
Figure out form.components.add in some cases.
2 parents a5ab4b8 + 82429a9 commit 4962177

11 files changed

Lines changed: 334 additions & 1 deletion

File tree

IDisposableAnalyzers.Test/AssemblyAttributes.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
typeof(System.Xaml.XamlLanguage),
1818
typeof(Moq.Mock<>),
1919
typeof(Ninject.KernelConfiguration),
20+
typeof(System.Windows.Forms.Form),
2021
typeof(Gu.Roslyn.AnalyzerExtensions.SyntaxTokenExt),
2122
typeof(Gu.Roslyn.CodeFixExtensions.Parse),
2223
typeof(NUnit.Framework.Assert))]

IDisposableAnalyzers.Test/Helpers/DisposableMemberTests.cs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public void Dispose()
3636
}
3737

3838
[Test]
39-
public static void DiposedInOverridden()
39+
public static void DisposedInOverridden()
4040
{
4141
var syntaxTree = CSharpSyntaxTree.ParseText(@"
4242
namespace N
@@ -70,5 +70,35 @@ protected override void M()
7070
var fieldSymbol = semanticModel.GetDeclaredSymbolSafe(field, CancellationToken.None);
7171
Assert.AreEqual(Result.Yes, DisposableMember.IsDisposed(new FieldOrProperty(fieldSymbol), (TypeDeclarationSyntax)field.Parent, semanticModel, CancellationToken.None));
7272
}
73+
74+
[Ignore("tbd")]
75+
[TestCase("this.components.Add(this.stream)")]
76+
[TestCase("components.Add(stream)")]
77+
public static void FieldAddedToFormComponents(string expression)
78+
{
79+
var syntaxTree = CSharpSyntaxTree.ParseText(@"
80+
namespace ValidCode
81+
{
82+
using System.IO;
83+
using System.Windows.Forms;
84+
85+
public class Winform : Form
86+
{
87+
private readonly Stream stream;
88+
89+
Winform()
90+
{
91+
this.stream = File.OpenRead(string.Empty);
92+
// Since this is added to components, it is automatically disposed of with the form.
93+
this.components.Add(this.stream);
94+
}
95+
}
96+
}".AssertReplace("this.components.Add(this.stream)", expression));
97+
var compilation = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
98+
var semanticModel = compilation.GetSemanticModel(syntaxTree);
99+
var field = syntaxTree.FindFieldDeclaration("stream");
100+
var fieldSymbol = semanticModel.GetDeclaredSymbolSafe(field, CancellationToken.None);
101+
Assert.AreEqual(Result.Yes, DisposableMember.IsDisposed(new FieldOrProperty(fieldSymbol), (TypeDeclarationSyntax)field.Parent, semanticModel, CancellationToken.None));
102+
}
73103
}
74104
}

IDisposableAnalyzers.Test/Helpers/DisposableWalkerTests.Disposes.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,33 @@ internal C(string fileName)
144144
Assert.AreEqual(true, semanticModel.TryGetSymbol(value, CancellationToken.None, out ILocalSymbol symbol));
145145
Assert.AreEqual(true, DisposableWalker.Disposes(symbol, semanticModel, CancellationToken.None));
146146
}
147+
148+
[Test]
149+
public static void WhenAddedToFormComponents()
150+
{
151+
var code = @"
152+
namespace ValidCode
153+
{
154+
using System.IO;
155+
using System.Windows.Forms;
156+
157+
public class Winform : Form
158+
{
159+
Winform()
160+
{
161+
var stream = File.OpenRead(string.Empty);
162+
// Since this is added to components, it is automatically disposed of with the form.
163+
this.components.Add(stream);
164+
}
165+
}
166+
}";
167+
var syntaxTree = CSharpSyntaxTree.ParseText(code);
168+
var compilation = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
169+
var semanticModel = compilation.GetSemanticModel(syntaxTree);
170+
var value = syntaxTree.FindVariableDeclaration("stream = File.OpenRead(string.Empty)");
171+
Assert.AreEqual(true, semanticModel.TryGetSymbol(value, CancellationToken.None, out ILocalSymbol symbol));
172+
Assert.AreEqual(true, DisposableWalker.Disposes(symbol, semanticModel, CancellationToken.None));
173+
}
147174
}
148175
}
149176
}

IDisposableAnalyzers.Test/IDISP001DisposeCreatedTests/Valid.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,5 +586,53 @@ public Pair(T item1, T item2)
586586

587587
RoslynAssert.Valid(Analyzer, code);
588588
}
589+
590+
[TestCase("this.components.Add(stream)")]
591+
[TestCase("components.Add(stream)")]
592+
public static void LocalAddedToFormComponents(string expression)
593+
{
594+
var code = @"
595+
namespace ValidCode
596+
{
597+
using System.IO;
598+
using System.Windows.Forms;
599+
600+
public class Winform : Form
601+
{
602+
Winform()
603+
{
604+
var stream = File.OpenRead(string.Empty);
605+
// Since this is added to components, it is automatically disposed of with the form.
606+
this.components.Add(stream);
607+
}
608+
}
609+
}".AssertReplace("this.components.Add(stream)", expression);
610+
RoslynAssert.NoAnalyzerDiagnostics(Analyzer, code);
611+
}
612+
613+
[TestCase("this.components.Add(this.stream)")]
614+
[TestCase("components.Add(stream)")]
615+
public static void FieldAddedToFormComponents(string expression)
616+
{
617+
var code = @"
618+
namespace ValidCode
619+
{
620+
using System.IO;
621+
using System.Windows.Forms;
622+
623+
public class Winform : Form
624+
{
625+
private readonly Stream stream;
626+
627+
Winform()
628+
{
629+
this.stream = File.OpenRead(string.Empty);
630+
// Since this is added to components, it is automatically disposed of with the form.
631+
this.components.Add(this.stream);
632+
}
633+
}
634+
}".AssertReplace("this.components.Add(this.stream)", expression);
635+
RoslynAssert.NoAnalyzerDiagnostics(Analyzer, code);
636+
}
589637
}
590638
}

IDisposableAnalyzers.Test/IDISP002DisposeMemberTests/Valid.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,5 +1064,54 @@ protected override void M()
10641064

10651065
RoslynAssert.Valid(Analyzer, baseClass, code);
10661066
}
1067+
1068+
[TestCase("this.components.Add(stream)")]
1069+
[TestCase("components.Add(stream)")]
1070+
public static void LocalAddedToFormComponents(string expression)
1071+
{
1072+
var code = @"
1073+
namespace ValidCode
1074+
{
1075+
using System.IO;
1076+
using System.Windows.Forms;
1077+
1078+
public class Winform : Form
1079+
{
1080+
Winform()
1081+
{
1082+
var stream = File.OpenRead(string.Empty);
1083+
// Since this is added to components, it is automatically disposed of with the form.
1084+
this.components.Add(stream);
1085+
}
1086+
}
1087+
}".AssertReplace("this.components.Add(stream)", expression);
1088+
RoslynAssert.NoAnalyzerDiagnostics(Analyzer, code);
1089+
}
1090+
1091+
[Ignore("tbd")]
1092+
[TestCase("this.components.Add(this.stream)")]
1093+
[TestCase("components.Add(stream)")]
1094+
public static void FieldAddedToFormComponents(string expression)
1095+
{
1096+
var code = @"
1097+
namespace ValidCode
1098+
{
1099+
using System.IO;
1100+
using System.Windows.Forms;
1101+
1102+
public class Winform : Form
1103+
{
1104+
private readonly Stream stream;
1105+
1106+
Winform()
1107+
{
1108+
this.stream = File.OpenRead(string.Empty);
1109+
// Since this is added to components, it is automatically disposed of with the form.
1110+
this.components.Add(this.stream);
1111+
}
1112+
}
1113+
}".AssertReplace("this.components.Add(this.stream)", expression);
1114+
RoslynAssert.NoAnalyzerDiagnostics(Analyzer, code);
1115+
}
10671116
}
10681117
}

IDisposableAnalyzers.Test/IDISP003DisposeBeforeReassigningTests/Valid.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1407,5 +1407,53 @@ public C1 GetOrCreateFoo()
14071407

14081408
RoslynAssert.Valid(Analyzer, c1, code);
14091409
}
1410+
1411+
[TestCase("this.components.Add(stream)")]
1412+
[TestCase("components.Add(stream)")]
1413+
public static void LocalAddedToFormComponents(string expression)
1414+
{
1415+
var code = @"
1416+
namespace ValidCode
1417+
{
1418+
using System.IO;
1419+
using System.Windows.Forms;
1420+
1421+
public class Winform : Form
1422+
{
1423+
Winform()
1424+
{
1425+
var stream = File.OpenRead(string.Empty);
1426+
// Since this is added to components, it is automatically disposed of with the form.
1427+
this.components.Add(stream);
1428+
}
1429+
}
1430+
}".AssertReplace("this.components.Add(stream)", expression);
1431+
RoslynAssert.NoAnalyzerDiagnostics(Analyzer, code);
1432+
}
1433+
1434+
[TestCase("this.components.Add(this.stream)")]
1435+
[TestCase("components.Add(stream)")]
1436+
public static void FieldAddedToFormComponents(string expression)
1437+
{
1438+
var code = @"
1439+
namespace ValidCode
1440+
{
1441+
using System.IO;
1442+
using System.Windows.Forms;
1443+
1444+
public class Winform : Form
1445+
{
1446+
private readonly Stream stream;
1447+
1448+
Winform()
1449+
{
1450+
this.stream = File.OpenRead(string.Empty);
1451+
// Since this is added to components, it is automatically disposed of with the form.
1452+
this.components.Add(this.stream);
1453+
}
1454+
}
1455+
}".AssertReplace("this.components.Add(this.stream)", expression);
1456+
RoslynAssert.NoAnalyzerDiagnostics(Analyzer, code);
1457+
}
14101458
}
14111459
}

IDisposableAnalyzers.Test/IDISP004DoNotIgnoreCreatedTests/Valid.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,5 +790,53 @@ public async Task M()
790790
}";
791791
RoslynAssert.Valid(Analyzer, code);
792792
}
793+
794+
[TestCase("this.components.Add(stream)")]
795+
[TestCase("components.Add(stream)")]
796+
public static void LocalAddedToFormComponents(string expression)
797+
{
798+
var code = @"
799+
namespace ValidCode
800+
{
801+
using System.IO;
802+
using System.Windows.Forms;
803+
804+
public class Winform : Form
805+
{
806+
Winform()
807+
{
808+
var stream = File.OpenRead(string.Empty);
809+
// Since this is added to components, it is automatically disposed of with the form.
810+
this.components.Add(stream);
811+
}
812+
}
813+
}".AssertReplace("this.components.Add(stream)", expression);
814+
RoslynAssert.NoAnalyzerDiagnostics(Analyzer, code);
815+
}
816+
817+
[TestCase("this.components.Add(this.stream)")]
818+
[TestCase("components.Add(stream)")]
819+
public static void FieldAddedToFormComponents(string expression)
820+
{
821+
var code = @"
822+
namespace ValidCode
823+
{
824+
using System.IO;
825+
using System.Windows.Forms;
826+
827+
public class Winform : Form
828+
{
829+
private readonly Stream stream;
830+
831+
Winform()
832+
{
833+
this.stream = File.OpenRead(string.Empty);
834+
// Since this is added to components, it is automatically disposed of with the form.
835+
this.components.Add(this.stream);
836+
}
837+
}
838+
}".AssertReplace("this.components.Add(this.stream)", expression);
839+
RoslynAssert.NoAnalyzerDiagnostics(Analyzer, code);
840+
}
793841
}
794842
}

IDisposableAnalyzers.Test/IDISP006ImplementIDisposableTests/ValidCode.Inheritance.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,55 @@ protected override void M()
213213

214214
RoslynAssert.Valid(Analyzer, Descriptor, baseClass, code);
215215
}
216+
217+
[TestCase("this.components.Add(stream)")]
218+
[TestCase("components.Add(stream)")]
219+
public static void LocalAddedToFormComponents(string expression)
220+
{
221+
var code = @"
222+
namespace ValidCode
223+
{
224+
using System.IO;
225+
using System.Windows.Forms;
226+
227+
public class Winform : Form
228+
{
229+
Winform()
230+
{
231+
var stream = File.OpenRead(string.Empty);
232+
// Since this is added to components, it is automatically disposed of with the form.
233+
this.components.Add(stream);
234+
}
235+
}
236+
}".AssertReplace("this.components.Add(stream)", expression);
237+
RoslynAssert.NoAnalyzerDiagnostics(Analyzer, code);
238+
}
239+
240+
[Ignore("tbd")]
241+
[TestCase("this.components.Add(this.stream)")]
242+
[TestCase("components.Add(stream)")]
243+
public static void FieldAddedToFormComponents(string expression)
244+
{
245+
var code = @"
246+
namespace ValidCode
247+
{
248+
using System.IO;
249+
using System.Windows.Forms;
250+
251+
public class Winform : Form
252+
{
253+
private readonly Stream stream;
254+
255+
Winform()
256+
{
257+
this.stream = File.OpenRead(string.Empty);
258+
// Since this is added to components, it is automatically disposed of with the form.
259+
this.components.Add(this.stream);
260+
}
261+
}
262+
}".AssertReplace("this.components.Add(this.stream)", expression);
263+
RoslynAssert.NoAnalyzerDiagnostics(Analyzer, code);
264+
}
216265
}
217266
}
218267
}

IDisposableAnalyzers/Helpers/KnownSymbols/KnownSymbol.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ internal static class KnownSymbol
6666
internal static readonly CompositeDisposableType CompositeDisposable = new CompositeDisposableType();
6767

6868
internal static readonly PasswordBoxType PasswordBox = new PasswordBoxType();
69+
internal static readonly QualifiedType SystemWindowsFormsForm = new QualifiedType("System.Windows.Forms.Form");
6970

7071
internal static readonly QualifiedType NUnitSetUpAttribute = new QualifiedType("NUnit.Framework.SetUpAttribute");
7172
internal static readonly QualifiedType NUnitTearDownAttribute = new QualifiedType("NUnit.Framework.TearDownAttribute");

IDisposableAnalyzers/Helpers/Walkers/DisposableWalker.Disposes.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ when Identity(candidate, recursion) is { } id &&
162162
{ Parent: EqualsValueClauseSyntax { Parent: VariableDeclaratorSyntax variableDeclarator } }
163163
when recursion.Target(variableDeclarator) is { } target
164164
=> Disposes(target, recursion),
165+
{ Parent: ArgumentSyntax { Parent: ArgumentListSyntax { Parent: InvocationExpressionSyntax invocation } } }
166+
when Winform.IsComponentsAdd(invocation, recursion.SemanticModel, recursion.CancellationToken)
167+
=> true,
165168
{ Parent: ArgumentSyntax argument }
166169
when recursion.Target(argument) is { } target
167170
=> DisposedByReturnValue(target, recursion, out var wrapper) &&

0 commit comments

Comments
 (0)