Skip to content

Commit fcb6d3f

Browse files
silkfiretimcassell
andauthored
Fix for issue #2864 (#2865)
* Fix issue #2864 * Formatting * Formatting --------- Co-authored-by: Tim Cassell <cassell.timothy@gmail.com>
1 parent fdebe7c commit fcb6d3f

File tree

8 files changed

+883
-73
lines changed

8 files changed

+883
-73
lines changed

src/BenchmarkDotNet.Analyzers/AnalyzerHelper.cs

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ namespace BenchmarkDotNet.Analyzers;
1010

1111
internal static class AnalyzerHelper
1212
{
13+
internal const string InterceptorsNamespaces = "InterceptorsNamespaces";
14+
1315
public static LocalizableResourceString GetResourceString(string name)
1416
=> new(name, BenchmarkDotNetAnalyzerResources.ResourceManager, typeof(BenchmarkDotNetAnalyzerResources));
1517

@@ -143,48 +145,58 @@ public static string NormalizeTypeName(INamedTypeSymbol namedTypeSymbol)
143145
return typeName;
144146
}
145147

146-
public static bool IsAssignableToField(Compilation compilation, ITypeSymbol targetType, string valueExpression, Optional<object?> constantValue, string? valueType)
148+
public static bool IsAssignableToField(Compilation compilation, LanguageVersion languageVersion, string? valueTypeContainingNamespace, ITypeSymbol targetType, string valueExpression, Optional<object?> constantValue, string? valueType)
147149
{
148150
const string codeTemplate1 = """
151+
{0}
152+
149153
file static class Internal {{
150-
static readonly {0} x = {1};
154+
static readonly {1} x = {2};
151155
}}
152156
""";
153157

154158
const string codeTemplate2 = """
159+
{0}
160+
155161
file static class Internal {{
156-
static readonly {0} x = ({1}){2};
162+
static readonly {1} x = ({2}){3};
157163
}}
158164
""";
159165

160-
return IsAssignableTo(codeTemplate1, codeTemplate2, compilation, targetType, valueExpression, constantValue, valueType);
166+
return IsAssignableTo(codeTemplate1, codeTemplate2, compilation, languageVersion, valueTypeContainingNamespace, targetType, valueExpression, constantValue, valueType);
161167
}
162168

163-
public static bool IsAssignableToLocal(Compilation compilation, ITypeSymbol targetType, string valueExpression, Optional<object?> constantValue, string? valueType)
169+
public static bool IsAssignableToLocal(Compilation compilation, LanguageVersion languageVersion, string? valueTypeContainingNamespace, ITypeSymbol targetType, string valueExpression, Optional<object?> constantValue, string? valueType)
164170
{
165171
const string codeTemplate1 = """
172+
{0}
173+
166174
file static class Internal {{
167175
static void Method() {{
168-
{0} x = {1};
176+
{1} x = {2};
169177
}}
170178
}}
171179
""";
172180

173181
const string codeTemplate2 = """
182+
{0}
183+
174184
file static class Internal {{
175185
static void Method() {{
176-
{0} x = ({1}){2};
186+
{1} x = ({2}){3};
177187
}}
178188
}}
179189
""";
180190

181-
return IsAssignableTo(codeTemplate1, codeTemplate2, compilation, targetType, valueExpression, constantValue, valueType);
191+
return IsAssignableTo(codeTemplate1, codeTemplate2, compilation, languageVersion, valueTypeContainingNamespace, targetType, valueExpression, constantValue, valueType);
182192
}
183193

184-
private static bool IsAssignableTo(string codeTemplate1, string codeTemplate2, Compilation compilation, ITypeSymbol targetType, string valueExpression, Optional<object?> constantValue, string? valueType)
194+
private static bool IsAssignableTo(string codeTemplate1, string codeTemplate2, Compilation compilation, LanguageVersion languageVersion, string? valueTypeContainingNamespace, ITypeSymbol targetType, string valueExpression, Optional<object?> constantValue, string? valueType)
185195
{
186-
var hasCompilerDiagnostics = HasNoCompilerDiagnostics(string.Format(codeTemplate1, targetType, valueExpression), compilation);
187-
if (hasCompilerDiagnostics)
196+
var usingDirective = valueTypeContainingNamespace != null ? $"using {valueTypeContainingNamespace};" : "";
197+
198+
var hasNoCompilerDiagnostics = HasNoCompilerDiagnostics(string.Format(codeTemplate1, usingDirective, targetType, valueExpression), compilation, languageVersion);
199+
if (hasNoCompilerDiagnostics)
188200
{
189201
return true;
190202
}
@@ -200,16 +212,19 @@ private static bool IsAssignableTo(string codeTemplate1, string codeTemplate2, C
200212
return false;
201213
}
202214

203-
return HasNoCompilerDiagnostics(string.Format(codeTemplate2, targetType, valueType, constantLiteral), compilation);
215+
return HasNoCompilerDiagnostics(string.Format(codeTemplate2, usingDirective, targetType, valueType, constantLiteral), compilation, languageVersion);
204216
}
205217

206-
private static bool HasNoCompilerDiagnostics(string code, Compilation compilation)
218+
private static bool HasNoCompilerDiagnostics(string code, Compilation compilation, LanguageVersion languageVersion)
207219
{
208-
var syntaxTree = CSharpSyntaxTree.ParseText(code);
220+
var compilationTestSyntaxTree = CSharpSyntaxTree.ParseText(code, new CSharpParseOptions(languageVersion));
221+
222+
var syntaxTreesWithInterceptorsNamespaces = compilation.SyntaxTrees.Where(st => st.Options.Features.ContainsKey(InterceptorsNamespaces));
209223

210224
var compilerDiagnostics = compilation
211-
.AddSyntaxTrees(syntaxTree)
212-
.GetSemanticModel(syntaxTree)
225+
.RemoveSyntaxTrees(syntaxTreesWithInterceptorsNamespaces)
226+
.AddSyntaxTrees(compilationTestSyntaxTree)
227+
.GetSemanticModel(compilationTestSyntaxTree)
213228
.GetMethodBodyDiagnostics()
214229
.Where(d => d.DefaultSeverity == DiagnosticSeverity.Error)
215230
.ToList();

src/BenchmarkDotNet.Analyzers/Attributes/ArgumentsAttributeAnalyzer.cs

Lines changed: 89 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -281,28 +281,109 @@ void ReportIfNotImplicitlyConvertibleValueTypeDiagnostic(Func<int, ExpressionSyn
281281

282282
var constantValue = context.SemanticModel.GetConstantValue(valueExpressionSyntax);
283283

284+
var expectedValueTypeString = methodParameterTypeSymbol.ToString();
284285
var actualValueTypeSymbol = context.SemanticModel.GetTypeInfo(valueExpressionSyntax).Type;
285-
if (actualValueTypeSymbol != null && actualValueTypeSymbol.TypeKind != TypeKind.Error)
286+
287+
if (actualValueTypeSymbol is
288+
{ TypeKind: TypeKind.Array
289+
or TypeKind.Class
290+
or TypeKind.Struct
291+
or TypeKind.Enum
292+
})
286293
{
287-
if (!AnalyzerHelper.IsAssignableToLocal(context.Compilation, methodParameterTypeSymbol, valueExpressionString, constantValue, actualValueTypeSymbol.ToString()))
294+
var actualValueTypeString = actualValueTypeSymbol.ToString();
295+
296+
var typeTypeSymbol = context.Compilation.GetTypeByMetadataName("System.Type");
297+
298+
if (methodParameterTypeSymbol.Equals(typeTypeSymbol, SymbolEqualityComparer.Default))
299+
{
300+
if (!actualValueTypeSymbol.Equals(typeTypeSymbol, SymbolEqualityComparer.Default))
301+
{
302+
ReportValueTypeMustBeImplicitlyConvertibleDiagnostic(
303+
valueExpressionSyntax.GetLocation(),
304+
valueExpressionString,
305+
expectedValueTypeString,
306+
actualValueTypeString
307+
);
308+
}
309+
310+
continue;
311+
}
312+
313+
string? valueTypeContainingNamespace = null;
314+
315+
if (actualValueTypeSymbol.TypeKind == TypeKind.Enum && !actualValueTypeSymbol.ContainingNamespace.IsGlobalNamespace)
316+
{
317+
valueTypeContainingNamespace = actualValueTypeSymbol.ContainingNamespace.ToString();
318+
}
319+
320+
if (actualValueTypeSymbol is IArrayTypeSymbol actualValueArrayTypeSymbol)
321+
{
322+
if (methodParameterTypeSymbol is IArrayTypeSymbol expectedValueArrayTypeSymbol && expectedValueArrayTypeSymbol.ElementType.Equals(typeTypeSymbol, SymbolEqualityComparer.Default))
323+
{
324+
if (!actualValueArrayTypeSymbol.ElementType.Equals(typeTypeSymbol, SymbolEqualityComparer.Default))
325+
{
326+
ReportValueTypeMustBeImplicitlyConvertibleDiagnostic(
327+
valueExpressionSyntax.GetLocation(),
328+
valueExpressionString,
329+
expectedValueTypeString,
330+
actualValueTypeString
331+
);
332+
}
333+
334+
continue;
335+
}
336+
337+
if (actualValueArrayTypeSymbol.ElementType.TypeKind == TypeKind.Enum)
338+
{
339+
if (!actualValueArrayTypeSymbol.ElementType.ContainingNamespace.IsGlobalNamespace)
340+
{
341+
valueTypeContainingNamespace = actualValueArrayTypeSymbol.ElementType.ContainingNamespace.ToString();
342+
}
343+
}
344+
else if (actualValueArrayTypeSymbol.ElementType.TypeKind is TypeKind.Struct)
345+
{
346+
if (actualValueArrayTypeSymbol.ElementType.NullableAnnotation == NullableAnnotation.Annotated)
347+
{
348+
continue;
349+
}
350+
}
351+
else if (actualValueArrayTypeSymbol.ElementType.TypeKind is not TypeKind.Class)
352+
{
353+
continue;
354+
}
355+
}
356+
357+
if (!AnalyzerHelper.IsAssignableToLocal(context.Compilation,
358+
(context.FilterTree.Options as CSharpParseOptions)!.LanguageVersion,
359+
valueTypeContainingNamespace,
360+
methodParameterTypeSymbol,
361+
valueExpressionString,
362+
constantValue,
363+
actualValueTypeString))
288364
{
289365
ReportValueTypeMustBeImplicitlyConvertibleDiagnostic(
290366
valueExpressionSyntax.GetLocation(),
291-
valueExpressionSyntax.ToString(),
367+
valueExpressionString,
292368
methodParameterTypeSymbol.ToString(),
293-
actualValueTypeSymbol.ToString()
369+
actualValueTypeString
294370
);
295371
}
296372
}
297-
else
373+
else if (constantValue is { HasValue: true, Value: null })
298374
{
299-
if (constantValue is { HasValue: true, Value: null }
300-
&& !AnalyzerHelper.IsAssignableToLocal(context.Compilation, methodParameterTypeSymbol, valueExpressionString, constantValue, null))
375+
if (!AnalyzerHelper.IsAssignableToField(context.Compilation,
376+
(context.FilterTree.Options as CSharpParseOptions)!.LanguageVersion,
377+
null,
378+
methodParameterTypeSymbol,
379+
valueExpressionString,
380+
constantValue,
381+
null))
301382
{
302383
ReportValueTypeMustBeImplicitlyConvertibleDiagnostic(
303384
valueExpressionSyntax.GetLocation(),
304385
valueExpressionString,
305-
methodParameterTypeSymbol.ToString(),
386+
expectedValueTypeString,
306387
"null"
307388
);
308389
}

src/BenchmarkDotNet.Analyzers/Attributes/ParamsAttributeAnalyzer.cs

Lines changed: 88 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -231,27 +231,109 @@ void ReportIfNotImplicitlyConvertibleValueTypeDiagnostic(ExpressionSyntax valueE
231231

232232
var valueExpressionString = valueExpressionSyntax.ToString();
233233

234+
var expectedValueTypeString = expectedValueTypeSymbol.ToString();
234235
var actualValueTypeSymbol = context.SemanticModel.GetTypeInfo(valueExpressionSyntax).Type;
235-
if (actualValueTypeSymbol != null && actualValueTypeSymbol.TypeKind != TypeKind.Error)
236+
237+
if (actualValueTypeSymbol is
238+
{ TypeKind: TypeKind.Array
239+
or TypeKind.Class
240+
or TypeKind.Struct
241+
or TypeKind.Enum
242+
})
236243
{
237-
if (!AnalyzerHelper.IsAssignableToField(context.Compilation, expectedValueTypeSymbol, valueExpressionString, constantValue, actualValueTypeSymbol.ToString()))
244+
var actualValueTypeString = actualValueTypeSymbol.ToString();
245+
246+
var typeTypeSymbol = context.Compilation.GetTypeByMetadataName("System.Type");
247+
248+
if (expectedValueTypeSymbol.Equals(typeTypeSymbol, SymbolEqualityComparer.Default))
249+
{
250+
if (!actualValueTypeSymbol.Equals(typeTypeSymbol, SymbolEqualityComparer.Default))
251+
{
252+
ReportValueTypeMustBeImplicitlyConvertibleDiagnostic(
253+
valueExpressionSyntax.GetLocation(),
254+
valueExpressionString,
255+
expectedValueTypeString,
256+
actualValueTypeString
257+
);
258+
}
259+
260+
return;
261+
}
262+
263+
string? valueTypeContainingNamespace = null;
264+
265+
if (actualValueTypeSymbol.TypeKind == TypeKind.Enum && !actualValueTypeSymbol.ContainingNamespace.IsGlobalNamespace)
266+
{
267+
valueTypeContainingNamespace = actualValueTypeSymbol.ContainingNamespace.ToString();
268+
}
269+
270+
if (actualValueTypeSymbol is IArrayTypeSymbol actualValueArrayTypeSymbol)
271+
{
272+
if (expectedValueTypeSymbol is IArrayTypeSymbol expectedValueArrayTypeSymbol && expectedValueArrayTypeSymbol.ElementType.Equals(typeTypeSymbol, SymbolEqualityComparer.Default))
273+
{
274+
if (!actualValueArrayTypeSymbol.ElementType.Equals(typeTypeSymbol, SymbolEqualityComparer.Default))
275+
{
276+
ReportValueTypeMustBeImplicitlyConvertibleDiagnostic(
277+
valueExpressionSyntax.GetLocation(),
278+
valueExpressionString,
279+
expectedValueTypeString,
280+
actualValueTypeString
281+
);
282+
}
283+
284+
return;
285+
}
286+
287+
if (actualValueArrayTypeSymbol.ElementType.TypeKind == TypeKind.Enum)
288+
{
289+
if (!actualValueArrayTypeSymbol.ElementType.ContainingNamespace.IsGlobalNamespace)
290+
{
291+
valueTypeContainingNamespace = actualValueArrayTypeSymbol.ElementType.ContainingNamespace.ToString();
292+
}
293+
}
294+
else if (actualValueArrayTypeSymbol.ElementType.TypeKind is TypeKind.Struct)
295+
{
296+
if (actualValueArrayTypeSymbol.ElementType.NullableAnnotation == NullableAnnotation.Annotated)
297+
{
298+
return;
299+
}
300+
}
301+
else if (actualValueArrayTypeSymbol.ElementType.TypeKind is not TypeKind.Class)
302+
{
303+
return;
304+
}
305+
}
306+
307+
if (!AnalyzerHelper.IsAssignableToField(context.Compilation,
308+
(context.FilterTree.Options as CSharpParseOptions)!.LanguageVersion,
309+
valueTypeContainingNamespace,
310+
expectedValueTypeSymbol,
311+
valueExpressionString,
312+
constantValue,
313+
actualValueTypeString))
238314
{
239315
ReportValueTypeMustBeImplicitlyConvertibleDiagnostic(
240316
valueExpressionSyntax.GetLocation(),
241317
valueExpressionString,
242-
fieldOrPropertyTypeSyntax.ToString(),
243-
actualValueTypeSymbol.ToString()
318+
expectedValueTypeString,
319+
actualValueTypeString
244320
);
245321
}
246322
}
247323
else if (constantValue is { HasValue: true, Value: null })
248324
{
249-
if (!AnalyzerHelper.IsAssignableToField(context.Compilation, expectedValueTypeSymbol, valueExpressionString, constantValue, null))
325+
if (!AnalyzerHelper.IsAssignableToField(context.Compilation,
326+
(context.FilterTree.Options as CSharpParseOptions)!.LanguageVersion,
327+
null,
328+
expectedValueTypeSymbol,
329+
valueExpressionString,
330+
constantValue,
331+
null))
250332
{
251333
ReportValueTypeMustBeImplicitlyConvertibleDiagnostic(
252334
valueExpressionSyntax.GetLocation(),
253335
valueExpressionString,
254-
fieldOrPropertyTypeSyntax.ToString(),
336+
expectedValueTypeString,
255337
"null"
256338
);
257339
}

src/BenchmarkDotNet.Analyzers/BenchmarkDotNet.Analyzers.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<NoWarn>$(NoWarn);CS1591</NoWarn>
1010
</PropertyGroup>
1111
<ItemGroup>
12-
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
12+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="5.0.0-2.final" />
1313
</ItemGroup>
1414
<ItemGroup>
1515
<AdditionalFiles Include="AnalyzerReleases.Shipped.md" />

0 commit comments

Comments
 (0)