Skip to content
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
181 changes: 180 additions & 1 deletion docs/generators/visitor-generator.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Visitor Pattern Generator

The Visitor Pattern Generator automatically generates fluent, type-safe visitor infrastructure for class hierarchies marked with the `[GenerateVisitor]` attribute. This eliminates boilerplate code and provides modern C# ergonomics including async/await support, ValueTask, and generic type inference.
The Visitor Pattern Generator automatically generates fluent, type-safe visitor infrastructure for hierarchies marked with the `[GenerateVisitor]` attribute. It supports class, interface, struct, and record hierarchies, eliminating boilerplate code and providing modern C# ergonomics including async/await support, ValueTask, and generic type inference.

## Overview

Expand Down Expand Up @@ -118,6 +118,129 @@ var asyncLogger = new AstNodeAsyncActionVisitorBuilder()
await myExpression.AcceptAsync(asyncLogger);
```

## Supported Hierarchy Types

The visitor generator supports multiple types of hierarchies, providing flexibility in design:

### Class-Based Hierarchies

Traditional class inheritance hierarchies are fully supported:

```csharp
[GenerateVisitor]
public abstract partial class Animal
{
}

public partial class Dog : Animal
{
public string Breed { get; init; }
}

public partial class Cat : Animal
{
public bool IsIndoor { get; init; }
}
```

### Interface-Based Hierarchies

Hierarchies based on interfaces work seamlessly:

```csharp
[GenerateVisitor]
public partial interface IShape
{
}

public partial class Circle : IShape
{
public double Radius { get; init; }
}

public partial class Rectangle : IShape
{
public double Width { get; init; }
public double Height { get; init; }
}

public partial class Triangle : IShape
{
public double Base { get; init; }
public double Height { get; init; }
}
```

**Note:** For interface base types, the generated visitor interface name is intelligently derived. `IShape` generates `IShapeVisitor` (not `IIShapeVisitor`).

### Struct-Based Hierarchies

Value types can implement visitable interfaces for allocation-free visitor patterns:

```csharp
[GenerateVisitor]
public partial interface IValue
{
}

public partial struct IntValue : IValue
{
public int Value { get; init; }
}

public partial struct DoubleValue : IValue
{
public double Value { get; init; }
}

// No boxing occurs during visitation
var visitor = new IValueVisitorBuilder<string>()
.When<IntValue>(i => $"Int:{i.Value}")
.When<DoubleValue>(d => $"Double:{d.Value:F2}")
.Build();

var intVal = new IntValue { Value = 42 };
var result = intVal.Accept(visitor); // "Int:42"
```

### Record Types

Records are also supported:

```csharp
[GenerateVisitor]
public abstract partial record Message;

public partial record TextMessage(string Content) : Message;
public partial record ImageMessage(byte[] Data, string Format) : Message;
```

### Mixed Hierarchies

You can mix interfaces, classes, and structs in complex hierarchies:

```csharp
[GenerateVisitor]
public partial interface INode
{
}

public abstract partial class Expression : INode
{
}

public partial class Literal : Expression
{
public object Value { get; init; }
}

public partial struct Position : INode
{
public int Line { get; init; }
public int Column { get; init; }
}
```

## Attribute Options

The `[GenerateVisitor]` attribute supports several options:
Expand Down Expand Up @@ -391,6 +514,62 @@ var validator = new DocumentVisitorBuilder<ValidationResult>()
.Build();
```

## Diagnostics

The generator provides helpful diagnostics to catch common issues:

### PKVIS001: No concrete types found

**Severity:** Warning

This warning appears when the generator cannot find any concrete types implementing or deriving from the marked base type.

```csharp
[GenerateVisitor]
public partial interface IEmptyHierarchy { }

// Warning PKVIS001: No concrete types implementing or deriving from 'IEmptyHierarchy' were found
```

**Solutions:**
- Add concrete types that implement the interface or derive from the class
- Set `AutoDiscoverDerivedTypes = false` if you're building types manually

### PKVIS002: Type must be partial

**Severity:** Error

The base type (class, struct, or interface) must be declared as `partial` to allow Accept method generation.

```csharp
[GenerateVisitor]
public class NonPartialBase { } // Error!

// Fix:
[GenerateVisitor]
public partial class PartialBase { } // Correct
```

**Solution:** Add the `partial` keyword to the type declaration.

### PKVIS004: Derived type must be partial

**Severity:** Error

All derived types must be `partial` to allow Accept method generation.

```csharp
[GenerateVisitor]
public partial class Base { }

public class Derived : Base { } // Error!

// Fix:
public partial class Derived : Base { } // Correct
```

**Solution:** Add the `partial` keyword to all derived types in the hierarchy.

## Troubleshooting

### "No handler registered for type X"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,25 @@ namespace PatternKit.Generators.Visitors;
/// <item>Fluent builder APIs for composing visitors</item>
/// </list>
/// <example>
/// <para>Class-based hierarchy:</para>
/// <code>
/// [GenerateVisitor]
/// public partial class AstNode { }
///
/// public partial class Expression : AstNode { }
/// public partial class Statement : AstNode { }
/// </code>
/// <para>Interface-based hierarchy:</para>
/// <code>
/// [GenerateVisitor]
/// public partial interface IShape { }
///
/// public partial class Circle : IShape { }
/// public partial class Rectangle : IShape { }
/// </code>
/// </example>
/// </remarks>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
public sealed class GenerateVisitorAttribute : Attribute
{
/// <summary>
Expand Down
3 changes: 3 additions & 0 deletions src/PatternKit.Generators/AnalyzerReleases.Unshipped.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,6 @@ PKMEM003 | PatternKit.Generators.Memento | Warning | Unsafe reference capture
PKMEM004 | PatternKit.Generators.Memento | Error | Clone strategy requested but mechanism missing
PKMEM005 | PatternKit.Generators.Memento | Error | Record restore generation failed
PKMEM006 | PatternKit.Generators.Memento | Info | Init-only or readonly restrictions prevent in-place restore
PKVIS001 | PatternKit.Generators.Visitor | Warning | No concrete types found for visitor generation
PKVIS002 | PatternKit.Generators.Visitor | Error | Type must be partial for Accept method generation
PKVIS004 | PatternKit.Generators.Visitor | Error | Derived type must be partial for Accept method generation
Loading
Loading