2024-09-16 14:53:20 +00:00
|
|
|
|
namespace BizHawk.Analyzers;
|
|
|
|
|
|
|
|
|
|
using System.Collections.Immutable;
|
|
|
|
|
|
|
|
|
|
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
|
|
|
|
public sealed class AmbiguousMoneyToFloatConversionAnalyzer : DiagnosticAnalyzer
|
|
|
|
|
{
|
|
|
|
|
private static readonly DiagnosticDescriptor DiagAmbiguousMoneyToFloatConversion = new(
|
|
|
|
|
id: "BHI1105",
|
|
|
|
|
title: "Use unambiguous decimal<=>float/double conversion methods",
|
|
|
|
|
messageFormat: "use {0} for checked conversion, or {1} for unchecked",
|
|
|
|
|
category: "Usage",
|
|
|
|
|
defaultSeverity: DiagnosticSeverity.Warning,
|
|
|
|
|
isEnabledByDefault: true);
|
|
|
|
|
|
|
|
|
|
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(DiagAmbiguousMoneyToFloatConversion);
|
|
|
|
|
|
|
|
|
|
public override void Initialize(AnalysisContext context)
|
|
|
|
|
{
|
|
|
|
|
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
|
|
|
|
context.EnableConcurrentExecution();
|
|
|
|
|
context.RegisterCompilationStartAction(initContext =>
|
|
|
|
|
{
|
|
|
|
|
initContext.RegisterOperationAction(oac =>
|
|
|
|
|
{
|
|
|
|
|
var conversionOp = (IConversionOperation) oac.Operation;
|
2025-01-31 07:52:20 +00:00
|
|
|
|
var typeOutput = conversionOp.Type?.SpecialType ?? SpecialType.None;
|
|
|
|
|
var typeInput = conversionOp.Operand.Type?.SpecialType ?? SpecialType.None;
|
2024-09-16 14:53:20 +00:00
|
|
|
|
bool isToDecimal;
|
|
|
|
|
bool isDoublePrecision;
|
2025-01-31 07:52:20 +00:00
|
|
|
|
if (typeOutput is SpecialType.System_Decimal)
|
2024-09-16 14:53:20 +00:00
|
|
|
|
{
|
2025-01-31 07:52:20 +00:00
|
|
|
|
if (typeInput is SpecialType.System_Double) isDoublePrecision = true;
|
|
|
|
|
else if (typeInput is SpecialType.System_Single) isDoublePrecision = false;
|
2024-09-16 14:53:20 +00:00
|
|
|
|
else return;
|
|
|
|
|
isToDecimal = true;
|
|
|
|
|
}
|
2025-01-31 07:52:20 +00:00
|
|
|
|
else if (typeInput is SpecialType.System_Decimal)
|
2024-09-16 14:53:20 +00:00
|
|
|
|
{
|
2025-01-31 07:52:20 +00:00
|
|
|
|
if (typeOutput is SpecialType.System_Double) isDoublePrecision = true;
|
|
|
|
|
else if (typeOutput is SpecialType.System_Single) isDoublePrecision = false;
|
2024-09-16 14:53:20 +00:00
|
|
|
|
else return;
|
|
|
|
|
isToDecimal = false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
var conversionSyn = conversionOp.Syntax;
|
|
|
|
|
//TODO check the suggested methods are accessible (i.e. BizHawk.Common is referenced)
|
|
|
|
|
oac.ReportDiagnostic(Diagnostic.Create(
|
|
|
|
|
DiagAmbiguousMoneyToFloatConversion,
|
|
|
|
|
(conversionSyn.Parent?.Kind() is SyntaxKind.CheckedExpression or SyntaxKind.UncheckedExpression
|
|
|
|
|
? conversionSyn.Parent
|
|
|
|
|
: conversionSyn).GetLocation(),
|
|
|
|
|
conversionOp.IsChecked ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning,
|
|
|
|
|
additionalLocations: null,
|
|
|
|
|
properties: null,
|
|
|
|
|
messageArgs: isToDecimal
|
|
|
|
|
? [
|
|
|
|
|
$"new decimal({(isDoublePrecision ? "double" : "float")})", // "checked"
|
|
|
|
|
"static NumberExtensions.ConvertToMoneyTruncated", // "unchecked"
|
|
|
|
|
]
|
|
|
|
|
: [
|
|
|
|
|
$"decimal.{(isDoublePrecision ? "ConvertToF64" : "ConvertToF32")} ext. (from NumberExtensions)", // "checked"
|
|
|
|
|
$"static Decimal.{(isDoublePrecision ? "ToDouble" : "ToSingle")}", // "unchecked"
|
|
|
|
|
]));
|
|
|
|
|
},
|
|
|
|
|
OperationKind.Conversion);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|