81 lines
3.1 KiB
C#
81 lines
3.1 KiB
C#
namespace BizHawk.Analyzers;
|
|
|
|
using System.Collections.Immutable;
|
|
|
|
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CSharp;
|
|
using Microsoft.CodeAnalysis.Diagnostics;
|
|
using Microsoft.CodeAnalysis.Operations;
|
|
|
|
[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 =>
|
|
{
|
|
var decimalSym = initContext.Compilation.GetTypeByMetadataName("System.Decimal")!;
|
|
var doubleSym = initContext.Compilation.GetTypeByMetadataName("System.Double")!;
|
|
var floatSym = initContext.Compilation.GetTypeByMetadataName("System.Single")!;
|
|
initContext.RegisterOperationAction(oac =>
|
|
{
|
|
var conversionOp = (IConversionOperation) oac.Operation;
|
|
var typeOutput = conversionOp.Type;
|
|
var typeInput = conversionOp.Operand.Type;
|
|
bool isToDecimal;
|
|
bool isDoublePrecision;
|
|
if (decimalSym.Matches(typeOutput))
|
|
{
|
|
if (doubleSym.Matches(typeInput)) isDoublePrecision = true;
|
|
else if (floatSym.Matches(typeInput)) isDoublePrecision = false;
|
|
else return;
|
|
isToDecimal = true;
|
|
}
|
|
else if (decimalSym.Matches(typeInput))
|
|
{
|
|
if (doubleSym.Matches(typeOutput)) isDoublePrecision = true;
|
|
else if (floatSym.Matches(typeOutput)) isDoublePrecision = false;
|
|
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);
|
|
});
|
|
}
|
|
}
|