com.wallstop-studios.dxmessaging 2.1.9 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/CHANGELOG.md +106 -65
  2. package/CHANGELOG.md.meta +7 -7
  3. package/Editor/Analyzers/BaseCallIlInspector.cs +277 -0
  4. package/Editor/Analyzers/BaseCallIlInspector.cs.meta +11 -0
  5. package/Editor/Analyzers/BaseCallLogMessageParser.cs +295 -0
  6. package/Editor/Analyzers/BaseCallLogMessageParser.cs.meta +11 -0
  7. package/Editor/Analyzers/BaseCallReportAggregator.cs +308 -0
  8. package/Editor/Analyzers/BaseCallReportAggregator.cs.meta +11 -0
  9. package/Editor/Analyzers/BaseCallTypeScanner.cs +110 -0
  10. package/Editor/Analyzers/BaseCallTypeScanner.cs.meta +11 -0
  11. package/Editor/Analyzers/BaseCallTypeScannerCore.cs +562 -0
  12. package/Editor/Analyzers/BaseCallTypeScannerCore.cs.meta +11 -0
  13. package/Editor/Analyzers/DxMessagingConsoleHarvester.cs +1122 -0
  14. package/Editor/Analyzers/DxMessagingConsoleHarvester.cs.meta +11 -0
  15. package/Editor/Analyzers/Microsoft.CodeAnalysis.CSharp.dll.meta +44 -44
  16. package/Editor/Analyzers/Microsoft.CodeAnalysis.dll.meta +44 -44
  17. package/Editor/Analyzers/System.Collections.Immutable.dll.meta +44 -44
  18. package/Editor/Analyzers/System.Reflection.Metadata.dll.meta +44 -44
  19. package/Editor/Analyzers/System.Runtime.CompilerServices.Unsafe.dll.meta +44 -44
  20. package/Editor/Analyzers/WallstopStudios.DxMessaging.Analyzer.dll +0 -0
  21. package/Editor/Analyzers/WallstopStudios.DxMessaging.Analyzer.dll.meta +33 -0
  22. package/Editor/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll +0 -0
  23. package/Editor/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll.meta +72 -72
  24. package/Editor/Analyzers.meta +8 -8
  25. package/Editor/AssemblyInfo.cs.meta +3 -3
  26. package/Editor/CustomEditors/MessageAwareComponentFallbackEditor.cs +81 -0
  27. package/Editor/CustomEditors/MessageAwareComponentFallbackEditor.cs.meta +11 -0
  28. package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs +420 -0
  29. package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs.meta +11 -0
  30. package/Editor/CustomEditors/MessagingComponentEditor.cs +1 -1
  31. package/Editor/CustomEditors/MessagingComponentEditor.cs.meta +2 -2
  32. package/Editor/CustomEditors.meta +2 -2
  33. package/Editor/DxMessagingEditorInitializer.cs +1 -1
  34. package/Editor/DxMessagingEditorInitializer.cs.meta +2 -2
  35. package/Editor/DxMessagingMenu.cs.meta +11 -11
  36. package/Editor/DxMessagingSceneBuildProcessor.cs.meta +11 -11
  37. package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs +190 -0
  38. package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs.meta +11 -0
  39. package/Editor/Settings/DxMessagingSettings.cs +189 -0
  40. package/Editor/Settings/DxMessagingSettings.cs.meta +2 -2
  41. package/Editor/Settings/DxMessagingSettingsProvider.cs +50 -33
  42. package/Editor/Settings/DxMessagingSettingsProvider.cs.meta +2 -2
  43. package/Editor/Settings.meta +2 -2
  44. package/Editor/SetupCscRsp.cs +209 -8
  45. package/Editor/SetupCscRsp.cs.meta +2 -2
  46. package/Editor/Testing/MessagingComponentEditorHarness.cs +1 -1
  47. package/Editor/Testing/MessagingComponentEditorHarness.cs.meta +3 -3
  48. package/Editor/Testing.meta +3 -3
  49. package/Editor/WallstopStudios.DxMessaging.Editor.asmdef +14 -14
  50. package/Editor/WallstopStudios.DxMessaging.Editor.asmdef.meta +7 -7
  51. package/Editor.meta +8 -8
  52. package/LICENSE.md +9 -9
  53. package/LICENSE.md.meta +7 -7
  54. package/README.md +941 -900
  55. package/README.md.meta +7 -7
  56. package/Runtime/AssemblyInfo.cs +4 -0
  57. package/Runtime/AssemblyInfo.cs.meta +2 -2
  58. package/Runtime/Core/Attributes/DxAutoConstructorAttribute.cs.meta +2 -2
  59. package/Runtime/Core/Attributes/DxBroadcastMessageAttribute.cs.meta +2 -2
  60. package/Runtime/Core/Attributes/DxIgnoreMissingBaseCallAttribute.cs +26 -0
  61. package/Runtime/Core/Attributes/DxIgnoreMissingBaseCallAttribute.cs.meta +11 -0
  62. package/Runtime/Core/Attributes/DxOptionalParameterAttribute.cs.meta +2 -2
  63. package/Runtime/Core/Attributes/DxTargetedMessageAttribute.cs.meta +2 -2
  64. package/Runtime/Core/Attributes/DxUntargetedMessageAttribute.cs.meta +2 -2
  65. package/Runtime/Core/Attributes.meta +2 -2
  66. package/Runtime/Core/Configuration/DxMessagingRuntimeSettings.cs +195 -0
  67. package/Runtime/Core/Configuration/DxMessagingRuntimeSettings.cs.meta +11 -0
  68. package/Runtime/Core/Configuration/DxMessagingRuntimeSettingsProvider.cs +179 -0
  69. package/Runtime/Core/Configuration/DxMessagingRuntimeSettingsProvider.cs.meta +11 -0
  70. package/Runtime/Core/Configuration.meta +9 -0
  71. package/Runtime/Core/DataStructure/CyclicBuffer.cs +2 -2
  72. package/Runtime/Core/DataStructure/CyclicBuffer.cs.meta +2 -2
  73. package/Runtime/Core/DataStructure.meta +2 -2
  74. package/Runtime/Core/Diagnostics/MessageEmissionData.cs.meta +2 -2
  75. package/Runtime/Core/Diagnostics/MessageRegistrationData.cs.meta +2 -2
  76. package/Runtime/Core/Diagnostics/MessageRegistrationType.cs.meta +2 -2
  77. package/Runtime/Core/Diagnostics.meta +2 -2
  78. package/Runtime/Core/DxMessagingStaticState.cs +19 -0
  79. package/Runtime/Core/DxMessagingStaticState.cs.meta +11 -11
  80. package/Runtime/Core/Extensions/EnumExtensions.cs.meta +2 -2
  81. package/Runtime/Core/Extensions/IListExtensions.cs.meta +2 -2
  82. package/Runtime/Core/Extensions/MessageBusExtensions.cs.meta +12 -12
  83. package/Runtime/Core/Extensions/MessageExtensions.cs.meta +11 -11
  84. package/Runtime/Core/Extensions.meta +8 -8
  85. package/Runtime/Core/Helper/MessageCache.cs +32 -0
  86. package/Runtime/Core/Helper/MessageCache.cs.meta +2 -2
  87. package/Runtime/Core/Helper/MessageHelperIndexer.cs.meta +2 -2
  88. package/Runtime/Core/Helper.meta +2 -2
  89. package/Runtime/Core/IMessage.cs +3 -3
  90. package/Runtime/Core/IMessage.cs.meta +11 -11
  91. package/Runtime/Core/InstanceId.cs.meta +11 -11
  92. package/Runtime/Core/Internal/TypedDispatchLinkIndex.cs +51 -0
  93. package/Runtime/Core/Internal/TypedDispatchLinkIndex.cs.meta +11 -0
  94. package/Runtime/Core/Internal/TypedGlobalSlotIndex.cs +38 -0
  95. package/Runtime/Core/Internal/TypedGlobalSlotIndex.cs.meta +11 -0
  96. package/Runtime/Core/Internal/TypedSlotIndex.cs +81 -0
  97. package/Runtime/Core/Internal/TypedSlotIndex.cs.meta +11 -0
  98. package/Runtime/Core/Internal/TypedSlots.cs +613 -0
  99. package/Runtime/Core/Internal/TypedSlots.cs.meta +11 -0
  100. package/Runtime/Core/Internal.meta +9 -0
  101. package/Runtime/Core/MessageBus/DiagnosticsTarget.cs.meta +11 -11
  102. package/Runtime/Core/MessageBus/GlobalMessageBusProvider.cs.meta +11 -11
  103. package/Runtime/Core/MessageBus/IMessageBus.cs +177 -3
  104. package/Runtime/Core/MessageBus/IMessageBus.cs.meta +11 -11
  105. package/Runtime/Core/MessageBus/IMessageBusProvider.cs.meta +11 -11
  106. package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs.meta +11 -11
  107. package/Runtime/Core/MessageBus/Internal/BusContextIndex.cs +16 -0
  108. package/Runtime/Core/MessageBus/Internal/BusContextIndex.cs.meta +11 -0
  109. package/Runtime/Core/MessageBus/Internal/BusSinkIndex.cs +40 -0
  110. package/Runtime/Core/MessageBus/Internal/BusSinkIndex.cs.meta +11 -0
  111. package/Runtime/Core/MessageBus/Internal/BusSlots.cs +718 -0
  112. package/Runtime/Core/MessageBus/Internal/BusSlots.cs.meta +11 -0
  113. package/Runtime/Core/MessageBus/Internal/DispatchKind.cs +38 -0
  114. package/Runtime/Core/MessageBus/Internal/DispatchKind.cs.meta +11 -0
  115. package/Runtime/Core/MessageBus/Internal/DispatchPhase.cs +20 -0
  116. package/Runtime/Core/MessageBus/Internal/DispatchPhase.cs.meta +11 -0
  117. package/Runtime/Core/MessageBus/Internal/DispatchVariant.cs +28 -0
  118. package/Runtime/Core/MessageBus/Internal/DispatchVariant.cs.meta +11 -0
  119. package/Runtime/Core/MessageBus/Internal/IEvictableSlot.cs +48 -0
  120. package/Runtime/Core/MessageBus/Internal/IEvictableSlot.cs.meta +11 -0
  121. package/Runtime/Core/MessageBus/Internal/ISweepable.cs +15 -0
  122. package/Runtime/Core/MessageBus/Internal/ISweepable.cs.meta +11 -0
  123. package/Runtime/Core/MessageBus/Internal/RegistrationMethodAxes.cs +222 -0
  124. package/Runtime/Core/MessageBus/Internal/RegistrationMethodAxes.cs.meta +11 -0
  125. package/Runtime/Core/MessageBus/Internal/SlotKey.cs +192 -0
  126. package/Runtime/Core/MessageBus/Internal/SlotKey.cs.meta +11 -0
  127. package/Runtime/Core/MessageBus/Internal.meta +9 -0
  128. package/Runtime/Core/MessageBus/MessageBus.cs +2651 -500
  129. package/Runtime/Core/MessageBus/MessageBus.cs.meta +11 -11
  130. package/Runtime/Core/MessageBus/MessageBusRebindMode.cs.meta +11 -11
  131. package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs.meta +11 -11
  132. package/Runtime/Core/MessageBus/MessagingRegistration.cs.meta +11 -11
  133. package/Runtime/Core/MessageBus/RegistrationLog.cs.meta +11 -11
  134. package/Runtime/Core/MessageBus.meta +8 -8
  135. package/Runtime/Core/MessageHandler.cs +2019 -542
  136. package/Runtime/Core/MessageHandler.cs.meta +11 -11
  137. package/Runtime/Core/MessageRegistrationHandle.cs.meta +11 -11
  138. package/Runtime/Core/MessageRegistrationToken.cs +7 -0
  139. package/Runtime/Core/MessageRegistrationToken.cs.meta +11 -11
  140. package/Runtime/Core/Messages/GlobalStringMessage.cs.meta +2 -2
  141. package/Runtime/Core/Messages/IBroadcastMessage.cs.meta +11 -11
  142. package/Runtime/Core/Messages/ITargetedMessage.cs.meta +11 -11
  143. package/Runtime/Core/Messages/IUntargetedMessage.cs.meta +11 -11
  144. package/Runtime/Core/Messages/ReflexiveMessage.cs.meta +2 -2
  145. package/Runtime/Core/Messages/SourcedStringMessage.cs.meta +11 -11
  146. package/Runtime/Core/Messages/StringMessage.cs.meta +2 -2
  147. package/Runtime/Core/Messages.meta +8 -8
  148. package/Runtime/Core/MessagingDebug.cs.meta +11 -11
  149. package/Runtime/Core/Pooling/CollectionPool.cs +266 -0
  150. package/Runtime/Core/Pooling/CollectionPool.cs.meta +11 -0
  151. package/Runtime/Core/Pooling/CollectionPoolDiagnostics.cs +30 -0
  152. package/Runtime/Core/Pooling/CollectionPoolDiagnostics.cs.meta +11 -0
  153. package/Runtime/Core/Pooling/DxPools.cs +157 -0
  154. package/Runtime/Core/Pooling/DxPools.cs.meta +11 -0
  155. package/Runtime/Core/Pooling/EvictionPlayerLoopHook.cs +106 -0
  156. package/Runtime/Core/Pooling/EvictionPlayerLoopHook.cs.meta +11 -0
  157. package/Runtime/Core/Pooling/IDxMessagingClock.cs +18 -0
  158. package/Runtime/Core/Pooling/IDxMessagingClock.cs.meta +11 -0
  159. package/Runtime/Core/Pooling/PoolDiagnosticsSnapshot.cs +55 -0
  160. package/Runtime/Core/Pooling/PoolDiagnosticsSnapshot.cs.meta +11 -0
  161. package/Runtime/Core/Pooling/StopwatchClock.cs +27 -0
  162. package/Runtime/Core/Pooling/StopwatchClock.cs.meta +11 -0
  163. package/Runtime/Core/Pooling/UnityRealtimeClock.cs +31 -0
  164. package/Runtime/Core/Pooling/UnityRealtimeClock.cs.meta +11 -0
  165. package/Runtime/Core/Pooling.meta +9 -0
  166. package/Runtime/Core.meta +8 -8
  167. package/Runtime/Unity/CurrentGlobalMessageBusProvider.cs.meta +12 -12
  168. package/Runtime/Unity/DxMessagingRuntimeInitializer.cs.meta +11 -11
  169. package/Runtime/Unity/InitialGlobalMessageBusProvider.cs.meta +12 -12
  170. package/Runtime/Unity/Integrations/Reflex/AssemblyInfo.cs.meta +2 -2
  171. package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs +73 -0
  172. package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs.meta +11 -11
  173. package/Runtime/Unity/Integrations/Reflex/WallstopStudios.DxMessaging.Reflex.asmdef +20 -20
  174. package/Runtime/Unity/Integrations/Reflex/WallstopStudios.DxMessaging.Reflex.asmdef.meta +7 -7
  175. package/Runtime/Unity/Integrations/Reflex.meta +8 -8
  176. package/Runtime/Unity/Integrations/VContainer/AssemblyInfo.cs.meta +2 -2
  177. package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs +109 -1
  178. package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs.meta +11 -11
  179. package/Runtime/Unity/Integrations/VContainer/WallstopStudios.DxMessaging.VContainer.asmdef +30 -30
  180. package/Runtime/Unity/Integrations/VContainer/WallstopStudios.DxMessaging.VContainer.asmdef.meta +7 -7
  181. package/Runtime/Unity/Integrations/VContainer.meta +8 -8
  182. package/Runtime/Unity/Integrations/Zenject/AssemblyInfo.cs.meta +2 -2
  183. package/Runtime/Unity/Integrations/Zenject/WallstopStudios.DxMessaging.Zenject.asmdef +30 -30
  184. package/Runtime/Unity/Integrations/Zenject/WallstopStudios.DxMessaging.Zenject.asmdef.meta +7 -7
  185. package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs +79 -1
  186. package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs.meta +11 -11
  187. package/Runtime/Unity/Integrations/Zenject.meta +8 -8
  188. package/Runtime/Unity/Integrations.meta +8 -8
  189. package/Runtime/Unity/MessageAwareComponent.cs +29 -0
  190. package/Runtime/Unity/MessageAwareComponent.cs.meta +11 -11
  191. package/Runtime/Unity/MessageBusProviderHandle.cs.meta +12 -12
  192. package/Runtime/Unity/MessagingComponent.cs.meta +11 -11
  193. package/Runtime/Unity/MessagingComponentInstaller.cs.meta +12 -12
  194. package/Runtime/Unity/ScriptableMessageBusProvider.cs.meta +12 -12
  195. package/Runtime/Unity.meta +8 -8
  196. package/Runtime/WallstopStudios.DxMessaging.asmdef +14 -14
  197. package/Runtime/WallstopStudios.DxMessaging.asmdef.meta +7 -7
  198. package/Runtime.meta +8 -8
  199. package/Samples~/DI/Prefabs/MessagingInstallerSample.prefab +98 -98
  200. package/Samples~/DI/Prefabs/MessagingInstallerSample.prefab.meta +7 -7
  201. package/Samples~/DI/Prefabs.meta +8 -8
  202. package/Samples~/DI/Providers/GlobalMessageBusProvider.asset +14 -14
  203. package/Samples~/DI/Providers/GlobalMessageBusProvider.asset.meta +8 -8
  204. package/Samples~/DI/Providers/InitialGlobalMessageBusProvider.asset +14 -14
  205. package/Samples~/DI/Providers/InitialGlobalMessageBusProvider.asset.meta +8 -8
  206. package/Samples~/DI/Providers.meta +8 -8
  207. package/Samples~/DI/README.md +51 -51
  208. package/Samples~/DI/README.md.meta +7 -7
  209. package/Samples~/DI/Reflex/SampleInstaller.cs +7 -0
  210. package/Samples~/DI/Reflex/SampleInstaller.cs.meta +11 -11
  211. package/Samples~/DI/Reflex.meta +8 -8
  212. package/Samples~/DI/VContainer/SampleLifetimeScope.cs +6 -1
  213. package/Samples~/DI/VContainer/SampleLifetimeScope.cs.meta +11 -11
  214. package/Samples~/DI/VContainer.meta +8 -8
  215. package/Samples~/DI/Zenject/SampleInstaller.cs +8 -0
  216. package/Samples~/DI/Zenject/SampleInstaller.cs.meta +11 -11
  217. package/Samples~/DI/Zenject.meta +8 -8
  218. package/Samples~/DI.meta +8 -8
  219. package/Samples~/Mini Combat/Boot.cs.meta +11 -11
  220. package/Samples~/Mini Combat/Enemy.cs.meta +11 -11
  221. package/Samples~/Mini Combat/Messages.cs.meta +11 -11
  222. package/Samples~/Mini Combat/Player.cs.meta +11 -11
  223. package/Samples~/Mini Combat/README.md +324 -323
  224. package/Samples~/Mini Combat/README.md.meta +7 -7
  225. package/Samples~/Mini Combat/UIOverlay.cs.meta +11 -11
  226. package/Samples~/Mini Combat/Walkthrough.md +430 -430
  227. package/Samples~/Mini Combat/Walkthrough.md.meta +7 -7
  228. package/Samples~/Mini Combat/WallstopStudios.DxMessaging.MiniCombat.Sample.asmdef +13 -13
  229. package/Samples~/Mini Combat/WallstopStudios.DxMessaging.MiniCombat.Sample.asmdef.meta +7 -7
  230. package/Samples~/Mini Combat.meta +8 -8
  231. package/Samples~/UI Buttons + Inspector/DiagnosticsEnabler.cs.meta +11 -11
  232. package/Samples~/UI Buttons + Inspector/Messages.cs.meta +11 -11
  233. package/Samples~/UI Buttons + Inspector/MessagingObserver.cs.meta +11 -11
  234. package/Samples~/UI Buttons + Inspector/README.md +210 -209
  235. package/Samples~/UI Buttons + Inspector/README.md.meta +7 -7
  236. package/Samples~/UI Buttons + Inspector/UIButtonEmitter.cs.meta +11 -11
  237. package/Samples~/UI Buttons + Inspector/WallstopStudios.DxMessaging.UIButtons.Sample.asmdef +13 -13
  238. package/Samples~/UI Buttons + Inspector/WallstopStudios.DxMessaging.UIButtons.Sample.asmdef.meta +7 -7
  239. package/Samples~/UI Buttons + Inspector.meta +8 -8
  240. package/SourceGenerators/Directory.Build.props.meta +7 -7
  241. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxAutoConstructorGenerator.cs.meta +11 -11
  242. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxMessageIdGenerator.cs.meta +2 -2
  243. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.csproj.meta +7 -7
  244. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.meta +8 -8
  245. package/SourceGenerators.meta +8 -8
  246. package/Third Party Notices.md +3 -3
  247. package/Third Party Notices.md.meta +7 -7
  248. package/package.json +115 -92
  249. package/package.json.meta +7 -7
@@ -0,0 +1,562 @@
1
+ // The Unity Editor assembly that hosts this file does not enable nullable annotations; the
2
+ // dotnet-test project that compiles a linked copy DOES (`<Nullable>enable</Nullable>`). Pin the
3
+ // nullable state per-file so behavior is identical in both compilation contexts.
4
+ #nullable disable
5
+ namespace DxMessaging.Editor.Analyzers
6
+ {
7
+ using System;
8
+ using System.Collections.Generic;
9
+ using System.Reflection;
10
+
11
+ /// <summary>
12
+ /// Pure (Unity-API-free) classification core for the IL-reflection scanner. Takes a
13
+ /// pre-supplied set of candidate <see cref="MessageAwareComponent"/>-derived types and
14
+ /// produces the per-FQN snapshot that the inspector overlay consumes.
15
+ /// </summary>
16
+ /// <remarks>
17
+ /// <para>
18
+ /// Extracted from <see cref="BaseCallTypeScanner"/> so the dotnet-test project can cover the
19
+ /// classification logic (chain walk, opt-out paths, master-toggle gating, FQN normalisation,
20
+ /// abstract / generic-definition skipping) without depending on Unity's <c>TypeCache</c> API.
21
+ /// The Unity-only wrapper <see cref="BaseCallTypeScanner.Scan"/> simply forwards
22
+ /// <c>TypeCache.GetTypesDerivedFrom&lt;MessageAwareComponent&gt;()</c> as the candidate set
23
+ /// and reads the project's ignore list off <c>DxMessagingSettings</c>.
24
+ /// </para>
25
+ /// <para>
26
+ /// All inputs are pure-BCL: tests compile small Roslyn fixtures, load the resulting
27
+ /// assemblies, enumerate <c>assembly.GetTypes().Where(t =&gt; ...)</c> as the candidate set,
28
+ /// and assert against the returned snapshot.
29
+ /// </para>
30
+ /// <para>
31
+ /// Diagnostic IDs produced match what the inspector overlay reads from the Unity-facing entry:
32
+ /// <c>DXMSG006</c> (override missing base call), <c>DXMSG007</c> (hides via <c>new</c>; also
33
+ /// covers DXMSG009 since IL alone can't distinguish the two -- see remarks on
34
+ /// <see cref="BaseCallIlInspector"/>), and <c>DXMSG010</c> (override calls base but a chain
35
+ /// link does not).
36
+ /// </para>
37
+ /// </remarks>
38
+ public static class BaseCallTypeScannerCore
39
+ {
40
+ /// <summary>
41
+ /// The guarded lifecycle methods on <c>MessageAwareComponent</c>. Method names are
42
+ /// matched ordinally. Most are zero-parameter, void-returning instance methods;
43
+ /// <c>OnApplicationFocus</c> and <c>OnApplicationPause</c> are guarded prospectively
44
+ /// for their canonical Unity <c>(bool)</c> signature even though the base class does
45
+ /// not currently declare them. See
46
+ /// <c>MessageAwareComponentBaseCallAnalyzer.GuardedMethodNames</c> /
47
+ /// <c>GuardedMethodsWithBoolSignature</c>; the two sets MUST stay in sync (a meta-test
48
+ /// in the dotnet-test project asserts set-equality).
49
+ /// </summary>
50
+ public static readonly string[] GuardedMethodNames =
51
+ {
52
+ "Awake",
53
+ "OnEnable",
54
+ "OnDisable",
55
+ "OnDestroy",
56
+ "OnApplicationFocus",
57
+ "OnApplicationPause",
58
+ "RegisterMessageHandlers",
59
+ };
60
+
61
+ /// <summary>
62
+ /// Guarded methods whose canonical Unity signature takes a single <c>bool</c>
63
+ /// (<c>OnApplicationFocus(bool focused)</c>, <c>OnApplicationPause(bool paused)</c>).
64
+ /// All other guarded methods are zero-argument.
65
+ /// </summary>
66
+ public static readonly HashSet<string> GuardedMethodsWithBoolSignature = new(
67
+ StringComparer.Ordinal
68
+ )
69
+ {
70
+ "OnApplicationFocus",
71
+ "OnApplicationPause",
72
+ };
73
+
74
+ /// <summary>
75
+ /// Per-method consequence text shown by the inspector overlay HelpBox when an override
76
+ /// is missing its base call. Mirrors
77
+ /// <c>MessageAwareComponentBaseCallAnalyzer.MissingBaseCallMessageFormatsByMethod</c>
78
+ /// keyed by method name. The two dictionaries MUST stay in sync; the inspector overlay
79
+ /// already calls <see cref="GetMissingBaseConsequenceLine"/> per missing-method row, and
80
+ /// the meta-test on the analyzer side keeps the analyzer's dictionary populated for every
81
+ /// guarded method, but a future contributor adding a new guarded method MUST update both.
82
+ /// ASCII-only by policy.
83
+ /// </summary>
84
+ public static readonly IReadOnlyDictionary<
85
+ string,
86
+ string
87
+ > MissingBaseCallMessageFormatsByMethod = new Dictionary<string, string>(
88
+ StringComparer.Ordinal
89
+ )
90
+ {
91
+ {
92
+ "Awake",
93
+ "'{0}' overrides MessageAwareComponent.Awake but does not call base.Awake(); the message registration token will never be created and handlers cannot register."
94
+ },
95
+ {
96
+ "OnEnable",
97
+ "'{0}' overrides MessageAwareComponent.OnEnable but does not call base.OnEnable(); handlers will not be re-enabled when this component is enabled."
98
+ },
99
+ {
100
+ "OnDisable",
101
+ "'{0}' overrides MessageAwareComponent.OnDisable but does not call base.OnDisable(); handlers will not be disabled when this component is disabled, causing unwanted message processing."
102
+ },
103
+ {
104
+ "OnDestroy",
105
+ "'{0}' overrides MessageAwareComponent.OnDestroy but does not call base.OnDestroy(); handlers will not be deregistered and the registration token will not be released, causing a memory leak."
106
+ },
107
+ {
108
+ "RegisterMessageHandlers",
109
+ "'{0}' overrides MessageAwareComponent.RegisterMessageHandlers but does not call base.RegisterMessageHandlers(); default string-message handlers will not be registered (override RegisterForStringMessages to suppress this warning)."
110
+ },
111
+ // Prospective entries. MessageAwareComponent does not currently declare these
112
+ // methods; entries exist so future changes immediately surface actionable
113
+ // consequence text. Keep aligned with the analyzer's dictionary.
114
+ {
115
+ "OnApplicationFocus",
116
+ "'{0}' overrides MessageAwareComponent.OnApplicationFocus but does not call base.OnApplicationFocus(); the messaging system may not function correctly on this component when focus changes."
117
+ },
118
+ {
119
+ "OnApplicationPause",
120
+ "'{0}' overrides MessageAwareComponent.OnApplicationPause but does not call base.OnApplicationPause(); the messaging system may not function correctly on this component when the application pauses."
121
+ },
122
+ };
123
+
124
+ private const string GenericMissingBaseCallMessageFormat =
125
+ "'{0}' overrides MessageAwareComponent.{1} but does not call base.{1}(); the messaging system may not function correctly on this component.";
126
+
127
+ /// <summary>
128
+ /// Returns the per-method consequence sentence for a given guarded method name, formatted
129
+ /// against the supplied type display string. Falls back to the generic format if the
130
+ /// method is unknown so that inserting a new guarded method does not blank the overlay.
131
+ /// </summary>
132
+ public static string GetMissingBaseConsequenceLine(string methodName, string typeDisplay)
133
+ {
134
+ string format = MissingBaseCallMessageFormatsByMethod.TryGetValue(
135
+ methodName ?? string.Empty,
136
+ out string perMethodFormat
137
+ )
138
+ ? perMethodFormat
139
+ : GenericMissingBaseCallMessageFormat;
140
+ return string.Format(
141
+ System.Globalization.CultureInfo.InvariantCulture,
142
+ format,
143
+ typeDisplay ?? string.Empty,
144
+ methodName ?? string.Empty
145
+ );
146
+ }
147
+
148
+ private const string IgnoreAttributeFullName =
149
+ "DxMessaging.Core.Attributes.DxIgnoreMissingBaseCallAttribute";
150
+
151
+ private const string MessageAwareComponentFullName =
152
+ "DxMessaging.Unity.MessageAwareComponent";
153
+
154
+ private static readonly Type[] BoolParameterTypes = { typeof(bool) };
155
+
156
+ /// <summary>
157
+ /// Result row produced by <see cref="Scan"/>. Mirrors the Unity-facing
158
+ /// <c>BaseCallReportEntry</c> shape but uses pure BCL collections so the helper is
159
+ /// callable from <c>dotnet test</c>.
160
+ /// </summary>
161
+ public sealed class ScanEntry
162
+ {
163
+ /// <summary>Fully-qualified name of the offending type (dot-form for nested types).</summary>
164
+ public string TypeName;
165
+
166
+ /// <summary>Method names whose overrides are missing the corresponding <c>base.*()</c> call.</summary>
167
+ public SortedSet<string> MissingBaseFor = new(StringComparer.Ordinal);
168
+
169
+ /// <summary>Diagnostic IDs that contributed to this entry (DXMSG006 / DXMSG007 / DXMSG010).</summary>
170
+ public HashSet<string> DiagnosticIds = new(StringComparer.Ordinal);
171
+ }
172
+
173
+ /// <summary>
174
+ /// Classify every <paramref name="candidates"/> type and return a per-FQN snapshot keyed
175
+ /// by fully-qualified type name (dot-form for nested types). Types opted out via
176
+ /// class-level <c>[DxIgnoreMissingBaseCall]</c> or via
177
+ /// <paramref name="ignoredTypeNames"/> are
178
+ /// intentionally NOT included in the returned dictionary -- the inspector overlay reads
179
+ /// the project ignore list directly to render its "Stop ignoring" HelpBox, and the
180
+ /// snapshot semantics here match the bridge path (DXMSG008-equivalent rows were never
181
+ /// present in the snapshot's <c>missingBaseFor</c> either). Method-level
182
+ /// <c>[DxIgnoreMissingBaseCall]</c> is applied per guarded method only.
183
+ /// </summary>
184
+ /// <param name="candidates">
185
+ /// Strict subclasses of <c>MessageAwareComponent</c>. Abstract types and generic-type
186
+ /// definitions are skipped; the <c>MessageAwareComponent</c> type itself is also skipped.
187
+ /// May contain <c>null</c> entries (defensively skipped).
188
+ /// </param>
189
+ /// <param name="ignoredTypeNames">
190
+ /// Project-level ignore list (typically the parsed contents of
191
+ /// <c>Assets/Editor/DxMessaging.BaseCallIgnore.txt</c>, surfaced through
192
+ /// <c>DxMessagingSettings._baseCallIgnoredTypes</c>). May be <c>null</c>.
193
+ /// </param>
194
+ public static Dictionary<string, ScanEntry> Scan(
195
+ IEnumerable<Type> candidates,
196
+ IEnumerable<string> ignoredTypeNames
197
+ )
198
+ {
199
+ Dictionary<string, ScanEntry> result = new(StringComparer.Ordinal);
200
+ if (candidates is null)
201
+ {
202
+ return result;
203
+ }
204
+
205
+ HashSet<string> projectIgnore = ignoredTypeNames is null
206
+ ? new HashSet<string>(StringComparer.Ordinal)
207
+ : new HashSet<string>(ignoredTypeNames, StringComparer.Ordinal);
208
+
209
+ foreach (Type concrete in candidates)
210
+ {
211
+ if (concrete == null)
212
+ {
213
+ continue;
214
+ }
215
+ if (concrete.IsAbstract)
216
+ {
217
+ continue;
218
+ }
219
+ if (concrete.IsGenericTypeDefinition)
220
+ {
221
+ continue;
222
+ }
223
+
224
+ string fullName = concrete.FullName ?? string.Empty;
225
+ if (string.IsNullOrEmpty(fullName))
226
+ {
227
+ continue;
228
+ }
229
+ if (
230
+ string.Equals(fullName, MessageAwareComponentFullName, StringComparison.Ordinal)
231
+ )
232
+ {
233
+ continue;
234
+ }
235
+ // FullName for nested types uses '+'; the analyzer (and the inspector overlay's
236
+ // lookup) emits the dotted form. Normalise here so the scanner-produced snapshot
237
+ // is keyed identically to the analyzer's identifiers.
238
+ fullName = fullName.Replace('+', '.');
239
+
240
+ bool optedOutByAttribute = TypeHasIgnoreAttribute(concrete);
241
+ bool optedOutByList = projectIgnore.Contains(fullName);
242
+
243
+ if (optedOutByAttribute || optedOutByList)
244
+ {
245
+ // Suppression makes the entry an audit-marker (DXMSG008-equivalent). The
246
+ // overlay's "ignored" branch handles this via the ignored-types list directly,
247
+ // so we don't add it to the snapshot at all; the overlay reads the project
248
+ // list to render the "Stop ignoring" HelpBox. This matches the bridge path's
249
+ // snapshot semantics (DXMSG008 was never in MissingBaseFor either).
250
+ continue;
251
+ }
252
+
253
+ HashSet<string> methodLevelIgnore = GetGuardedMethodsWithIgnoreAttribute(concrete);
254
+
255
+ ScanEntry entry = ScanOne(concrete, fullName, methodLevelIgnore);
256
+ if (entry == null || entry.MissingBaseFor.Count == 0)
257
+ {
258
+ continue;
259
+ }
260
+
261
+ result[fullName] = entry;
262
+ }
263
+
264
+ return result;
265
+ }
266
+
267
+ private static bool TypeHasIgnoreAttribute(Type type)
268
+ {
269
+ // [DxIgnoreMissingBaseCall] applies with Inherited=false (matches the analyzer's
270
+ // attribute declaration), so we inspect only the type itself.
271
+ foreach (object attr in type.GetCustomAttributes(inherit: false))
272
+ {
273
+ if (attr.GetType().FullName == IgnoreAttributeFullName)
274
+ {
275
+ return true;
276
+ }
277
+ }
278
+ return false;
279
+ }
280
+
281
+ private static HashSet<string> GetGuardedMethodsWithIgnoreAttribute(Type type)
282
+ {
283
+ HashSet<string> ignoredMethods = new(StringComparer.Ordinal);
284
+ foreach (string methodName in GuardedMethodNames)
285
+ {
286
+ MethodInfo m = GetDeclaredInstance(type, methodName);
287
+ if (m == null)
288
+ {
289
+ continue;
290
+ }
291
+ foreach (object attr in m.GetCustomAttributes(inherit: false))
292
+ {
293
+ if (attr.GetType().FullName == IgnoreAttributeFullName)
294
+ {
295
+ ignoredMethods.Add(methodName);
296
+ break;
297
+ }
298
+ }
299
+ }
300
+ return ignoredMethods;
301
+ }
302
+
303
+ private static ScanEntry ScanOne(
304
+ Type concrete,
305
+ string fullName,
306
+ HashSet<string> methodLevelIgnore
307
+ )
308
+ {
309
+ ScanEntry entry = new()
310
+ {
311
+ TypeName = fullName,
312
+ MissingBaseFor = new SortedSet<string>(StringComparer.Ordinal),
313
+ DiagnosticIds = new HashSet<string>(StringComparer.Ordinal),
314
+ };
315
+
316
+ foreach (string methodName in GuardedMethodNames)
317
+ {
318
+ ClassifyMethod(concrete, methodName, entry, methodLevelIgnore);
319
+ }
320
+
321
+ return entry;
322
+ }
323
+
324
+ private static void ClassifyMethod(
325
+ Type concrete,
326
+ string methodName,
327
+ ScanEntry entry,
328
+ HashSet<string> methodLevelIgnore
329
+ )
330
+ {
331
+ if (methodLevelIgnore.Contains(methodName))
332
+ {
333
+ return;
334
+ }
335
+
336
+ // Walk the type chain: first the leaf (concrete), then ancestors via BaseType until we
337
+ // leave the MessageAwareComponent inheritance subtree. For the leaf we determine which
338
+ // of DXMSG006/007/009 fires (if any). If the leaf overrides correctly, we walk
339
+ // ancestor links to detect DXMSG010 (a broken intermediate). Each link's diagnosis is
340
+ // independent; we only record the FIRST classification for the leaf in
341
+ // entry.MissingBaseFor since the overlay HelpBox shows one row per method per type.
342
+
343
+ MethodInfo declared = GetDeclaredInstance(concrete, methodName);
344
+ if (declared == null)
345
+ {
346
+ // Type does not declare this method at all; nothing to flag at this level.
347
+ return;
348
+ }
349
+ if (declared.ReturnType != typeof(void))
350
+ {
351
+ return;
352
+ }
353
+ if (declared.IsStatic)
354
+ {
355
+ return;
356
+ }
357
+ if (declared.IsGenericMethodDefinition)
358
+ {
359
+ return;
360
+ }
361
+
362
+ // DXMSG009 vs DXMSG007: declares without override (or with `new`); hides the base.
363
+ // In IL/reflection terms: the method does NOT have the override slot binding
364
+ // (GetBaseDefinition() returns the method itself) AND the base type has a same-named
365
+ // virtual we are hiding. The C# compiler emits the same IL for `new void X()` and
366
+ // `void X()`-with-CS0114, so we cannot perfectly distinguish DXMSG007 from DXMSG009
367
+ // from IL alone. The compile-time analyzer is authoritative for the precise ID;
368
+ // here we conservatively classify the case as DXMSG007; both produce the same
369
+ // overlay outcome (method listed in HelpBox).
370
+ bool isOverride = declared.GetBaseDefinition() != declared;
371
+ bool hasNewKeyword =
372
+ !isOverride && BaseHasSameNamedVirtual(concrete.BaseType, methodName);
373
+
374
+ if (!isOverride)
375
+ {
376
+ if (hasNewKeyword)
377
+ {
378
+ AddIfMissing(entry, methodName, "DXMSG007");
379
+ }
380
+ // else: not an override, no base virtual to hide; not our concern.
381
+ return;
382
+ }
383
+
384
+ // It IS an override. Check IL for base call.
385
+ bool callsBase = BaseCallIlInspector.MethodIlContainsBaseCall(declared, methodName);
386
+ if (!callsBase)
387
+ {
388
+ AddIfMissing(entry, methodName, "DXMSG006");
389
+ return;
390
+ }
391
+
392
+ // Leaf calls base. Walk the inheritance chain to look for a broken intermediate
393
+ // (DXMSG010). Each link's IL is inspected independently; the first broken link found
394
+ // produces DXMSG010 on the leaf and we stop. Cross-assembly ancestors with no IL body
395
+ // are trusted (assume-clean); the alternative would be unactionable warnings against
396
+ // closed-source code.
397
+ MethodInfo cursorOverridden = GetOverriddenMethod(declared);
398
+ HashSet<MethodInfo> visited = new();
399
+ while (cursorOverridden != null && visited.Add(cursorOverridden))
400
+ {
401
+ // Chain reached MessageAwareComponent itself; clean. We compare by full type
402
+ // name so the helper does not need a hard reference to the Unity-only type.
403
+ Type cursorDeclaring = cursorOverridden.DeclaringType;
404
+ if (
405
+ cursorDeclaring != null
406
+ && cursorDeclaring.FullName == "DxMessaging.Unity.MessageAwareComponent"
407
+ )
408
+ {
409
+ return;
410
+ }
411
+ if (cursorOverridden.GetMethodBody() == null)
412
+ {
413
+ // Cross-assembly / abstract; assume clean (cannot inspect).
414
+ return;
415
+ }
416
+ bool ancestorCallsBase = BaseCallIlInspector.MethodIlContainsBaseCall(
417
+ cursorOverridden,
418
+ methodName
419
+ );
420
+ if (!ancestorCallsBase)
421
+ {
422
+ AddIfMissing(entry, methodName, "DXMSG010");
423
+ return;
424
+ }
425
+ cursorOverridden = GetOverriddenMethod(cursorOverridden);
426
+ }
427
+ }
428
+
429
+ private static MethodInfo GetDeclaredZeroArgInstance(Type type, string methodName)
430
+ {
431
+ return type.GetMethod(
432
+ methodName,
433
+ BindingFlags.Public
434
+ | BindingFlags.NonPublic
435
+ | BindingFlags.Instance
436
+ | BindingFlags.DeclaredOnly,
437
+ null,
438
+ Type.EmptyTypes,
439
+ null
440
+ );
441
+ }
442
+
443
+ private static MethodInfo GetDeclaredBoolArgInstance(Type type, string methodName)
444
+ {
445
+ return type.GetMethod(
446
+ methodName,
447
+ BindingFlags.Public
448
+ | BindingFlags.NonPublic
449
+ | BindingFlags.Instance
450
+ | BindingFlags.DeclaredOnly,
451
+ null,
452
+ BoolParameterTypes,
453
+ null
454
+ );
455
+ }
456
+
457
+ /// <summary>
458
+ /// Resolves the declared instance method for a guarded name. Most guarded methods are
459
+ /// zero-arg (and we look up via <see cref="GetDeclaredZeroArgInstance"/>), but the
460
+ /// canonical Unity signature for <c>OnApplicationFocus</c> / <c>OnApplicationPause</c>
461
+ /// takes a single <c>bool</c>; for those names we try the zero-arg lookup first
462
+ /// (defensive against an unusual subclass that declared a zero-arg variant) and fall
463
+ /// back to the bool variant.
464
+ /// </summary>
465
+ private static MethodInfo GetDeclaredInstance(Type type, string methodName)
466
+ {
467
+ MethodInfo zeroArg = GetDeclaredZeroArgInstance(type, methodName);
468
+ if (zeroArg != null)
469
+ {
470
+ return zeroArg;
471
+ }
472
+ if (!GuardedMethodsWithBoolSignature.Contains(methodName))
473
+ {
474
+ return null;
475
+ }
476
+ return GetDeclaredBoolArgInstance(type, methodName);
477
+ }
478
+
479
+ private static IEnumerable<Type[]> GetGuardedMethodSignatures(string methodName)
480
+ {
481
+ yield return Type.EmptyTypes;
482
+ if (GuardedMethodsWithBoolSignature.Contains(methodName))
483
+ {
484
+ yield return BoolParameterTypes;
485
+ }
486
+ }
487
+
488
+ private static bool BaseHasSameNamedVirtual(Type baseType, string methodName)
489
+ {
490
+ while (baseType != null && baseType != typeof(object))
491
+ {
492
+ foreach (Type[] parameterTypes in GetGuardedMethodSignatures(methodName))
493
+ {
494
+ MethodInfo m = baseType.GetMethod(
495
+ methodName,
496
+ BindingFlags.Public
497
+ | BindingFlags.NonPublic
498
+ | BindingFlags.Instance
499
+ | BindingFlags.DeclaredOnly,
500
+ null,
501
+ parameterTypes,
502
+ null
503
+ );
504
+ if (m != null && (m.IsVirtual || m.IsAbstract))
505
+ {
506
+ return true;
507
+ }
508
+ }
509
+ baseType = baseType.BaseType;
510
+ }
511
+ return false;
512
+ }
513
+
514
+ private static Type[] GetParameterTypes(MethodInfo method)
515
+ {
516
+ ParameterInfo[] parameters = method.GetParameters();
517
+ Type[] parameterTypes = new Type[parameters.Length];
518
+ for (int i = 0; i < parameters.Length; i++)
519
+ {
520
+ parameterTypes[i] = parameters[i].ParameterType;
521
+ }
522
+ return parameterTypes;
523
+ }
524
+
525
+ private static MethodInfo GetOverriddenMethod(MethodInfo derivedOverride)
526
+ {
527
+ // For an override, GetBaseDefinition() returns the most-base virtual (the originating
528
+ // declaration). To walk the chain link-by-link we need the closest ancestor that
529
+ // declares the same-named method directly; we look up each BaseType in turn and
530
+ // return the first match. This skips intermediate types that don't override the slot
531
+ // (e.g. a generic intermediate that just passes through), which is exactly what the
532
+ // chain walk needs to detect DXMSG010 at the broken link rather than the pass-through.
533
+ Type baseType = derivedOverride.DeclaringType?.BaseType;
534
+ Type[] signature = GetParameterTypes(derivedOverride);
535
+ while (baseType != null && baseType != typeof(object))
536
+ {
537
+ MethodInfo m = baseType.GetMethod(
538
+ derivedOverride.Name,
539
+ BindingFlags.Public
540
+ | BindingFlags.NonPublic
541
+ | BindingFlags.Instance
542
+ | BindingFlags.DeclaredOnly,
543
+ null,
544
+ signature,
545
+ null
546
+ );
547
+ if (m != null)
548
+ {
549
+ return m;
550
+ }
551
+ baseType = baseType.BaseType;
552
+ }
553
+ return null;
554
+ }
555
+
556
+ private static void AddIfMissing(ScanEntry entry, string methodName, string diagnosticId)
557
+ {
558
+ entry.MissingBaseFor.Add(methodName);
559
+ entry.DiagnosticIds.Add(diagnosticId);
560
+ }
561
+ }
562
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 0ee81bf7d5b5447c94730f4185e64942
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant: