Skip to content

Commit 5844f5c

Browse files
committed
docs: update CLAUDE.md for clarity and structure improvements
1 parent 1a6ac55 commit 5844f5c

File tree

1 file changed

+107
-216
lines changed

1 file changed

+107
-216
lines changed

CLAUDE.md

Lines changed: 107 additions & 216 deletions
Original file line numberDiff line numberDiff line change
@@ -1,256 +1,147 @@
1-
This document describes the development guidelines, project structure, and technical background for this repository.
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
24

35
## Overview
46

5-
This project is a C# source generator and analyzer suite that automatically generates Expression trees and DTO classes corresponding to IQueryable<T>.Select usages. It also provides code analyzers and fixes to improve LINQ query patterns and API design.
7+
Linqraft is a C# source generator and analyzer suite that automatically generates Expression trees and DTO classes for IQueryable<T>.Select operations. It enables writing LINQ queries with null-propagation operators (?.) in expression trees and auto-generates DTOs from anonymous types.
68

7-
The source generator analyzes the contents of `.SelectExpr` calls and generates the corresponding `Select` expressions and DTO classes.
9+
The project consists of:
10+
- **Source Generator**: Analyzes `.SelectExpr` calls and generates Select expressions and DTO classes
11+
- **Interceptors**: Replaces SelectExpr calls with generated expressions at compile time
12+
- **Roslyn Analyzers**: Detects patterns and suggests code improvements with automatic fixes
813

9-
Here is an example:
14+
## Build and Test Commands
1015

11-
```csharp
12-
public class SampleClass
13-
{
14-
public void GetSample(List<BaseClass> data)
15-
{
16-
var query = data.AsQueryable();
17-
// pattern 1: use anonymous type to specify selection
18-
// return type is anonymous type
19-
query.SelectExpr(x => new
20-
{
21-
x.Id,
22-
// you can use the null-conditional operator
23-
ChildDescription = x.Child?.Description,
24-
});
25-
26-
// pattern 2: use an explicit DTO class
27-
// return type is SampleDto (auto-generated)
28-
query.SelectExpr<SampleDto>(x => new
29-
{
30-
x.Id,
31-
// you can select child properties
32-
ChildNames = x.Children.Select(c => c.Name).ToList(),
33-
});
34-
35-
// pattern 3: use an already defined DTO class
36-
query.SelectExpr(x => new PredefinedDto
37-
{
38-
x.Name,
39-
x.Value,
40-
});
41-
}
42-
}
43-
44-
public class PredefinedDto
45-
{
46-
public string Name { get; set; }
47-
public int Value { get; set; }
48-
}
16+
### Clean build (required to avoid stale generator caches)
17+
```bash
18+
sh scripts/cleanup.sh
19+
dotnet build --no-incremental
4920
```
5021

51-
## Project structure
52-
53-
The repository is organized as follows (relevant folders under `src/`):
54-
55-
### `src/Linqraft/`
56-
The runtime library distributed as a NuGet package. Notable file:
57-
- `DummyExpression.cs`: an empty extension method that acts as a marker for the Source Generator to detect `SelectExpr` usages. It performs no runtime work and exists only to be recognized at compile time.
58-
59-
### `src/Linqraft.Core/`
60-
Common infrastructure and helper classes shared between analyzers and source generators. This project contains:
61-
62-
#### AnalyzerHelpers/
63-
Analyzer-specific helper classes and base infrastructure:
64-
- `BaseLinqraftAnalyzer.cs`: Abstract base class for all analyzers, eliminating boilerplate code
65-
- `CaptureHelper.cs`: Helper for detecting captured variables in lambda expressions
66-
- `ExpressionHelper.cs`: Helper for extracting property names and member access chains from expressions
67-
- `LinqMethodHelper.cs`: Helper for detecting LINQ method calls (Select, SelectMany, ToList)
68-
- `NullCheckHelper.cs`: Helper for null checking patterns
69-
- `SyntaxGenerationHelper.cs`: Helper for generating typed SelectExpr calls
70-
- `SyntaxHelper.cs`: Helper for syntax node analysis and manipulation
71-
- `UsingDirectiveHelper.cs`: Helper for managing using directives
72-
73-
#### RoslynHelpers/
74-
Roslyn semantic analysis helpers:
75-
- `RoslynTypeHelper.cs`: Type checking using Roslyn semantic analysis instead of string matching. Provides methods for nullable type checking, IQueryable/IEnumerable detection, anonymous type detection, and more.
76-
77-
#### SyntaxHelpers/
78-
Syntax manipulation and formatting helpers:
79-
- `NullConditionalHelper.cs`: Helper for null-conditional operator (?.) handling and null check pattern detection
80-
- `TriviaHelper.cs`: Helper for preserving whitespace, comments, and formatting. Includes cross-platform line ending detection (CRLF vs LF).
81-
82-
### `src/Linqraft.Analyzer/`
83-
Roslyn analyzers and code fix providers that detect code patterns and suggest improvements. This project contains:
84-
85-
#### Analyzers (7 total)
86-
All analyzers inherit from `BaseLinqraftAnalyzer`:
87-
- `AnonymousTypeToDtoAnalyzer.cs`: Detects anonymous types that can be converted to DTOs
88-
- `ApiControllerProducesResponseTypeAnalyzer.cs`: Detects API controllers missing ProducesResponseType attributes
89-
- `LocalVariableCaptureAnalyzer.cs`: Detects local variables that should be captured in SelectExpr
90-
- `SelectExprToTypedAnalyzer.cs`: Detects SelectExpr calls that can use explicit type parameters
91-
- `SelectToSelectExprAnonymousAnalyzer.cs`: Detects Select calls with anonymous types that should use SelectExpr
92-
- `SelectToSelectExprNamedAnalyzer.cs`: Detects Select calls with named types that should use SelectExpr
93-
- `TernaryNullCheckToConditionalAnalyzer.cs`: Detects ternary null checks that can be simplified to null-conditional operators
94-
95-
#### Code Fix Providers (7 total)
96-
Each analyzer has a corresponding code fix provider:
97-
- `AnonymousTypeToDtoCodeFixProvider.cs`
98-
- `ApiControllerProducesResponseTypeCodeFixProvider.cs`
99-
- `LocalVariableCaptureCodeFixProvider.cs`
100-
- `SelectExprToTypedCodeFixProvider.cs`
101-
- `SelectToSelectExprAnonymousCodeFixProvider.cs`
102-
- `SelectToSelectExprNamedCodeFixProvider.cs`
103-
- `TernaryNullCheckToConditionalCodeFixProvider.cs`
104-
105-
#### Utilities
106-
- `TernaryNullCheckSimplifier.cs`: Centralized logic for simplifying ternary null checks to null-conditional operators
107-
108-
### `src/Linqraft.SourceGenerator/`
109-
The Source Generator implementation that performs the actual code generation. Important files include:
110-
- `SelectExprGenerator.cs`: the generator entry point
111-
- `SelectExprGroups.cs`: grouping SelectExpr information (grouped per namespace)
112-
- `SelectExprInfo.cs`: holds information for each SelectExpr and provides the foundation for code generation
113-
- `SelectExprInfoAnonymous.cs`: handles anonymous-type SelectExpr information (pattern 1)
114-
- `SelectExprInfoExplicitDto.cs`: handles explicit DTO SelectExpr information (pattern 2)
115-
- `SelectExprInfoPredefinedDto.cs`: handles pre-existing DTO SelectExpr information (pattern 3)
116-
117-
### `tests/Linqraft.Tests/`
118-
The test project for source generators. Contains test cases exercising various scenarios and verifies generated output.
119-
120-
### `tests/Linqraft.Analyzer.Tests/`
121-
The test project for analyzers and code fix providers. Contains comprehensive tests for all 7 analyzers and their corresponding code fixes.
122-
123-
### `examples/Linqraft.Sample/`
124-
A sample project demonstrating usage examples.
125-
126-
### `docs/developments/`
127-
Development documentation and guides:
128-
- `refactoring-guide.md`: Comprehensive guide documenting the refactored codebase architecture, helper class organization, code quality standards, and migration guidelines. **Read this before making significant changes to analyzers or helper classes.**
129-
130-
## Technical background
22+
### Instant feedback build (runtime library only)
23+
```bash
24+
sh scripts/instant-build.sh
25+
```
13126

132-
This project consists of three main components:
27+
### Quick test (single framework)
28+
```bash
29+
sh scripts/clean-test.sh
30+
```
13331

134-
1. **C# Source Generator**: A compile-time code generation feature (available since C# 9). The generator inspects `SelectExpr` calls and emits expression trees and DTO classes.
32+
### Run specific test
33+
```bash
34+
dotnet test --filter "FullyQualifiedName~YourTestName" --no-build
35+
```
13536

136-
2. **Interceptor**: A technique used to intercept method calls and replace the `SelectExpr` call with the generated expression trees at runtime.
37+
### Inspect generated sources
38+
1. Run `sh scripts/instant-build.sh` to build the project
39+
2. Generated code appears in `tests/Linqraft.Tests/.generated/**/*.g.cs`
13740

138-
3. **Roslyn Analyzers**: Compile-time code analyzers that detect patterns and suggest improvements. Analyzers use the Roslyn API for semantic analysis and syntax tree manipulation.
41+
## Architecture
13942

140-
## Build and test
43+
### Three SelectExpr patterns
14144

142-
Always perform a clean build to avoid stale generator caches:
45+
The codebase handles three distinct patterns, each with its own SelectExprInfo implementation:
14346

144-
```bash
145-
dotnet clean
146-
dotnet build --no-incremental
147-
dotnet test --no-build
148-
```
47+
1. **Anonymous pattern** (`SelectExprInfoAnonymous.cs`): `query.SelectExpr(x => new { x.Id, x.Name })`
48+
- Returns anonymous type
49+
- No DTO generation needed
14950

150-
If you want to inspect the generated sources on disk, follow these steps:
51+
2. **Explicit DTO pattern** (`SelectExprInfoExplicitDto.cs`): `query.SelectExpr<Entity, EntityDto>(x => new { x.Id })`
52+
- Auto-generates EntityDto class from anonymous type structure
53+
- Type parameter specifies desired DTO name
15154

152-
1. Remove the `(test-project)/.generated` directory if it already exists.
153-
2. Enable `EmitCompilerGeneratedFiles` in `Linqraft.Tests.csproj`.
154-
3. The generated code will be emitted to `(test-project)/.generated/**/*.g.cs`.
55+
3. **Predefined DTO pattern** (`SelectExprInfoPredefinedDto.cs`): `query.SelectExpr(x => new PredefinedDto { x.Id })`
56+
- Uses existing DTO class
57+
- No generation, only expression tree creation
15558

156-
You can use the `./scripts/clean-test.sh` script as a shortcut.
59+
### Core components
15760

158-
## Development guidelines
61+
**Source Generator** (`src/Linqraft.SourceGenerator/`):
62+
- `SelectExprGenerator.cs`: Entry point, orchestrates generation
63+
- `SelectExprGroups.cs`: Groups SelectExpr calls by namespace
64+
- `SelectExprInfo.cs` and subclasses: Parse and hold information for each SelectExpr call
15965

160-
### Test-driven development recommended
66+
**Analyzer Infrastructure** (`src/Linqraft.Core/`):
67+
- `AnalyzerHelpers/`: Analyzer-specific helpers (BaseLinqraftAnalyzer, CaptureHelper, ExpressionHelper, etc.)
68+
- `RoslynHelpers/`: Roslyn semantic analysis helpers (RoslynTypeHelper for type checking)
69+
- `SyntaxHelpers/`: Syntax manipulation helpers (TriviaHelper for formatting, NullConditionalHelper)
16170

162-
- When adding new features, write tests first.
163-
- Verify the generated code in the test project to ensure it matches expectations.
164-
- Ensure all existing tests pass before committing changes.
165-
- For analyzers, add test cases to `tests/Linqraft.Analyzer.Tests/`.
166-
- For source generators, add test cases to `tests/Linqraft.Tests/`.
71+
**Analyzers** (`src/Linqraft.Analyzer/`):
72+
- 7 analyzers inheriting from `BaseLinqraftAnalyzer`
73+
- Each analyzer has a corresponding code fix provider
74+
- Examples: `SelectToSelectExprAnonymousAnalyzer`, `LocalVariableCaptureAnalyzer`, `TernaryNullCheckToConditionalAnalyzer`
16775

168-
### Source generator-specific considerations
76+
**Runtime Library** (`src/Linqraft/`):
77+
- `DummyExpression.cs`: Marker method for generator detection (do not edit)
16978

170-
- Cache issues: if changes to the generator are not reflected, run `dotnet clean`.
171-
- IDE restart: if generated code is not visible in Visual Studio or Rider, an IDE restart may be required.
172-
- Debugging: debugging source generators can be more involved than regular code. Use `EmitCompilerGeneratedFiles` to inspect emitted sources when necessary.
79+
### Key technical details
17380

174-
### Analyzer development guidelines
81+
**Null-propagation conversion**: The generator converts `x.Customer?.Name` to `x.Customer != null ? x.Customer.Name : null` in expression trees (EF Core/IQueryable compatible).
17582

176-
When developing or modifying analyzers:
83+
**Interceptors**: Uses C# 12 interceptor feature to replace SelectExpr calls with generated code at compile time, enabling zero-runtime-dependency.
17784

178-
1. **Inherit from BaseLinqraftAnalyzer**
179-
- All new analyzers should inherit from `BaseLinqraftAnalyzer`
180-
- Override the required abstract properties: `DiagnosticId`, `Title`, `MessageFormat`, `Description`, `Severity`, `Rule`
181-
- Define a public const `AnalyzerId` for use by code fix providers
85+
**Helper class organization**:
86+
- **RoslynTypeHelper**: Use for semantic type checking (never use string-based type matching)
87+
- **TriviaHelper**: Preserves formatting and detects cross-platform line endings
88+
- **BaseLinqraftAnalyzer**: Abstract base for all analyzers, reduces boilerplate
18289

183-
2. **Use Helper Classes**
184-
- **Always prefer semantic analysis over string matching**: Use `RoslynTypeHelper` instead of string-based type checking
185-
- **Use TriviaHelper for formatting**: Preserve whitespace and comments, and detect line endings for cross-platform compatibility
186-
- **Centralize common patterns**: If you write the same logic twice, extract it to a helper class
187-
- See `docs/developments/refactoring-guide.md` for detailed guidelines on when and how to create helper classes
90+
Please refer to README.md and docs/library/*.md for more details.
18891

189-
3. **Code Quality Standards**
190-
```csharp
191-
// Bad: string-based type checking
192-
if (typeName.EndsWith("?")) { }
92+
## Development Guidelines
19393

194-
// Good: semantic analysis
195-
if (RoslynTypeHelper.IsNullableType(typeSymbol)) { }
196-
```
94+
### Analyzer development
19795

198-
```csharp
199-
// Bad: hardcoded line endings
200-
var newNode = node.WithTrailingTrivia(SyntaxFactory.EndOfLine("\n"));
96+
1. **Inherit from BaseLinqraftAnalyzer** for all new analyzers
97+
2. **Use semantic analysis over string matching**: Always use `RoslynTypeHelper` instead of string-based type checks
98+
3. **Use TriviaHelper for formatting**: Preserve whitespace/comments and handle cross-platform line endings
99+
4. **Follow naming conventions**:
100+
- Analyzers: `*Analyzer.cs`
101+
- Code fixes: `*CodeFixProvider.cs`
102+
- Helpers: `*Helper.cs` in appropriate subfolder
201103

202-
// Good: cross-platform line ending detection
203-
var newNode = node.WithTrailingTrivia(TriviaHelper.EndOfLine(root));
204-
```
104+
Example:
105+
```csharp
106+
// Bad: string-based type checking
107+
if (typeName.EndsWith("?")) { }
205108

206-
4. **Follow Naming Conventions**
207-
- Analyzers: `*Analyzer.cs` inheriting from `BaseLinqraftAnalyzer`
208-
- Code Fix Providers: `*CodeFixProvider.cs`
209-
- Analyzer helpers: `*Helper.cs` in `Linqraft.Core/AnalyzerHelpers/`
210-
- Roslyn helpers: `Roslyn*Helper.cs` in `Linqraft.Core/RoslynHelpers/`
211-
- Syntax helpers: `*Helper.cs` in `Linqraft.Core/SyntaxHelpers/`
109+
// Good: semantic analysis
110+
if (RoslynTypeHelper.IsNullableType(typeSymbol)) { }
111+
```
212112

213-
5. **Performance Considerations**
214-
- Helper methods are called frequently during analysis
215-
- Keep helper methods lightweight and focused
216-
- Cache expensive operations when possible
217-
- Use `ISymbolEqualityComparer` for symbol comparisons
113+
### Source generator development
218114

219-
### Code editing guidelines
115+
- **Cache issues**: Run `sh scripts/cleanup.sh` if changes aren't reflected
116+
- **IDE issues**: Restart IDE if generated code isn't visible
117+
- **Always add/update tests** when modifying generators
220118

221-
- Do not edit `DummyExpression.cs` (it serves only as a marker).
222-
- When modifying the source generator, always add or update tests.
223-
- When modifying analyzers or code fix providers, always add or update tests.
224-
- Pay attention to the readability and performance of the generated code.
225-
- Document complex helper methods with XML comments.
226-
- Consult `docs/developments/refactoring-guide.md` for architecture guidelines and best practices.
119+
### Test-driven development
227120

228-
### Helper class organization
121+
- Write tests first when adding features
122+
- Verify generated code matches expectations
123+
- Ensure all existing tests pass before committing
124+
- Analyzer tests: `tests/Linqraft.Analyzer.Tests/`
125+
- Source generator tests: `tests/Linqraft.Tests/`
229126

230-
Helper classes are organized by purpose:
127+
## Project Structure
231128

232129
```
233-
src/Linqraft.Core/
234-
├── AnalyzerHelpers/ # Analyzer-specific helpers
235-
│ ├── BaseLinqraftAnalyzer.cs
236-
│ ├── CaptureHelper.cs
237-
│ ├── ExpressionHelper.cs
238-
│ ├── LinqMethodHelper.cs
239-
│ ├── NullCheckHelper.cs
240-
│ ├── SyntaxGenerationHelper.cs
241-
│ ├── SyntaxHelper.cs
242-
│ └── UsingDirectiveHelper.cs
243-
├── RoslynHelpers/ # Roslyn semantic analysis helpers
244-
│ └── RoslynTypeHelper.cs
245-
└── SyntaxHelpers/ # Syntax manipulation helpers
246-
├── NullConditionalHelper.cs
247-
└── TriviaHelper.cs
130+
src/
131+
├── Linqraft/ # Runtime library (NuGet package)
132+
├── Linqraft.Core/ # Shared helpers and infrastructure
133+
│ ├── AnalyzerHelpers/ # Analyzer-specific helpers
134+
│ ├── RoslynHelpers/ # Semantic analysis helpers
135+
│ └── SyntaxHelpers/ # Syntax manipulation helpers
136+
├── Linqraft.Analyzer/ # 7 analyzers + code fix providers
137+
└── Linqraft.SourceGenerator/ # Source generator implementation
138+
139+
tests/
140+
├── Linqraft.Tests/ # Source generator tests
141+
└── Linqraft.Analyzer.Tests/ # Analyzer and code fix tests
142+
143+
examples/
144+
├── Linqraft.Sample/ # Basic usage with EF Core
145+
├── Linqraft.MinimumSample/ # Minimal example
146+
└── Linqraft.ApiSample/ # API integration example
248147
```
249-
250-
**When to create a new helper class:**
251-
1. Same code appears in 3+ locations
252-
2. You're using string-based type checking (use RoslynTypeHelper instead)
253-
3. Repeated syntax patterns (use SyntaxHelper)
254-
4. Complex trivia handling (use TriviaHelper)
255-
256-
See `docs/developments/refactoring-guide.md` for detailed examples and migration guidelines.

0 commit comments

Comments
 (0)