Skip to content

Commit 8cccd86

Browse files
committed
Use a dummy syntax tree to test whether types are implicitly convertible
1 parent bde31fd commit 8cccd86

File tree

3 files changed

+19
-111
lines changed

3 files changed

+19
-111
lines changed

src/BenchmarkDotNet.Analyzers/AnalyzerHelper.cs

Lines changed: 10 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
namespace BenchmarkDotNet.Analyzers
22
{
33
using Microsoft.CodeAnalysis;
4+
using Microsoft.CodeAnalysis.CSharp;
45
using Microsoft.CodeAnalysis.CSharp.Syntax;
56

6-
using System;
77
using System.Collections.Immutable;
88

99
internal static class AnalyzerHelper
@@ -92,89 +92,19 @@ public static string NormalizeTypeName(INamedTypeSymbol namedTypeSymbol)
9292
return typeName;
9393
}
9494

95-
public static bool ValueFitsInType(object value, ITypeSymbol targetType)
95+
public static bool IsConstantAssignableToType(Compilation compilation, ITypeSymbol targetType, string valueExpression)
9696
{
97+
var code = $$"""
98+
file class Internal {
99+
{{targetType}} x = {{valueExpression}};
100+
}
101+
""";
97102

103+
var syntaxTree = CSharpSyntaxTree.ParseText(code);
98104

99-
try
100-
{
101-
switch (targetType.SpecialType)
102-
{
103-
case SpecialType.System_Byte:
104-
var byteVal = Convert.ToInt64(value);
105-
106-
return byteVal is >= byte.MinValue and <= byte.MaxValue;
107-
108-
case SpecialType.System_SByte:
109-
var sbyteVal = Convert.ToInt64(value);
110-
111-
return sbyteVal is >= sbyte.MinValue and <= sbyte.MaxValue;
112-
113-
case SpecialType.System_Int16:
114-
var int16Val = Convert.ToInt64(value);
115-
116-
return int16Val is >= short.MinValue and <= short.MaxValue;
117-
118-
case SpecialType.System_UInt16:
119-
var uint16Val = Convert.ToInt64(value);
120-
121-
return uint16Val is >= ushort.MinValue and <= ushort.MaxValue;
122-
123-
case SpecialType.System_Int32:
124-
var int32Val = Convert.ToInt64(value);
125-
126-
return int32Val is >= int.MinValue and <= int.MaxValue;
127-
128-
case SpecialType.System_UInt32:
129-
var uint32Val = Convert.ToInt64(value);
130-
131-
return uint32Val is >= uint.MinValue and <= uint.MaxValue;
132-
133-
case SpecialType.System_Int64:
134-
{
135-
_ = Convert.ToInt64(value);
136-
}
137-
138-
return true;
105+
var diagnostics = compilation.AddSyntaxTrees(syntaxTree).GetSemanticModel(syntaxTree).GetMethodBodyDiagnostics();
139106

140-
case SpecialType.System_UInt64:
141-
var val = Convert.ToInt64(value);
142-
143-
return val >= 0;
144-
145-
case SpecialType.System_Single:
146-
if (value is double)
147-
{
148-
return false;
149-
}
150-
151-
var floatVal = Convert.ToSingle(value);
152-
153-
return !float.IsInfinity(floatVal);
154-
155-
case SpecialType.System_Double:
156-
var doubleVal = Convert.ToDouble(value);
157-
158-
return !double.IsInfinity(doubleVal);
159-
160-
case SpecialType.System_Decimal:
161-
if (value is double or float)
162-
{
163-
return false;
164-
}
165-
166-
_ = Convert.ToDecimal(value);
167-
168-
return true;
169-
170-
default:
171-
return false;
172-
}
173-
}
174-
catch (Exception)
175-
{
176-
return false;
177-
}
107+
return diagnostics.Length == 0;
178108
}
179109
}
180110
}

src/BenchmarkDotNet.Analyzers/Attributes/ArgumentsAttributeAnalyzer.cs

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -277,24 +277,13 @@ void ReportIfNotImplicitlyConvertibleValueTypeDiagnostic(Func<int, ExpressionSyn
277277
continue;
278278
}
279279

280-
var actualValueTypeSymbol = context.SemanticModel.GetTypeInfo(valueExpressionSyntaxFunc(i)).Type;
280+
var valueExpressionString = valueExpressionSyntax.ToString();
281+
282+
var actualValueTypeSymbol = context.SemanticModel.GetTypeInfo(valueExpressionSyntax).Type;
281283
if (actualValueTypeSymbol != null && actualValueTypeSymbol.TypeKind != TypeKind.Error)
282284
{
283-
var conversionSummary = context.Compilation.ClassifyConversion(actualValueTypeSymbol, methodParameterTypeSymbol);
284-
if (!conversionSummary.IsImplicit)
285+
if (!AnalyzerHelper.IsConstantAssignableToType(context.Compilation, methodParameterTypeSymbol, valueExpressionString))
285286
{
286-
if (conversionSummary is { IsExplicit: true, IsEnumeration: false })
287-
{
288-
var constantValue = context.SemanticModel.GetConstantValue(valueExpressionSyntax is CastExpressionSyntax castExpressionSyntax ? castExpressionSyntax.Expression : valueExpressionSyntax);
289-
if (constantValue is { HasValue: true, Value: not null })
290-
{
291-
if (AnalyzerHelper.ValueFitsInType(constantValue.Value, methodParameterTypeSymbol))
292-
{
293-
return;
294-
}
295-
}
296-
}
297-
298287
ReportValueTypeMustBeImplicitlyConvertibleDiagnostic(valueExpressionSyntax.GetLocation(),
299288
valueExpressionSyntax.ToString(),
300289
methodParameterTypeSymbol.ToString(),

src/BenchmarkDotNet.Analyzers/Attributes/ParamsAttributeAnalyzer.cs

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -248,34 +248,23 @@ private static void AnalyzeFieldOrPropertyTypeSyntax(SyntaxNodeAnalysisContext c
248248

249249
void ReportIfNotImplicitlyConvertibleValueTypeDiagnostic(ExpressionSyntax valueExpressionSyntax)
250250
{
251+
var valueExpressionString = valueExpressionSyntax.ToString();
252+
251253
var actualValueTypeSymbol = context.SemanticModel.GetTypeInfo(valueExpressionSyntax).Type;
252254
if (actualValueTypeSymbol != null && actualValueTypeSymbol.TypeKind != TypeKind.Error)
253255
{
254-
var conversionSummary = context.Compilation.ClassifyConversion(actualValueTypeSymbol, expectedValueTypeSymbol);
255-
if (!conversionSummary.IsImplicit)
256+
if (!AnalyzerHelper.IsConstantAssignableToType(context.Compilation, expectedValueTypeSymbol, valueExpressionString))
256257
{
257-
if (conversionSummary is { IsExplicit: true, IsEnumeration: false })
258-
{
259-
var constantValue = context.SemanticModel.GetConstantValue(valueExpressionSyntax is CastExpressionSyntax castExpressionSyntax ? castExpressionSyntax.Expression : valueExpressionSyntax);
260-
if (constantValue is { HasValue: true, Value: not null })
261-
{
262-
if (AnalyzerHelper.ValueFitsInType(constantValue.Value, expectedValueTypeSymbol))
263-
{
264-
return;
265-
}
266-
}
267-
}
268-
269258
ReportValueTypeMustBeImplicitlyConvertibleDiagnostic(valueExpressionSyntax.GetLocation(),
270-
valueExpressionSyntax.ToString(),
259+
valueExpressionString,
271260
fieldOrPropertyTypeSyntax.ToString(),
272261
actualValueTypeSymbol.ToString());
273262
}
274263
}
275264
else
276265
{
277266
ReportValueTypeMustBeImplicitlyConvertibleDiagnostic(valueExpressionSyntax.GetLocation(),
278-
valueExpressionSyntax.ToString(),
267+
valueExpressionString,
279268
fieldOrPropertyTypeSyntax.ToString());
280269
}
281270

0 commit comments

Comments
 (0)