Skip to content

Commit

Permalink
fix list binding error
Browse files Browse the repository at this point in the history
  • Loading branch information
egokb committed Jan 8, 2025
1 parent d336d98 commit 100ea91
Show file tree
Hide file tree
Showing 6 changed files with 329 additions and 70 deletions.
32 changes: 16 additions & 16 deletions FUIAnalyzer/AttributeBinding/AttributeBindingAnalyzer.Property.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public partial class AttributeBindingAnalyzer
ConverterNotIConverterRule,
PropertyToTargetWithoutConverterRule,
PropertyToTargetWithConverterRule,
TargetMustBeNameOfRule
TargetMustBeNameOfRule,
};

/// <summary>
Expand Down Expand Up @@ -102,10 +102,16 @@ void AnalyzePropertyAttribute(SyntaxNodeAnalysisContext context, PropertyDeclara
return;
}

//如果属性和目标值类型都是可绑定列表,不进行下面的判断
if (propertyType.IsObservableList() && targetPropertyType.IsObservableList())
{
return;
}

if (converterInfo == default)
{
//如果没有转换器且属性类型无法转换成目标值类型
if (!propertyType.InheritsFromOrEquals(targetPropertyType))
if (!propertyType.Extends(targetPropertyType))
{
var diagnostic = Diagnostic.Create(PropertyToTargetWithoutConverterRule, attribute.GetLocation(), propertyType, targetPropertyType);
context.ReportDiagnostic(diagnostic);
Expand All @@ -114,8 +120,8 @@ void AnalyzePropertyAttribute(SyntaxNodeAnalysisContext context, PropertyDeclara
else
{
//如果有转换器,但是无法将属性类型转换成转换器源类型,或无法将转换器目标类型转换成绑定目标值类型
if (!propertyType.InheritsFromOrEquals(converterInfo.sourceType)
|| !converterInfo.targetType.InheritsFromOrEquals(targetPropertyType))
if (!propertyType.Extends(converterInfo.sourceType)
|| !converterInfo.targetType.Extends(targetPropertyType))
{
var diagnostic = Diagnostic.Create(PropertyToTargetWithConverterRule, attribute.GetLocation(), propertyType, converterInfo.sourceType, converterInfo.targetType, targetPropertyType);
context.ReportDiagnostic(diagnostic);
Expand Down Expand Up @@ -153,7 +159,7 @@ void AnalyzePropertyAttribute(SyntaxNodeAnalysisContext context, PropertyDeclara

//判断目标类型是否是IElement
var targetTypeInfo = context.SemanticModel.GetTypeInfo(memberAccess.Expression);
if (targetTypeInfo.Type.AllInterfaces.FirstOrDefault((item) => item.ToString().StartsWith("FUI.IElement")) == null)
if(!targetTypeInfo.Type.Extends(typeof(FUI.IElement)))
{
var diagnostic = Diagnostic.Create(TargetNotElementRule, memberAccess.Expression.GetLocation(), targetTypeInfo.Type);
context.ReportDiagnostic(diagnostic);
Expand All @@ -175,17 +181,16 @@ INamedTypeSymbol GetTargetPropertyType(SyntaxNodeAnalysisContext context, Attrib
return default;
}

var @interface = targetPropertyTypeInfo.Value.Type.AllInterfaces.FirstOrDefault(item => item.IsGenericType && item.ToString().StartsWith("FUI.Bindable.IBindableProperty"));
if (@interface == null)
//如果不是则报错
if(!targetPropertyTypeInfo.Value.Type.IsBindableProperty(out var targetValueType))
{
var diagnostic = Diagnostic.Create(TargetPropertyNotBindableRule, memberAccess.Name.GetLocation(), targetPropertyTypeInfo.Value.Type);
context.ReportDiagnostic(diagnostic);
return null;
}

//获取目标成员值类型
var targetValueType = @interface.TypeArguments[0] as INamedTypeSymbol;
return targetValueType;
return targetValueType as INamedTypeSymbol;
}

/// <summary>
Expand All @@ -207,20 +212,15 @@ INamedTypeSymbol GetTargetPropertyType(SyntaxNodeAnalysisContext context, Attrib
var typeInfo = context.SemanticModel.GetTypeInfo(typeofExpression.Type);

//判断是否继承自IValueConverter<>
var interfaces = typeInfo.Type.AllInterfaces.FirstOrDefault(item => item.IsGenericType && item.ToString().StartsWith("FUI.IValueConverter"));

//如果不继承 则报错
if (interfaces == null)
if(!typeInfo.Type.IsValueConverter(out var sourceType, out var targetType))
{
var diagnostic = Diagnostic.Create(ConverterNotIConverterRule, typeofExpression.Type.GetLocation(), typeInfo.Type);
context.ReportDiagnostic(diagnostic);
return default;
}

//返回其sourcesType和targetType
var sourceType = interfaces.TypeArguments[0] as INamedTypeSymbol;
var targetType = interfaces.TypeArguments[1] as INamedTypeSymbol;
return (sourceType, targetType);
return (sourceType as INamedTypeSymbol, targetType as INamedTypeSymbol);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ void AnalyzeClassAttribute(SyntaxNodeAnalysisContext context, ClassDeclarationSy
return;
}

if (!namedType.InheritsFrom(typeof(FUI.Bindable.ObservableObject)))
if (!namedType.IsObservableObject())
{
var diagnostic = Diagnostic.Create(BindingObjectNotObservableObjectRule, attribute.GetLocation(), namedType.ToString());
context.ReportDiagnostic(diagnostic);
Expand Down
16 changes: 0 additions & 16 deletions FUIAnalyzer/AttributeBinding/FUIStubs.cs

This file was deleted.

42 changes: 42 additions & 0 deletions FUIAnalyzer/FUIStubs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;

namespace FUI
{
public class BindingAttribute : Attribute { }

public class CommandAttribute : Attribute { }

public interface IValueConverter { }

public interface IValueConverter<T1, T2> { }

public interface ISynchronizeProperties
{
void Synchronize();
}

public interface IElement { }
}

namespace FUI.BindingDescriptor
{
public class ContextDescriptor { }

public class ContextDescriptor<TViewModel> : ContextDescriptor where TViewModel : FUI.Bindable.ObservableObject { }

public class CommandBindingDescriptor { }

public class PropertyBindingDescriptor { }
}

namespace FUI.Bindable
{
public class ObservableObject { }
public class BindableProperty<T> { }
public interface IBindableProperty<T> { }
public interface INotifyPropertyChanged { }
public interface INotifyCollectionChanged { }
public interface IReadOnlyObservableList<out T> : IReadOnlyList<T>, INotifyCollectionChanged { }
public class CommandTemplate<T> { }
}
158 changes: 158 additions & 0 deletions FUIAnalyzer/FUITypeSymbolExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
using Microsoft.CodeAnalysis;

using System.Collections.Generic;

namespace FUIAnalyzer
{
internal static class FUITypeSymbolExtensions
{
/// <summary>
/// 判断一个类型是否是可观察对象
/// </summary>
public static bool IsObservableObject(this ITypeSymbol type)
{
return type.Extends(typeof(FUI.Bindable.INotifyPropertyChanged));
}

/// <summary>
/// 判断一个类型是否是可观察列表
/// </summary>
public static bool IsObservableList(this ITypeSymbol type)
{
return type.Extends(typeof(FUI.Bindable.INotifyCollectionChanged));
}

/// <summary>
/// 判断一个类型是否是可观察列表 并获取元素类型
/// </summary>
/// <param name="type">源类型</param>
/// <param name="elementType">列表元素类型</param>
/// <returns></returns>
public static bool IsObservableList(this ITypeSymbol type, out ITypeSymbol elementType)
{
elementType = null;
if (!type.IsObservableList())
{
return false;
}

var listType = typeof(FUI.Bindable.IReadOnlyObservableList<>).GetGenericTypeDefinition();
foreach (var interfaceType in type.AllInterfaces)
{
if (interfaceType.IsGenericType && interfaceType.ConstructedFrom.Matches(listType))
{
elementType = interfaceType.TypeArguments[0];
return true;
}
}

return false;
}

/// <summary>
/// 判断一个类型是否是可观察属性
/// </summary>
public static bool IsBindableProperty(this ITypeSymbol type, out ITypeSymbol propertyValueType)
{
propertyValueType = null;
var bindablePropertyType = typeof(FUI.Bindable.IBindableProperty<>).GetGenericTypeDefinition();
foreach (var interfaceType in type.AllInterfaces)
{
if (interfaceType.IsGenericType && interfaceType.ConstructedFrom.Matches(bindablePropertyType))
{
propertyValueType = interfaceType.TypeArguments[0];
return true;
}
}

return false;
}

/// <summary>
/// 判断一个类型是否是命令
/// </summary>
public static bool IsCommand(this ITypeSymbol type, out IReadOnlyList<ITypeSymbol> arguments)
{
var commandType = typeof(FUI.Bindable.CommandTemplate<>).GetGenericTypeDefinition();
arguments = null;
foreach (var t in type.GetBaseTypesAndThis())
{
if (!(t is INamedTypeSymbol named))
{
continue;
}

if (named.IsGenericType && named.ConstructedFrom.Matches(commandType))
{
var args = named.TypeArguments;
if (args.Length != 1)
{
return false;
}

var actionType = args[0] as INamedTypeSymbol;
if (actionType == null)
{
return false;
}

arguments = actionType.TypeArguments;
return true;
}
}
return false;
}

/// <summary>
/// 判断一个类型是否是值转换器
/// </summary>
/// <param name="type">类型</param>
/// <param name="valueType">值转换器 值类型</param>
/// <param name="targetType">值转换器 目标类型</param>
/// <returns></returns>
public static bool IsValueConverter(this ITypeSymbol type, out ITypeSymbol valueType, out ITypeSymbol targetType)
{
valueType = null;
targetType = null;

var valueConverterType = typeof(FUI.IValueConverter<,>).GetGenericTypeDefinition();

foreach (var interfaceType in type.AllInterfaces)
{
if (interfaceType.IsGenericType && interfaceType.ConstructedFrom.Matches(valueConverterType))
{
valueType = interfaceType.TypeArguments[0];
targetType = interfaceType.TypeArguments[1];
return true;
}
}

return false;
}

/// <summary>
/// 判断一个类型是否是绑定上下文描述器
/// </summary>
/// <param name="type">目标类型</param>
/// <param name="viewModelType">viewModel类型</param>
public static bool IsContextDescriptor(this ITypeSymbol type, out ITypeSymbol viewModelType)
{
viewModelType = null;
var descriptorType = typeof(FUI.BindingDescriptor.ContextDescriptor<>).GetGenericTypeDefinition();
foreach (var t in type.GetBaseTypesAndThis())
{
if (!(t is INamedTypeSymbol named))
{
continue;
}

if (named.IsGenericType && named.ConstructedFrom.Matches(descriptorType))
{
viewModelType = named.TypeArguments[0];
return true;
}
}
return false;
}
}
}
Loading

0 comments on commit 100ea91

Please sign in to comment.