Files
CliFx/CliFx.Analyzers/ParameterMustHaveValidConverterAnalyzer.cs
2023-08-22 21:20:04 +03:00

68 lines
2.3 KiB
C#

using System.Linq;
using CliFx.Analyzers.ObjectModel;
using CliFx.Analyzers.Utils.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ParameterMustHaveValidConverterAnalyzer : AnalyzerBase
{
public ParameterMustHaveValidConverterAnalyzer()
: base(
$"Parameter converters must derive from `{SymbolNames.CliFxBindingConverterClass}`",
$"Converter specified for this parameter must derive from a compatible `{SymbolNames.CliFxBindingConverterClass}`."
) { }
private void Analyze(
SyntaxNodeAnalysisContext context,
PropertyDeclarationSyntax propertyDeclaration,
IPropertySymbol property
)
{
var parameter = CommandParameterSymbol.TryResolve(property);
if (parameter is null)
return;
if (parameter.ConverterType is null)
return;
var converterValueType = parameter.ConverterType
.GetBaseTypes()
.FirstOrDefault(
t => t.ConstructedFrom.DisplayNameMatches(SymbolNames.CliFxBindingConverterClass)
)
?.TypeArguments.FirstOrDefault();
// Value returned by the converter must be assignable to the property type
var isCompatible =
converterValueType is not null
&& (
parameter.IsScalar()
// Scalar
? context.Compilation.IsAssignable(converterValueType, property.Type)
// Non-scalar (assume we can handle all IEnumerable types for simplicity)
: property.Type.TryGetEnumerableUnderlyingType() is { } enumerableUnderlyingType
&& context.Compilation.IsAssignable(
converterValueType,
enumerableUnderlyingType
)
);
if (!isCompatible)
{
context.ReportDiagnostic(
CreateDiagnostic(propertyDeclaration.Identifier.GetLocation())
);
}
}
public override void Initialize(AnalysisContext context)
{
base.Initialize(context);
context.HandlePropertyDeclaration(Analyze);
}
}