Skip to content

chore(analyzers): fold the analyzer submodule into the main repo - #152

Merged
VPDPersonal merged 16 commits into
mainfrom
build/merge-analyzers-submodule
Aug 3, 2026
Merged

chore(analyzers): fold the analyzer submodule into the main repo#152
VPDPersonal merged 16 commits into
mainfrom
build/merge-analyzers-submodule

Conversation

@VPDPersonal

Copy link
Copy Markdown
Owner

Summary

  • 📦 Folds Aspid.FastTools.Analyzers from a git submodule into this repository via git subtree, keeping all 13 analyzer commits in the history. The submodule had no independent life — IsPackable=false, no own CI, no external consumers, and every rule it ships validates this package's own attributes — so each feature cost two PRs plus a gitlink bump.
  • 🔧 Adds Aspid.FastTools.Analyzers/Directory.Build.targets, mirroring the generator: the build now deploys the DLL into the Unity package, so the rebuild-analyzers-on-change.sh hook and the build-analyzer skill no longer copy it by hand.
  • ⚙️ Drops submodules: true from tests.yml (the matrix path is unchanged) and removes the analyzer's LICENSE, a byte-identical copy of the root one.
  • 📝 Updates CLAUDE.md and both QA checklists to match.

Notes for review

  • ⚠️ The copy is Release-only, deliberately. The generator's equivalent target has no such condition, but the analyzer's Tests and Sample projects reference the analyzer project, and CI runs dotnet test without -c — i.e. in Debug. Without the guard, every test run would overwrite the shipped Release DLL with a Debug build.
  • ✅ Verified locally: dotnet build -c Release deploys the DLL, a Debug build leaves it untouched, and dotnet test "Aspid.FastTools.Analyzers" (the exact CI command) passes 47/47.
  • 🧹 Two manual steps remain after merge, neither possible from a branch: drop the stale local submodule wiring (git config --remove-section submodule.Aspid.FastTools.Analyzers and rm -rf .git/modules/Aspid.FastTools.Analyzers), and archive VPDPersonal/Aspid.FastTools.Analyzers on GitHub.
  • 📐 Not touched here: the analyzer keeps the triple-nested Aspid.FastTools.Analyzers/Aspid.FastTools.Analyzers/Aspid.FastTools.Analyzers/ layout inherited from the standalone repo, where the generator has one level less. Flattening it would touch the .sln, the hook, the skill and CI — better as its own change.
🇷🇺 Описание на русском

Итог

  • 📦 Аналайзер Aspid.FastTools.Analyzers переезжает из git-сабмодуля в этот репозиторий через git subtree — все 13 его коммитов сохранены в истории. Самостоятельной жизни у сабмодуля не было: IsPackable=false, своего CI нет, внешних потребителей нет, а все правила валидируют атрибуты этого же пакета — поэтому каждая фича стоила двух PR плюс бампа гитлинка.
  • 🔧 Добавлен Aspid.FastTools.Analyzers/Directory.Build.targets по образцу генератора: сборка сама кладёт DLL в Unity-пакет, так что хук rebuild-analyzers-on-change.sh и скилл build-analyzer больше не копируют её руками.
  • ⚙️ Из tests.yml убран submodules: true (путь матрицы не менялся), удалён LICENSE аналайзера — байт-в-байт копия корневого.
  • 📝 CLAUDE.md и оба QA-чеклиста приведены в соответствие.

На что посмотреть

  • ⚠️ Копирование намеренно только для Release. У генератора в аналогичном таргете такого условия нет, но проекты Tests и Sample ссылаются на проект аналайзера, а CI гоняет dotnet test без -c, то есть в Debug. Без условия каждый прогон тестов затирал бы отгружаемую Release-DLL Debug-сборкой.
  • ✅ Проверено локально: dotnet build -c Release доставляет DLL, Debug-сборка её не трогает, dotnet test "Aspid.FastTools.Analyzers" (точная команда CI) — 47/47.
  • 🧹 После мержа остаются два ручных шага, из ветки их не сделать: убрать локальные остатки сабмодуля (git config --remove-section submodule.Aspid.FastTools.Analyzers и rm -rf .git/modules/Aspid.FastTools.Analyzers) и заархивировать VPDPersonal/Aspid.FastTools.Analyzers на GitHub.
  • 📐 Здесь не трогали: у аналайзера осталась тройная вложенность Aspid.FastTools.Analyzers/Aspid.FastTools.Analyzers/Aspid.FastTools.Analyzers/, доставшаяся от отдельного репо, тогда как у генератора уровнем меньше. Выпрямление затронет .sln, хук, скилл и CI — лучше отдельным изменением.

Vladislav Panin and others added 16 commits May 20, 2024 09:26
- Rename project, namespace and assembly UnityFastToolsAnalyzers -> Aspid.FastTools.Analyzers.
- Replace the dormant GetComponent/UnityHandler rules (UFT0001-UFT0005) with [TypeSelector] usage rules AFT0001-AFT0003 targeting Aspid.FastTools.Types.
- AFT0001 flags [TypeSelector] on a field that is neither a string nor a [SerializeReference] managed reference; AFT0002 flags Allow on a managed reference; AFT0003 flags a base type that shares no concrete type with the field.
- Add analyzer release tracking, xUnit tests, and a sample.

BREAKING CHANGE: the assembly and root namespace are renamed to Aspid.FastTools.Analyzers and every diagnostic ID changed from UFT* to AFT*.
A sealed class admits no further subtypes, so pairing it with an
interface it does not implement provably yields an empty candidate set.
#1)

Unity declares the class as SerializeReference : Attribute (no suffix),
so AFT0001 fired on every [SerializeReference] [TypeSelector] field.
The test stub carried the suffixed name and masked the mismatch; it now
mirrors Unity's real declaration, turning the existing managed-reference
tests into regression coverage.
* feat(analyzers): add AFT0004 and AFT0005 diagnostics for [TypeSelector]

AFT0004 (Error): reports [TypeSelector]+[SerializeReference] on a field
whose element type derives from UnityEngine.Object — Unity silently
skips serialization of managed references to Object-derived types.

AFT0005 (Warning): reports when no concrete, non-UnityEngine.Object class
visible in the compilation implements the effective base type (typeof(...)
args or the field's element type). Severity is Warning because
implementations may live in downstream assemblies. Uses a
RegisterCompilationStartAction + per-compilation Lazy<> candidate list
for performance. AFT0005 is suppressed for bases already covered by AFT0003.

Also fixes a pre-existing bug in Sample.cs where SerializeReferenceAttribute
(conventional suffix) was used instead of SerializeReference (Unity's
actual no-suffix name), causing AFT0001 to fire on valid sample code.

* fix(analyzers): AFT0005 checks intersection of typeof base and field element type

HasVisibleCandidate now requires a candidate to be assignable to both
the typeof(...) base type AND the field's declared element type, matching
the actual picker behaviour which intersects the two constraints. When
fieldElementType is System.Object the second condition is skipped.

Updated InterfaceBaseType_NoDiagnostic: impl must now derive from Base
too (MarkerImpl : Base, IMarker). Added positive test confirming AFT0005
fires when the only IMarker impl does not derive from Base.

* perf(analyzers): scan only assemblies that can contain an AFT0005 candidate

The AFT0005 check walked Compilation.GlobalNamespace, materialising every
symbol of every referenced assembly (all of the BCL and UnityEngine) — up
to minutes of csc time per Unity assembly using [TypeSelector] with
[SerializeReference]. The candidate search now walks only assemblies that
declare or reference both constraint types' assemblies, stops at the first
match, and memoises results per (base, field element) pair. In the package
sample assembly this cuts csc from ~187s to ~0.3s.

* feat: accept SerializableType as a valid [TypeSelector] field shape

- AFT0001 no longer fires on a SerializableType / SerializableType<T> field: it is a
  type-name shape like string, so Allow and base types are meaningful.
- Add AspidFastTools.ClassesDescription with the SerializableType full names; matched by
  the field's original definition (non-generic and open-generic forms).
- Cover the new shape with 4 tests (string/generic/list + explicit Allow), 28/28 pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R78LEJNg3b9sgDUZhYPdHz

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
InterfaceWithOneConcreteImpl_NoAFT0005 was byte-identical to
ManagedReferenceField_NoDiagnostic — same source snippet, same
no-diagnostics assertion.

Co-authored-by: Claude <noreply@anthropic.com>
- AFT0006 (error): an identifier string that matches no member of the declaring type — the drawer resolves string arguments member-first, so a typo silently drops the constraint
- AFT0007 (error): the referenced member is not an instance field/property of type Type, Type[], string, or string[]
- AFT0008 (warning): a non-identifier string that is not a plausible assembly-qualified type name (empty comma part, invalid identifier segments)
- 17 new analyzer tests; stub gains the string constructors

Co-Authored-By: Claude <noreply@anthropic.com>
- AFT0006 (error): an identifier string that matches no member of the declaring type — the drawer resolves string arguments member-first, so a typo silently drops the constraint
- AFT0007 (error): the referenced member is not an instance field/property of type Type, Type[], string, or string[]
- AFT0008 (warning): a non-identifier string that is not a plausible assembly-qualified type name (empty comma part, invalid identifier segments)
- 17 new analyzer tests; stub gains the string constructors

Co-authored-by: Claude <noreply@anthropic.com>
- the TypeSelector drawer now reads SerializableType / SerializableType<T> members as base-type sources, so IsSuitableConstraintSource must accept them too — otherwise a valid [TypeSelector(nameof(_serializableTypeMember))] wrongly reported AFT0007
- widen the AFT0007 message accordingly; add three analyzer tests (SerializableType field / generic / array members)

Co-Authored-By: Claude <noreply@anthropic.com>
…zabletype-members

Accept SerializableType members in AFT0007
Removes the gitlink and the now-empty .gitmodules so the analyzer sources
can be grafted in via git subtree in the following commit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017gqDPXf96xiQ8ehPf3foan
…799d85879af26fc7a'

git-subtree-dir: Aspid.FastTools.Analyzers
git-subtree-mainline: d3e77b5
git-subtree-split: f35c545
The analyzer was a submodule but had no independent life: IsPackable=false,
no own CI, no external consumers, and every rule it ships validates this
package's own attributes. Each feature therefore cost two PRs plus a gitlink
bump — five of the thirteen superproject commits touching it were pure
pointer bookkeeping.

Its history is grafted in via git subtree (previous commit), and the analyzer
now matches how the generator is already handled:

- Directory.Build.targets deploys the DLL into the Unity package on build, so
  the hook and the build-analyzer skill no longer copy it by hand. The copy is
  Release-only because the Tests and Sample projects reference the analyzer —
  CI runs `dotnet test` in Debug and would otherwise overwrite the shipped
  Release DLL.
- tests.yml no longer checks out submodules; the matrix path is unchanged.
- Drops the analyzer's LICENSE, a byte-identical copy of the root one.

Verified: `dotnet build -c Release` deploys the DLL, `dotnet test` (Debug)
leaves it untouched, and all 47 analyzer tests pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017gqDPXf96xiQ8ehPf3foan
@VPDPersonal VPDPersonal added type: chore Maintenance, version bumps, tooling status: work-in-progress Draft / not ready for review dependencies Dependency or submodule update area: claude Claude Code automation (.claude/ skills, agents, hooks) area: ci CI / GitHub Actions workflows area: docs Repository documentation (README, CHANGELOG, docs/) labels Aug 3, 2026
@VPDPersonal
VPDPersonal marked this pull request as ready for review August 3, 2026 18:23
@VPDPersonal VPDPersonal added status: needs-review Ready for review and removed status: work-in-progress Draft / not ready for review labels Aug 3, 2026
@VPDPersonal
VPDPersonal merged commit b8acbcd into main Aug 3, 2026
5 checks passed
@VPDPersonal
VPDPersonal deleted the build/merge-analyzers-submodule branch August 3, 2026 18:26
@github-actions github-actions Bot removed the status: needs-review Ready for review label Aug 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: ci CI / GitHub Actions workflows area: claude Claude Code automation (.claude/ skills, agents, hooks) area: docs Repository documentation (README, CHANGELOG, docs/) dependencies Dependency or submodule update type: chore Maintenance, version bumps, tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant