Skip to content

Commit 5ef6da6

Browse files
fix: Add unit tests and improve analyzers for handling generated code and diagnostics (#399)
1 parent 49c1eb7 commit 5ef6da6

12 files changed

Lines changed: 354 additions & 116 deletions

IntelliTect.Analyzer/IntelliTect.Analyzer.Test/AttributesOnSeparateLinesTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,31 @@ static void Main()
390390
await VerifyCSharpFix(test, fixTest);
391391
}
392392

393+
[TestMethod]
394+
[Description("Analyzer should not report on generated code")]
395+
public void AttributesOnSameLine_InGeneratedCode_NoDiagnostic()
396+
{
397+
string test = @"using System;
398+
using System.CodeDom.Compiler;
399+
400+
namespace ConsoleApp
401+
{
402+
class AAttribute : Attribute { }
403+
class BAttribute : Attribute { }
404+
405+
[GeneratedCode(""tool"", ""1.0"")]
406+
class Program
407+
{
408+
[A][B]
409+
static void Main()
410+
{
411+
}
412+
}
413+
}";
414+
// Should NOT produce a diagnostic for attributes on same line inside generated code
415+
VerifyCSharpDiagnostic(test);
416+
}
417+
393418
private static DiagnosticResult GetExpectedDiagnosticResult(int line, int col)
394419
{
395420
return new DiagnosticResult

IntelliTect.Analyzer/IntelliTect.Analyzer.Test/FavorEnumeratorDirectoryCallsTests.cs

Lines changed: 208 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,27 @@ public class FavorEnumeratorDirectoryCallsTests : CodeFixVerifier
1111
[TestMethod]
1212
public void UsageOfDirectoryGetFiles_ProducesInfoMessage()
1313
{
14-
string source = @"using System;
15-
using System.Diagnostics;
16-
using System.IO;
14+
string source = """
15+
using System;
16+
using System.Diagnostics;
17+
using System.IO;
1718
18-
namespace ConsoleApp5
19-
{
20-
class Program
21-
{
22-
static void Main(string[] args)
23-
{
24-
string[] files = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory);
19+
namespace ConsoleApp5
20+
{
21+
class Program
22+
{
23+
static void Main(string[] args)
24+
{
25+
string[] files = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory);
2526
26-
foreach (string file in files)
27-
{
28-
Console.WriteLine($""File found: ${file}"");
29-
}
30-
}
31-
}
32-
}";
27+
foreach (string file in files)
28+
{
29+
Console.WriteLine($"File found: ${file}");
30+
}
31+
}
32+
}
33+
}
34+
""";
3335
VerifyCSharpDiagnostic(source,
3436
new DiagnosticResult
3537
{
@@ -46,26 +48,28 @@ static void Main(string[] args)
4648
[TestMethod]
4749
public void DeclarationOfOtherDirectoryGetFiles_ProducesNothing()
4850
{
49-
string source = @"using System;
50-
using System.Diagnostics;
51-
using System.IO;
51+
string source = """
52+
using System;
53+
using System.Diagnostics;
54+
using System.IO;
5255
53-
namespace ConsoleApp5
54-
{
55-
public static class Directory {
56-
public static string[] GetFiles(string path) => Array.Empty<string>();
57-
}
56+
namespace ConsoleApp5
57+
{
58+
public static class Directory {
59+
public static string[] GetFiles(string path) => Array.Empty<string>();
60+
}
5861
5962
60-
class Program
61-
{
62-
static void Main(string[] args)
63-
{
64-
string[] files = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory);
63+
class Program
64+
{
65+
static void Main(string[] args)
66+
{
67+
string[] files = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory);
6568
66-
}
67-
}
68-
}";
69+
}
70+
}
71+
}
72+
""";
6973
VerifyCSharpDiagnostic(source);
7074
}
7175

@@ -79,25 +83,27 @@ protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer()
7983
[TestMethod]
8084
public void UsageOfDirectoryGetDirectories_ProducesInfoMessage()
8185
{
82-
string source = @"using System;
83-
using System.Diagnostics;
84-
using System.IO;
86+
string source = """
87+
using System;
88+
using System.Diagnostics;
89+
using System.IO;
8590
86-
namespace ConsoleApp5
87-
{
88-
class Program
89-
{
90-
static void Main(string[] args)
91-
{
92-
string[] files = Directory.GetDirectories(AppDomain.CurrentDomain.BaseDirectory);
91+
namespace ConsoleApp5
92+
{
93+
class Program
94+
{
95+
static void Main(string[] args)
96+
{
97+
string[] files = Directory.GetDirectories(AppDomain.CurrentDomain.BaseDirectory);
9398
94-
foreach (string file in files)
95-
{
96-
Console.WriteLine($""File found: ${file}"");
97-
}
98-
}
99-
}
100-
}";
99+
foreach (string file in files)
100+
{
101+
Console.WriteLine($"File found: ${file}");
102+
}
103+
}
104+
}
105+
}
106+
""";
101107
VerifyCSharpDiagnostic(source,
102108
new DiagnosticResult
103109
{
@@ -114,52 +120,173 @@ static void Main(string[] args)
114120
[TestMethod]
115121
public void DeclarationOfOtherDirectoryGetDirectories_ProducesNothing()
116122
{
117-
string source = @"using System;
118-
using System.Diagnostics;
119-
using System.IO;
123+
string source = """
124+
using System;
125+
using System.Diagnostics;
126+
using System.IO;
120127
121-
namespace ConsoleApp5
122-
{
123-
public static class Directory {
124-
public static string[] GetDirectories(string path) => Array.Empty<string>();
125-
}
128+
namespace ConsoleApp5
129+
{
130+
public static class Directory {
131+
public static string[] GetDirectories(string path) => Array.Empty<string>();
132+
}
126133
127134
128-
class Program
129-
{
130-
static void Main(string[] args)
131-
{
132-
string[] files = Directory.GetDirectories(AppDomain.CurrentDomain.BaseDirectory);
135+
class Program
136+
{
137+
static void Main(string[] args)
138+
{
139+
string[] files = Directory.GetDirectories(AppDomain.CurrentDomain.BaseDirectory);
133140
134-
}
135-
}
136-
}";
141+
}
142+
}
143+
}
144+
""";
137145
VerifyCSharpDiagnostic(source);
138146
}
139147

140148
[TestMethod]
141149
[Description("Issue 53")]
142150
public void Diagnostic_HandlesMemberAccess()
143151
{
144-
string source = @"
145-
using System;
152+
string source = """
146153
147-
namespace Namespace
148-
{
149-
class Program
150-
{
151-
static void Main(string[] args)
154+
using System;
155+
156+
namespace Namespace
157+
{
158+
class Program
159+
{
160+
static void Main(string[] args)
161+
{
162+
int selection;
163+
if (!int.TryParse(Console.ReadLine(), out selection))
164+
{
165+
selection = 0;
166+
}
167+
}
168+
}
169+
}
170+
""";
171+
VerifyCSharpDiagnostic(source);
172+
}
173+
174+
[TestMethod]
175+
[Description("Cast<IdentifierNameSyntax>() throws InvalidCastException on generic method calls")]
176+
public void GenericMethodCallOnDirectory_DoesNotThrow()
152177
{
153-
int selection;
154-
if (!int.TryParse(Console.ReadLine(), out selection))
155-
{
156-
selection = 0;
157-
}
178+
// memberAccess.ChildNodes().Cast<IdentifierNameSyntax>() will throw
179+
// if any child node is not IdentifierNameSyntax (e.g. GenericNameSyntax).
180+
// This test uses a generic method call on a class named Directory.
181+
string source = """
182+
183+
using System;
184+
using System.Collections.Generic;
185+
186+
namespace ConsoleApp
187+
{
188+
public static class Directory
189+
{
190+
public static List<T> GetItems<T>() => new List<T>();
191+
}
192+
193+
class Program
194+
{
195+
static void Main(string[] args)
196+
{
197+
var items = Directory.GetItems<string>();
198+
}
199+
}
200+
}
201+
""";
202+
VerifyCSharpDiagnostic(source);
158203
}
159-
}
160-
}
161-
";
204+
205+
[TestMethod]
206+
[Description("Analyzer should not report when symbol is unresolved (compile error)")]
207+
public void UnresolvableDirectoryType_NoDiagnostic()
208+
{
209+
// When symbol.Symbol is null it means the code has a compile error,
210+
// not that it's System.IO.Directory. Should not produce a false positive.
211+
string source = """
212+
213+
namespace ConsoleApp
214+
{
215+
class Program
216+
{
217+
static void Main(string[] args)
218+
{
219+
var files = Directory.GetFiles(".");
220+
}
221+
}
222+
}
223+
""";
224+
// No 'using System.IO' so Directory is unresolvable — should NOT produce diagnostic
162225
VerifyCSharpDiagnostic(source);
163226
}
227+
228+
[TestMethod]
229+
[Description("Detect fully-qualified System.IO.Directory.GetFiles()")]
230+
public void FullyQualifiedDirectoryGetFiles_ProducesInfoMessage()
231+
{
232+
string source = """
233+
234+
using System;
235+
236+
namespace ConsoleApp
237+
{
238+
class Program
239+
{
240+
static void Main(string[] args)
241+
{
242+
string[] files = System.IO.Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory);
243+
}
244+
}
245+
}
246+
""";
247+
VerifyCSharpDiagnostic(source,
248+
new DiagnosticResult
249+
{
250+
Id = "INTL0301",
251+
Severity = DiagnosticSeverity.Info,
252+
Message = "Favor using the method `EnumerateFiles` over the `GetFiles` method",
253+
Locations =
254+
[
255+
new DiagnosticResultLocation("Test0.cs", 10, 30)
256+
]
257+
});
258+
}
259+
260+
[TestMethod]
261+
[Description("Detect fully-qualified System.IO.Directory.GetDirectories()")]
262+
public void FullyQualifiedDirectoryGetDirectories_ProducesInfoMessage()
263+
{
264+
string source = """
265+
266+
using System;
267+
268+
namespace ConsoleApp
269+
{
270+
class Program
271+
{
272+
static void Main(string[] args)
273+
{
274+
string[] dirs = System.IO.Directory.GetDirectories(AppDomain.CurrentDomain.BaseDirectory);
275+
}
276+
}
277+
}
278+
""";
279+
VerifyCSharpDiagnostic(source,
280+
new DiagnosticResult
281+
{
282+
Id = "INTL0302",
283+
Severity = DiagnosticSeverity.Info,
284+
Message = "Favor using the method `EnumerateDirectories` over the `GetDirectories` method",
285+
Locations =
286+
[
287+
new DiagnosticResultLocation("Test0.cs", 10, 29)
288+
]
289+
});
290+
}
164291
}
165292
}

0 commit comments

Comments
 (0)