Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle override, virtual and abstract property modifiers #31

Merged
merged 1 commit into from
Dec 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ namespace MainLib
private static readonly PropertyChangedEventArgs _virtualPropChangedEventArgs = new PropertyChangedEventArgs(nameof(VirtualProp));
private static readonly PropertyChangedEventArgs _derivedPropChangedEventArgs = new PropertyChangedEventArgs(nameof(DerivedProp));

public partial string VirtualProp
public override partial string VirtualProp
{
get => field;
set
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public partial class TestClass
private static readonly PropertyChangedEventArgs _virtualPropChangedEventArgs = new PropertyChangedEventArgs(nameof(VirtualProp));
private static readonly PropertyChangedEventArgs _nonVirtualPropChangedEventArgs = new PropertyChangedEventArgs(nameof(NonVirtualProp));

public partial string VirtualProp
public virtual partial string VirtualProp
{
get => field;
set
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public partial class Container
{
private static readonly PropertyChangedEventArgs _virtualPropChangedEventArgs = new PropertyChangedEventArgs(nameof(VirtualProp));

public partial string VirtualProp
public virtual partial string VirtualProp
{
get => field;
set
Expand Down Expand Up @@ -81,7 +81,7 @@ public partial class Container
private static readonly PropertyChangedEventArgs _virtualPropChangedEventArgs = new PropertyChangedEventArgs(nameof(VirtualProp));
private static readonly PropertyChangedEventArgs _regularPropChangedEventArgs = new PropertyChangedEventArgs(nameof(RegularProp));

public partial string VirtualProp
public override partial string VirtualProp
{
get => field;
set
Expand Down
79 changes: 58 additions & 21 deletions ReactiveGenerator/ReactiveGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,26 @@
[Generator]
public class ReactiveGenerator : IIncrementalGenerator
{
private record PropertyInfo(
private record PropertyInfo(
IPropertySymbol Property,
bool HasReactiveAttribute,
bool HasIgnoreAttribute,
bool HasImplementation);
bool HasImplementation)
{
public string GetPropertyModifiers()
{
var modifiers = new List<string>();

if (Property.IsOverride)
modifiers.Add("override");
else if (Property.IsVirtual)
modifiers.Add("virtual");
else if (Property.IsAbstract)
modifiers.Add("abstract");

return string.Join(" ", modifiers);
}
}

public void Initialize(IncrementalGeneratorInitializationContext context)
{
Expand Down Expand Up @@ -275,7 +290,7 @@
// Add types that need processing
foreach (var type in GetAllTypesInCompilation(compilation))
{
if (IsTypeMarkedReactive(type) || HasAnyReactiveProperties(type, propertyGroups))

Check warning on line 293 in ReactiveGenerator/ReactiveGenerator.cs

View workflow job for this annotation

GitHub Actions / Build ubuntu-latest

Argument of type 'Dictionary<ISymbol?, List<ReactiveGenerator.PropertyInfo>>' cannot be used for parameter 'propertyGroups' of type 'Dictionary<ISymbol, List<ReactiveGenerator.PropertyInfo>>' in 'bool ReactiveGenerator.HasAnyReactiveProperties(INamedTypeSymbol type, Dictionary<ISymbol, List<PropertyInfo>> propertyGroups)' due to differences in the nullability of reference types.

Check warning on line 293 in ReactiveGenerator/ReactiveGenerator.cs

View workflow job for this annotation

GitHub Actions / Build windows-latest

Argument of type 'Dictionary<ISymbol?, List<ReactiveGenerator.PropertyInfo>>' cannot be used for parameter 'propertyGroups' of type 'Dictionary<ISymbol, List<ReactiveGenerator.PropertyInfo>>' in 'bool ReactiveGenerator.HasAnyReactiveProperties(INamedTypeSymbol type, Dictionary<ISymbol, List<PropertyInfo>> propertyGroups)' due to differences in the nullability of reference types.

Check warning on line 293 in ReactiveGenerator/ReactiveGenerator.cs

View workflow job for this annotation

GitHub Actions / Build macos-latest

Argument of type 'Dictionary<ISymbol?, List<ReactiveGenerator.PropertyInfo>>' cannot be used for parameter 'propertyGroups' of type 'Dictionary<ISymbol, List<ReactiveGenerator.PropertyInfo>>' in 'bool ReactiveGenerator.HasAnyReactiveProperties(INamedTypeSymbol type, Dictionary<ISymbol, List<PropertyInfo>> propertyGroups)' due to differences in the nullability of reference types.
{
allTypes.Add(type);
}
Expand Down Expand Up @@ -342,33 +357,37 @@
processedTypes.Add(type);
}
}

private static bool HasINPCImplementation(Compilation compilation, INamedTypeSymbol typeSymbol,
HashSet<INamedTypeSymbol> processedTypes)

private static bool HasINPCImplementation(Compilation compilation, INamedTypeSymbol typeSymbol, HashSet<INamedTypeSymbol> processedTypes)
{
var inpcType = compilation.GetTypeByMetadataName("System.ComponentModel.INotifyPropertyChanged");
if (inpcType is null)
return false;

// First check if current type implements INPC directly
if (typeSymbol.AllInterfaces.Contains(inpcType, SymbolEqualityComparer.Default))
return true;

// Check if current type is in processedTypes (will have INPC implemented)
if (processedTypes.Contains(typeSymbol))
// First check if current type implements INPC directly or is in processedTypes
if (typeSymbol.AllInterfaces.Contains(inpcType, SymbolEqualityComparer.Default) ||
processedTypes.Contains(typeSymbol))
return true;

// Check base types recursively
var current = typeSymbol.BaseType;
while (current is not null)
{
// Check if base type implements INPC directly
if (current.AllInterfaces.Contains(inpcType, SymbolEqualityComparer.Default))
return true;

// Check if base type is in processedTypes
if (processedTypes.Contains(current))
return true;
// If base type is in same assembly
if (current.ContainingAssembly == typeSymbol.ContainingAssembly)
{
// Check for INPC implementation or presence in processedTypes
if (current.AllInterfaces.Contains(inpcType, SymbolEqualityComparer.Default) ||
processedTypes.Contains(current))
return true;
}
// If base type is in different assembly
else
{
// For external types, only check for actual INPC implementation
if (current.AllInterfaces.Contains(inpcType, SymbolEqualityComparer.Default))
return true;
}

current = current.BaseType;
}
Expand Down Expand Up @@ -767,7 +786,15 @@
var getterAccessibility = GetAccessorAccessibility(property.GetMethod);
var setterAccessibility = GetAccessorAccessibility(property.SetMethod);

sb.AppendLine($"{indent}{propertyAccessibility} partial {propertyType} {propertyName}");
var propInfo = new PropertyInfo(property, false, false, false);
var modifiers = propInfo.GetPropertyModifiers();

var declarationModifiers = new List<string> { propertyAccessibility };
if (!string.IsNullOrEmpty(modifiers))
declarationModifiers.Add(modifiers);
declarationModifiers.Add("partial");

sb.AppendLine($"{indent}{string.Join(" ", declarationModifiers)} {propertyType} {propertyName}");
sb.AppendLine($"{indent}{{");

if (isReactiveObject)
Expand Down Expand Up @@ -817,8 +844,18 @@
var propertyType = GetPropertyTypeWithNullability(property);
var getterAccessibility = GetAccessorAccessibility(property.GetMethod);
var setterAccessibility = GetAccessorAccessibility(property.SetMethod);

sb.AppendLine($"{indent}{propertyAccessibility} partial {propertyType} {propertyName}");

// Create PropertyInfo to get modifiers
var propInfo = new PropertyInfo(property, false, false, false);
var modifiers = propInfo.GetPropertyModifiers();

// Combine modifiers with accessibility and partial
var declarationModifiers = new List<string> { propertyAccessibility };
if (!string.IsNullOrEmpty(modifiers))
declarationModifiers.Add(modifiers);
declarationModifiers.Add("partial");

sb.AppendLine($"{indent}{string.Join(" ", declarationModifiers)} {propertyType} {propertyName}");
sb.AppendLine($"{indent}{{");

if (isReactiveObject)
Expand Down
Loading