> For the complete documentation index, see [llms.txt](https://docs.parsue.io/aceland-unity-packages/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.parsue.io/aceland-unity-packages/core-packages/injection/tooling.md).

# Tooling

AceLand Injection ships two pieces of build-time tooling that make the framework fast and safe:

* A **Source Generator** that emits zero-reflection injection plans at compile time.
* **Project Settings** that control build-time validation of your bindings.

***

### Source Generator

The generator is a Roslyn `IIncrementalGenerator`. During compilation it inspects your injectable types and emits a per-type **injector plan** — code that constructs and injects the type directly, with no runtime reflection.

#### Why it exists

| Without a plan                                                               | With a generated plan                                                 |
| ---------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| The runtime reflects over constructors, fields and methods on first resolve. | The runtime calls generated code that already knows every dependency. |
| Slower first resolve; allocates.                                             | Fast, allocation-free resolution.                                     |
| Works everywhere.                                                            | Works everywhere, and is IL2CPP / AOT friendly.                       |

{% hint style="info" %}
The generated plans are **an optimisation, not a requirement**. If no plan exists for a type, the runtime transparently falls back to reflection — behaviour is identical either way. This means you can start coding immediately and let the generator catch up.
{% endhint %}

#### What gets a plan

The generator produces a plan for any type that:

* has an `[Inject]` constructor / field / property / method, **or**
* is marked `[Injectable]` (useful for plain single-constructor types with no `[Inject]`), **or**
* is named by an assembly attribute `[assembly: GenerateInjectorFor(typeof(T))]`.

```csharp
using AceLand.Injection;

// 1. Implicit — has [Inject] members, so a plan is generated automatically.
public sealed class Gameplay
{
    [Inject] public Gameplay(IScoreService score) { }
}

// 2. Explicit — no [Inject] but you still want a compiled plan.
[Injectable]
public sealed class PlainService
{
    public PlainService(ILogger logger) { }
}
```

```csharp
// 3. From an assembly you can annotate but whose type you'd rather leave clean:
[assembly: GenerateInjectorFor(typeof(ThirdPartyThing))]
```

#### Mark private members' type `partial`

When a type injects **private or protected** members (fields, properties or methods), the generated plan must live *inside* that type to reach them. The generator emits the plan as a `partial` declaration, so **the type must also be declared `partial`** — otherwise there is nowhere for the generated code to attach and the runtime falls back to reflection.

{% tabs %}
{% tab title="✅ partial — gets a plan" %}

```csharp
using AceLand.Injection;
using UnityEngine;

// 'partial' lets the generator reach the private field → zero-reflection plan.
public partial class Hud : MonoBehaviour
{
    [Inject] private IScoreService _score;
}
```

{% endtab %}

{% tab title="⚠️ no partial — falls back" %}

```csharp
using AceLand.Injection;
using UnityEngine;

// Still works, but injection goes through reflection.
// The compiler raises ACEDI005 asking you to add 'partial'.
public class Hud : MonoBehaviour
{
    [Inject] private IScoreService _score;
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Only **private / protected** members need `partial`. If every `[Inject]` target is **public** (or the injection is constructor-only), the generator can reach it from an external file and no `partial` is required.
{% endhint %}

#### Diagnostics (ACEDI)

The generator reports the following diagnostics. Errors stop the build; warnings and infos let it continue (falling back to reflection where noted).

| ID           | Severity | Meaning                                                                                     | Fix                                             |
| ------------ | -------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------- |
| **ACEDI000** | Warning  | The generator threw and fell back to reflection.                                            | Report it; injection still works.               |
| **ACEDI001** | Error    | An `[Inject]` member is `readonly` / `const`.                                               | Make it writable.                               |
| **ACEDI002** | Error    | An `[Inject]` property has no setter.                                                       | Add a setter.                                   |
| **ACEDI003** | Error    | A component attribute (`[Self]`/`[Parent]`/...) is on a non-`Component` type.               | Use it only on a `MonoBehaviour` / `Component`. |
| **ACEDI004** | Error    | More than one `[Inject]` constructor.                                                       | Keep a single `[Inject]` constructor.           |
| **ACEDI005** | Warning  | Private/protected members are injected but the type is not `partial` → reflection fallback. | Mark the type `partial`.                        |
| **ACEDI006** | Info     | A generic type cannot be code-generated → reflection fallback.                              | None needed; generics always use reflection.    |

{% hint style="info" %}
**ACEDI005** is a **warning** so it surfaces in the Unity Console. If you prefer it as a silent suggestion (or want to turn it off entirely), override its severity from an `.editorconfig` at your project root:

```ini
[*.cs]
dotnet_diagnostic.ACEDI005.severity = suggestion   # or: none
```

{% endhint %}

#### Opting out

```csharp
using AceLand.Injection;

// Always use reflection for this type; never generate a plan.
[NoInjector]
public sealed class RarelyResolved { }
```

#### Version notes

The generator targets **Roslyn 4.1** so it runs on Unity 2022.3 and every newer LTS. It uses `CreateSyntaxProvider` (rather than `ForAttributeWithMetadataName`) for the same compatibility reason. Generated code is written under `Temp/GeneratedCode` — open it from the menu (see Editor Tools) when you want to inspect exactly what was emitted.

{% hint style="warning" %}
The generator ships as a Roslyn analyzer DLL. If plans are not being produced, its import settings may be wrong — run **Tools ▸ AceLand ▸ Injection ▸ Fix Analyzer Import Settings** (see Editor Tools).
{% endhint %}

***

### Project Settings

Open **Edit ▸ Project Settings ▸ AceLand Packages ▸ Injection**. These settings drive the build-time validator, which resolves every scene / prefab against the container and reports bindings that cannot be satisfied. Values are stored in `ProjectSettings/AceLandInjectionValidation.asset`.

#### Build Gate

| Setting                 | Default | Meaning                                                            |
| ----------------------- | ------- | ------------------------------------------------------------------ |
| **Validate On Build**   | `true`  | Run validation in `IPreprocessBuildWithReport` before every build. |
| **Fail Build On Error** | `true`  | Throw `BuildFailedException` when unresolvable bindings are found. |

#### Scope

| Setting                    | Default | Meaning                                                                                                                                                       |
| -------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Include All Scenes**     | `false` | ON — every `.unity` under `Assets/` (catches Addressables and additive scenes). OFF — enabled Build Settings entries plus anything matching *Always Include*. |
| **Validate Prefabs**       | `true`  | Check prefab assets against `DI.Global`. Their runtime scope is unknown, so only globally-registered contracts are verified.                                  |
| **Component Miss = Error** | `true`  | A missing `[Self]`/`[Parent]`/`[Child]` target fails the build. Turn off to treat as a warning.                                                               |

#### Filters

| Setting            | Default                              | Meaning                                                                                            |
| ------------------ | ------------------------------------ | -------------------------------------------------------------------------------------------------- |
| **Ignore Paths**   | `/Tests/`, `/Sandbox/`, `/_Scratch/` | Skip scenes whose path contains any of these (case-insensitive).                                   |
| **Always Include** | `/Addressables/`                     | Validate these even when *Include All Scenes* is off — use for Addressables / async-loaded scenes. |

The settings panel shows a live count of how many scenes will be validated given the current scope and filters.

***

### Best Practices

* Leave **Validate On Build** and **Fail Build On Error** on for CI — it turns missing bindings into a red build instead of a runtime `NullReferenceException`.
* Add editor-only sandbox / test scene folders to **Ignore Paths** so experiments don't block builds.
* Add Addressables scene folders to **Always Include** so async-loaded scenes are still checked.
* Let the Source Generator do its job; only reach for `[NoInjector]` when you have a specific reason.
* Mark plain (no-`[Inject]`) types you resolve frequently with `[Injectable]` to skip reflection.
* Declare types that inject **private / protected** members as `partial` so they get a compiled plan instead of the reflection fallback — the **ACEDI005** warning points these out.
