com.wallstop-studios.dxmessaging 2.2.0 → 3.1.0

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 (270) hide show
  1. package/CHANGELOG.md +315 -67
  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 +1129 -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 +46 -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 +11 -3
  26. package/Editor/CustomEditors/MessageAwareComponentFallbackEditor.cs +81 -0
  27. package/Editor/CustomEditors/MessageAwareComponentFallbackEditor.cs.meta +11 -0
  28. package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs +429 -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 +11 -3
  32. package/Editor/CustomEditors.meta +2 -2
  33. package/Editor/DxMessagingEditorIdle.cs +62 -0
  34. package/Editor/DxMessagingEditorIdle.cs.meta +11 -0
  35. package/Editor/DxMessagingEditorInitializer.cs +112 -15
  36. package/Editor/DxMessagingEditorInitializer.cs.meta +11 -3
  37. package/Editor/DxMessagingEditorLog.cs +32 -0
  38. package/Editor/DxMessagingEditorLog.cs.meta +11 -0
  39. package/Editor/DxMessagingMenu.cs.meta +11 -11
  40. package/Editor/DxMessagingSceneBuildProcessor.cs.meta +11 -11
  41. package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs +313 -0
  42. package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs.meta +11 -0
  43. package/Editor/Settings/DxMessagingSettings.cs +261 -11
  44. package/Editor/Settings/DxMessagingSettings.cs.meta +11 -3
  45. package/Editor/Settings/DxMessagingSettingsProvider.cs +50 -33
  46. package/Editor/Settings/DxMessagingSettingsProvider.cs.meta +11 -3
  47. package/Editor/Settings.meta +2 -2
  48. package/Editor/SetupCscRsp.cs +406 -39
  49. package/Editor/SetupCscRsp.cs.meta +11 -3
  50. package/Editor/Testing/MessagingComponentEditorHarness.cs +2 -2
  51. package/Editor/Testing/MessagingComponentEditorHarness.cs.meta +11 -3
  52. package/Editor/Testing.meta +3 -3
  53. package/Editor/WallstopStudios.DxMessaging.Editor.asmdef +14 -14
  54. package/Editor/WallstopStudios.DxMessaging.Editor.asmdef.meta +7 -7
  55. package/Editor.meta +8 -8
  56. package/LICENSE.md +9 -9
  57. package/LICENSE.md.meta +7 -7
  58. package/README.md +940 -900
  59. package/README.md.meta +7 -7
  60. package/Runtime/AssemblyInfo.cs +4 -0
  61. package/Runtime/AssemblyInfo.cs.meta +11 -3
  62. package/Runtime/Core/Attributes/DxAutoConstructorAttribute.cs.meta +11 -3
  63. package/Runtime/Core/Attributes/DxBroadcastMessageAttribute.cs.meta +11 -3
  64. package/Runtime/Core/Attributes/DxIgnoreMissingBaseCallAttribute.cs +26 -0
  65. package/Runtime/Core/Attributes/DxIgnoreMissingBaseCallAttribute.cs.meta +11 -0
  66. package/Runtime/Core/Attributes/DxOptionalParameterAttribute.cs +2 -4
  67. package/Runtime/Core/Attributes/DxOptionalParameterAttribute.cs.meta +11 -3
  68. package/Runtime/Core/Attributes/DxTargetedMessageAttribute.cs.meta +11 -3
  69. package/Runtime/Core/Attributes/DxUntargetedMessageAttribute.cs.meta +11 -3
  70. package/Runtime/Core/Attributes/Il2CppSetOptionAttribute.cs +56 -0
  71. package/Runtime/Core/Attributes/Il2CppSetOptionAttribute.cs.meta +11 -0
  72. package/Runtime/Core/Attributes.meta +2 -2
  73. package/Runtime/Core/Configuration/DxMessagingRuntimeSettings.cs +195 -0
  74. package/Runtime/Core/Configuration/DxMessagingRuntimeSettings.cs.meta +11 -0
  75. package/Runtime/Core/Configuration/DxMessagingRuntimeSettingsProvider.cs +179 -0
  76. package/Runtime/Core/Configuration/DxMessagingRuntimeSettingsProvider.cs.meta +11 -0
  77. package/Runtime/Core/Configuration.meta +9 -0
  78. package/Runtime/Core/DataStructure/CyclicBuffer.cs +46 -28
  79. package/Runtime/Core/DataStructure/CyclicBuffer.cs.meta +11 -3
  80. package/Runtime/Core/DataStructure.meta +2 -2
  81. package/Runtime/Core/Diagnostics/MessageEmissionData.cs.meta +11 -3
  82. package/Runtime/Core/Diagnostics/MessageRegistrationData.cs.meta +11 -3
  83. package/Runtime/Core/Diagnostics/MessageRegistrationType.cs.meta +11 -3
  84. package/Runtime/Core/Diagnostics.meta +2 -2
  85. package/Runtime/Core/DxMessagingStaticState.cs +19 -0
  86. package/Runtime/Core/DxMessagingStaticState.cs.meta +11 -11
  87. package/Runtime/Core/Extensions/EnumExtensions.cs +6 -5
  88. package/Runtime/Core/Extensions/EnumExtensions.cs.meta +11 -3
  89. package/Runtime/Core/Extensions/IListExtensions.cs.meta +11 -3
  90. package/Runtime/Core/Extensions/MessageBusExtensions.cs.meta +12 -12
  91. package/Runtime/Core/Extensions/MessageExtensions.cs +0 -60
  92. package/Runtime/Core/Extensions/MessageExtensions.cs.meta +11 -11
  93. package/Runtime/Core/Extensions.meta +8 -8
  94. package/Runtime/Core/Helper/MessageCache.cs +32 -0
  95. package/Runtime/Core/Helper/MessageCache.cs.meta +11 -3
  96. package/Runtime/Core/Helper/MessageHelperIndexer.cs.meta +11 -3
  97. package/Runtime/Core/Helper.meta +2 -2
  98. package/Runtime/Core/IMessage.cs +3 -3
  99. package/Runtime/Core/IMessage.cs.meta +11 -11
  100. package/Runtime/Core/InstanceId.cs +25 -1
  101. package/Runtime/Core/InstanceId.cs.meta +11 -11
  102. package/Runtime/Core/Internal/DxUnsafe.cs +60 -0
  103. package/Runtime/Core/Internal/DxUnsafe.cs.meta +11 -0
  104. package/Runtime/Core/Internal/FlatDispatch.cs +198 -0
  105. package/Runtime/Core/Internal/FlatDispatch.cs.meta +11 -0
  106. package/Runtime/Core/Internal/TypedGlobalSlotIndex.cs +38 -0
  107. package/Runtime/Core/Internal/TypedGlobalSlotIndex.cs.meta +11 -0
  108. package/Runtime/Core/Internal/TypedSlotIndex.cs +81 -0
  109. package/Runtime/Core/Internal/TypedSlotIndex.cs.meta +11 -0
  110. package/Runtime/Core/Internal/TypedSlots.cs +597 -0
  111. package/Runtime/Core/Internal/TypedSlots.cs.meta +11 -0
  112. package/Runtime/Core/Internal.meta +9 -0
  113. package/Runtime/Core/MessageBus/DiagnosticsTarget.cs.meta +11 -11
  114. package/Runtime/Core/MessageBus/GlobalMessageBusProvider.cs.meta +11 -11
  115. package/Runtime/Core/MessageBus/IMessageBus.cs +189 -15
  116. package/Runtime/Core/MessageBus/IMessageBus.cs.meta +11 -11
  117. package/Runtime/Core/MessageBus/IMessageBusProvider.cs.meta +11 -11
  118. package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs +1 -0
  119. package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs.meta +11 -11
  120. package/Runtime/Core/MessageBus/Internal/BusContextIndex.cs +16 -0
  121. package/Runtime/Core/MessageBus/Internal/BusContextIndex.cs.meta +11 -0
  122. package/Runtime/Core/MessageBus/Internal/BusSinkIndex.cs +40 -0
  123. package/Runtime/Core/MessageBus/Internal/BusSinkIndex.cs.meta +11 -0
  124. package/Runtime/Core/MessageBus/Internal/BusSlots.cs +719 -0
  125. package/Runtime/Core/MessageBus/Internal/BusSlots.cs.meta +11 -0
  126. package/Runtime/Core/MessageBus/Internal/DispatchKind.cs +38 -0
  127. package/Runtime/Core/MessageBus/Internal/DispatchKind.cs.meta +11 -0
  128. package/Runtime/Core/MessageBus/Internal/DispatchPhase.cs +20 -0
  129. package/Runtime/Core/MessageBus/Internal/DispatchPhase.cs.meta +11 -0
  130. package/Runtime/Core/MessageBus/Internal/DispatchVariant.cs +28 -0
  131. package/Runtime/Core/MessageBus/Internal/DispatchVariant.cs.meta +11 -0
  132. package/Runtime/Core/MessageBus/Internal/IEvictableSlot.cs +48 -0
  133. package/Runtime/Core/MessageBus/Internal/IEvictableSlot.cs.meta +11 -0
  134. package/Runtime/Core/MessageBus/Internal/ISweepable.cs +15 -0
  135. package/Runtime/Core/MessageBus/Internal/ISweepable.cs.meta +11 -0
  136. package/Runtime/Core/MessageBus/Internal/RegistrationMethodAxes.cs +222 -0
  137. package/Runtime/Core/MessageBus/Internal/RegistrationMethodAxes.cs.meta +11 -0
  138. package/Runtime/Core/MessageBus/Internal/SlotKey.cs +192 -0
  139. package/Runtime/Core/MessageBus/Internal/SlotKey.cs.meta +11 -0
  140. package/Runtime/Core/MessageBus/Internal.meta +9 -0
  141. package/Runtime/Core/MessageBus/MessageBus.cs +5366 -3838
  142. package/Runtime/Core/MessageBus/MessageBus.cs.meta +11 -11
  143. package/Runtime/Core/MessageBus/MessageBusRebindMode.cs.meta +11 -11
  144. package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs +187 -14
  145. package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs.meta +11 -11
  146. package/Runtime/Core/MessageBus/MessagingRegistration.cs.meta +11 -11
  147. package/Runtime/Core/MessageBus/RegistrationLog.cs.meta +11 -11
  148. package/Runtime/Core/MessageBus.meta +8 -8
  149. package/Runtime/Core/MessageHandler.cs +2399 -1042
  150. package/Runtime/Core/MessageHandler.cs.meta +11 -11
  151. package/Runtime/Core/MessageRegistrationHandle.cs.meta +11 -11
  152. package/Runtime/Core/MessageRegistrationToken.cs +429 -44
  153. package/Runtime/Core/MessageRegistrationToken.cs.meta +11 -11
  154. package/Runtime/Core/Messages/GlobalStringMessage.cs.meta +11 -3
  155. package/Runtime/Core/Messages/IBroadcastMessage.cs.meta +11 -11
  156. package/Runtime/Core/Messages/ITargetedMessage.cs.meta +11 -11
  157. package/Runtime/Core/Messages/IUntargetedMessage.cs.meta +11 -11
  158. package/Runtime/Core/Messages/ReflexiveMessage.cs.meta +11 -3
  159. package/Runtime/Core/Messages/SourcedStringMessage.cs.meta +11 -11
  160. package/Runtime/Core/Messages/StringMessage.cs.meta +11 -3
  161. package/Runtime/Core/Messages.meta +8 -8
  162. package/Runtime/Core/MessagingDebug.cs.meta +11 -11
  163. package/Runtime/Core/Pooling/CollectionPool.cs +266 -0
  164. package/Runtime/Core/Pooling/CollectionPool.cs.meta +11 -0
  165. package/Runtime/Core/Pooling/CollectionPoolDiagnostics.cs +30 -0
  166. package/Runtime/Core/Pooling/CollectionPoolDiagnostics.cs.meta +11 -0
  167. package/Runtime/Core/Pooling/DxPools.cs +157 -0
  168. package/Runtime/Core/Pooling/DxPools.cs.meta +11 -0
  169. package/Runtime/Core/Pooling/EvictionPlayerLoopHook.cs +106 -0
  170. package/Runtime/Core/Pooling/EvictionPlayerLoopHook.cs.meta +11 -0
  171. package/Runtime/Core/Pooling/IDxMessagingClock.cs +18 -0
  172. package/Runtime/Core/Pooling/IDxMessagingClock.cs.meta +11 -0
  173. package/Runtime/Core/Pooling/PoolDiagnosticsSnapshot.cs +55 -0
  174. package/Runtime/Core/Pooling/PoolDiagnosticsSnapshot.cs.meta +11 -0
  175. package/Runtime/Core/Pooling/StopwatchClock.cs +27 -0
  176. package/Runtime/Core/Pooling/StopwatchClock.cs.meta +11 -0
  177. package/Runtime/Core/Pooling/UnityRealtimeClock.cs +31 -0
  178. package/Runtime/Core/Pooling/UnityRealtimeClock.cs.meta +11 -0
  179. package/Runtime/Core/Pooling.meta +9 -0
  180. package/Runtime/Core.meta +8 -8
  181. package/Runtime/Unity/CurrentGlobalMessageBusProvider.cs.meta +12 -12
  182. package/Runtime/Unity/DxMessagingRuntimeInitializer.cs.meta +11 -11
  183. package/Runtime/Unity/InitialGlobalMessageBusProvider.cs.meta +12 -12
  184. package/Runtime/Unity/Integrations/Reflex/AssemblyInfo.cs.meta +11 -3
  185. package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs +73 -0
  186. package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs.meta +11 -11
  187. package/Runtime/Unity/Integrations/Reflex/WallstopStudios.DxMessaging.Reflex.asmdef +20 -20
  188. package/Runtime/Unity/Integrations/Reflex/WallstopStudios.DxMessaging.Reflex.asmdef.meta +7 -7
  189. package/Runtime/Unity/Integrations/Reflex.meta +8 -8
  190. package/Runtime/Unity/Integrations/VContainer/AssemblyInfo.cs.meta +11 -3
  191. package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs +109 -1
  192. package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs.meta +11 -11
  193. package/Runtime/Unity/Integrations/VContainer/WallstopStudios.DxMessaging.VContainer.asmdef +30 -30
  194. package/Runtime/Unity/Integrations/VContainer/WallstopStudios.DxMessaging.VContainer.asmdef.meta +7 -7
  195. package/Runtime/Unity/Integrations/VContainer.meta +8 -8
  196. package/Runtime/Unity/Integrations/Zenject/AssemblyInfo.cs.meta +11 -3
  197. package/Runtime/Unity/Integrations/Zenject/WallstopStudios.DxMessaging.Zenject.asmdef +30 -30
  198. package/Runtime/Unity/Integrations/Zenject/WallstopStudios.DxMessaging.Zenject.asmdef.meta +7 -7
  199. package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs +79 -1
  200. package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs.meta +11 -11
  201. package/Runtime/Unity/Integrations/Zenject.meta +8 -8
  202. package/Runtime/Unity/Integrations.meta +8 -8
  203. package/Runtime/Unity/MessageAwareComponent.cs +74 -0
  204. package/Runtime/Unity/MessageAwareComponent.cs.meta +11 -11
  205. package/Runtime/Unity/MessageBusProviderHandle.cs.meta +12 -12
  206. package/Runtime/Unity/MessagingComponent.cs +43 -10
  207. package/Runtime/Unity/MessagingComponent.cs.meta +11 -11
  208. package/Runtime/Unity/MessagingComponentInstaller.cs.meta +12 -12
  209. package/Runtime/Unity/ScriptableMessageBusProvider.cs.meta +12 -12
  210. package/Runtime/Unity.meta +8 -8
  211. package/Runtime/WallstopStudios.DxMessaging.asmdef +14 -14
  212. package/Runtime/WallstopStudios.DxMessaging.asmdef.meta +7 -7
  213. package/Runtime.meta +8 -8
  214. package/Samples~/DI/Prefabs/MessagingInstallerSample.prefab +98 -98
  215. package/Samples~/DI/Prefabs/MessagingInstallerSample.prefab.meta +7 -7
  216. package/Samples~/DI/Prefabs.meta +8 -8
  217. package/Samples~/DI/Providers/GlobalMessageBusProvider.asset +14 -14
  218. package/Samples~/DI/Providers/GlobalMessageBusProvider.asset.meta +8 -8
  219. package/Samples~/DI/Providers/InitialGlobalMessageBusProvider.asset +14 -14
  220. package/Samples~/DI/Providers/InitialGlobalMessageBusProvider.asset.meta +8 -8
  221. package/Samples~/DI/Providers.meta +8 -8
  222. package/Samples~/DI/README.md +51 -51
  223. package/Samples~/DI/README.md.meta +7 -7
  224. package/Samples~/DI/Reflex/SampleInstaller.cs +7 -0
  225. package/Samples~/DI/Reflex/SampleInstaller.cs.meta +11 -11
  226. package/Samples~/DI/Reflex.meta +8 -8
  227. package/Samples~/DI/VContainer/SampleLifetimeScope.cs +6 -1
  228. package/Samples~/DI/VContainer/SampleLifetimeScope.cs.meta +11 -11
  229. package/Samples~/DI/VContainer.meta +8 -8
  230. package/Samples~/DI/Zenject/SampleInstaller.cs +8 -0
  231. package/Samples~/DI/Zenject/SampleInstaller.cs.meta +11 -11
  232. package/Samples~/DI/Zenject.meta +8 -8
  233. package/Samples~/DI.meta +8 -8
  234. package/Samples~/Mini Combat/Boot.cs.meta +11 -11
  235. package/Samples~/Mini Combat/Enemy.cs.meta +11 -11
  236. package/Samples~/Mini Combat/Messages.cs.meta +11 -11
  237. package/Samples~/Mini Combat/Player.cs.meta +11 -11
  238. package/Samples~/Mini Combat/README.md +324 -323
  239. package/Samples~/Mini Combat/README.md.meta +7 -7
  240. package/Samples~/Mini Combat/UIOverlay.cs.meta +11 -11
  241. package/Samples~/Mini Combat/Walkthrough.md +430 -430
  242. package/Samples~/Mini Combat/Walkthrough.md.meta +7 -7
  243. package/Samples~/Mini Combat/WallstopStudios.DxMessaging.MiniCombat.Sample.asmdef +13 -13
  244. package/Samples~/Mini Combat/WallstopStudios.DxMessaging.MiniCombat.Sample.asmdef.meta +7 -7
  245. package/Samples~/Mini Combat.meta +8 -8
  246. package/Samples~/UI Buttons + Inspector/DiagnosticsEnabler.cs.meta +11 -11
  247. package/Samples~/UI Buttons + Inspector/Messages.cs.meta +11 -11
  248. package/Samples~/UI Buttons + Inspector/MessagingObserver.cs.meta +11 -11
  249. package/Samples~/UI Buttons + Inspector/README.md +210 -209
  250. package/Samples~/UI Buttons + Inspector/README.md.meta +7 -7
  251. package/Samples~/UI Buttons + Inspector/UIButtonEmitter.cs.meta +11 -11
  252. package/Samples~/UI Buttons + Inspector/WallstopStudios.DxMessaging.UIButtons.Sample.asmdef +13 -13
  253. package/Samples~/UI Buttons + Inspector/WallstopStudios.DxMessaging.UIButtons.Sample.asmdef.meta +7 -7
  254. package/Samples~/UI Buttons + Inspector.meta +8 -8
  255. package/SourceGenerators/Directory.Build.props +50 -3
  256. package/SourceGenerators/Directory.Build.props.meta +7 -7
  257. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxAutoConstructorGenerator.cs +96 -63
  258. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxAutoConstructorGenerator.cs.meta +11 -11
  259. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxMessageIdGenerator.cs +745 -87
  260. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxMessageIdGenerator.cs.meta +11 -3
  261. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.csproj +39 -46
  262. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.csproj.meta +7 -7
  263. package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.meta +8 -8
  264. package/SourceGenerators/global.json +7 -0
  265. package/SourceGenerators/global.json.meta +7 -0
  266. package/SourceGenerators.meta +8 -8
  267. package/Third Party Notices.md +3 -3
  268. package/Third Party Notices.md.meta +7 -7
  269. package/package.json +102 -92
  270. package/package.json.meta +7 -7
@@ -0,0 +1,277 @@
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.Reflection;
9
+ using System.Reflection.Emit;
10
+
11
+ /// <summary>
12
+ /// Pure (Unity-API-free) IL inspector that decides whether a given <see cref="MethodInfo"/>'s
13
+ /// IL body invokes a parent's same-named method (i.e. the IL emit shape of <c>base.X()</c>).
14
+ /// Extracted from <c>BaseCallTypeScanner</c> so the dotnet-test project can cover the byte
15
+ /// walker without depending on Unity APIs.
16
+ /// </summary>
17
+ /// <remarks>
18
+ /// <para>
19
+ /// <b>Why does this exist?</b> The console-scrape harvester is non-deterministic across Unity
20
+ /// 2021 cache hits (Unity skips routing analyzer warnings to <c>LogEntries</c> /
21
+ /// <c>CompilerMessage[]</c> on incremental compiles where Bee/csc reused a cached output).
22
+ /// IL reflection over the loaded assemblies in the AppDomain is deterministic -- the bytes do
23
+ /// not depend on Unity's compile-pipeline state. <see cref="BaseCallTypeScanner"/> uses this
24
+ /// helper to classify every loaded <c>MessageAwareComponent</c> subclass on every domain
25
+ /// reload.
26
+ /// </para>
27
+ /// <para>
28
+ /// <b>Proper OpCodes-table walker.</b> The walker decodes every CIL instruction by looking up
29
+ /// its <see cref="OpCode"/> in the static tables built from <see cref="OpCodes"/> reflection
30
+ /// (single-byte and two-byte 0xFE-prefix forms) and steps the operand-size that the opcode
31
+ /// declares (<see cref="OpCode.OperandType"/>). Misalignment past multi-byte-operand opcodes
32
+ /// (<c>switch</c> jump tables, <c>ldstr</c> 4-byte tokens, 8-byte literal constants, etc.) is
33
+ /// therefore impossible -- the walker either consumes every byte correctly or stops at the
34
+ /// first unrecognised opcode. Phantom DXMSG006 from a misread <c>0x28</c> inside a wider
35
+ /// operand is no longer a failure mode.
36
+ /// </para>
37
+ /// <para>
38
+ /// <b>Defensive bias.</b> When we cannot reason at all (null method, empty name, inaccessible
39
+ /// IL body, <c>GetMethodBody()</c> returns null on abstract / P/Invoke / IL2CPP-stripped
40
+ /// targets, or any reflection exception), the inspector returns <c>true</c>
41
+ /// ("assume clean -- calls base") so the scanner never invents a phantom warning. The
42
+ /// compile-time analyzer is the authoritative source for CI builds (DXMSG006/007/009/010 via
43
+ /// full Roslyn semantic-model precision); the IL scanner exists only to make the editor
44
+ /// overlay light up at edit-time, where a missed warning is far worse than a phantom one.
45
+ /// </para>
46
+ /// </remarks>
47
+ public static class BaseCallIlInspector
48
+ {
49
+ // CIL opcode tables, indexed by the low byte of OpCode.Value. Built once by reflecting over
50
+ // System.Reflection.Emit.OpCodes; every public static OpCode field there represents a
51
+ // canonical CIL instruction. The two-byte form of the table is used when a 0xFE prefix is
52
+ // observed in the IL stream; otherwise we use the single-byte form. Because CIL specifies
53
+ // exactly two prefix bytes (single-byte = direct, two-byte = 0xFE prefix), this division
54
+ // covers every defined opcode.
55
+ private static readonly OpCode[] s_singleByteOps = BuildOpCodeTable(twoByte: false);
56
+ private static readonly OpCode[] s_twoByteOps = BuildOpCodeTable(twoByte: true);
57
+
58
+ private static OpCode[] BuildOpCodeTable(bool twoByte)
59
+ {
60
+ OpCode[] table = new OpCode[256];
61
+ foreach (
62
+ FieldInfo field in typeof(OpCodes).GetFields(
63
+ BindingFlags.Public | BindingFlags.Static
64
+ )
65
+ )
66
+ {
67
+ if (field.GetValue(null) is not OpCode op)
68
+ {
69
+ continue;
70
+ }
71
+ ushort value = (ushort)op.Value;
72
+ bool isTwoByte = (value & 0xFF00) == 0xFE00;
73
+ bool isSingleByte = (value & 0xFF00) == 0;
74
+ if (twoByte && isTwoByte)
75
+ {
76
+ table[value & 0xFF] = op;
77
+ }
78
+ else if (!twoByte && isSingleByte)
79
+ {
80
+ table[value & 0xFF] = op;
81
+ }
82
+ }
83
+ return table;
84
+ }
85
+
86
+ /// <summary>
87
+ /// Returns <c>true</c> if <paramref name="method"/>'s IL body contains a
88
+ /// <c>call</c>/<c>callvirt</c> to a parent type's same-named method. Defensive:
89
+ /// returns <c>true</c> (assume-clean) if the IL body is null/inaccessible to avoid
90
+ /// false-positive warnings on platforms or methods where reflection on bodies is
91
+ /// restricted (abstract methods, P/Invoke, IL2CPP-stripped bodies, etc.).
92
+ /// </summary>
93
+ /// <param name="method">The override on the descendant type whose IL we wish to inspect.</param>
94
+ /// <param name="methodName">The expected base method name (e.g. <c>"OnEnable"</c>).</param>
95
+ /// <returns>
96
+ /// <c>true</c> if the IL contains a base-call shape, OR the IL was inaccessible (safe
97
+ /// default -- assume clean). <c>false</c> only when IL was readable AND no call/callvirt
98
+ /// targeting a parent same-named method was found.
99
+ /// </returns>
100
+ public static bool MethodIlContainsBaseCall(MethodInfo method, string methodName)
101
+ {
102
+ if (method == null || string.IsNullOrEmpty(methodName))
103
+ {
104
+ // Defensive: treat as clean when we don't have enough information to reason. This
105
+ // ensures the scanner never emits a phantom warning on a degenerate input.
106
+ return true;
107
+ }
108
+
109
+ try
110
+ {
111
+ MethodBody body = method.GetMethodBody();
112
+ if (body == null)
113
+ {
114
+ // Abstract / extern / runtime-implemented / IL2CPP-stripped; cannot inspect.
115
+ return true;
116
+ }
117
+
118
+ byte[] il = body.GetILAsByteArray();
119
+ if (il == null || il.Length == 0)
120
+ {
121
+ return true;
122
+ }
123
+
124
+ Module module = method.Module;
125
+ Type[] genericTypeArgs =
126
+ method.DeclaringType?.IsGenericType == true
127
+ ? method.DeclaringType.GetGenericArguments()
128
+ : null;
129
+ Type[] genericMethodArgs = method.IsGenericMethod
130
+ ? method.GetGenericArguments()
131
+ : null;
132
+
133
+ int i = 0;
134
+ while (i < il.Length)
135
+ {
136
+ OpCode op;
137
+ if (il[i] == 0xFE)
138
+ {
139
+ // Two-byte (0xFE-prefixed) opcode. Without a following byte we cannot
140
+ // decode the instruction; bail out conservatively. Truncated IL is not a
141
+ // shape Roslyn ever emits, so reaching this path means we mis-stepped and
142
+ // the safest answer is the assume-clean default.
143
+ if (i + 1 >= il.Length)
144
+ {
145
+ return true;
146
+ }
147
+ op = s_twoByteOps[il[i + 1]];
148
+ i += 2;
149
+ }
150
+ else
151
+ {
152
+ op = s_singleByteOps[il[i]];
153
+ i += 1;
154
+ }
155
+
156
+ // Unrecognised opcode (zero-initialised slot in the table); abandon the walk
157
+ // rather than risk the rest of the stream getting misread. Returning the
158
+ // assume-clean default keeps the scanner from inventing a phantom warning.
159
+ if (op.Size == 0)
160
+ {
161
+ return true;
162
+ }
163
+
164
+ if (op == OpCodes.Call || op == OpCodes.Callvirt)
165
+ {
166
+ if (i + 4 > il.Length)
167
+ {
168
+ return true;
169
+ }
170
+ int token = BitConverter.ToInt32(il, i);
171
+ try
172
+ {
173
+ MethodBase target = module.ResolveMethod(
174
+ token,
175
+ genericTypeArgs,
176
+ genericMethodArgs
177
+ );
178
+ if (
179
+ target != null
180
+ && string.Equals(target.Name, methodName, StringComparison.Ordinal)
181
+ )
182
+ {
183
+ Type declaring = method.DeclaringType;
184
+ Type resolved = target.DeclaringType;
185
+ // Guard against false-positives: the resolved method must live on a
186
+ // STRICT base type of the declaring class (not the declaring class
187
+ // itself, not a sibling, not a generic-arg shadow). IsAssignableFrom
188
+ // checks "is `declaring` assignable TO `resolved`"; i.e. is
189
+ // `resolved` an ancestor of `declaring`.
190
+ if (
191
+ declaring != null
192
+ && resolved != null
193
+ && declaring != resolved
194
+ && resolved.IsAssignableFrom(declaring)
195
+ )
196
+ {
197
+ return true;
198
+ }
199
+ }
200
+ }
201
+ catch
202
+ {
203
+ // ResolveMethod throws on tokens that don't bind in our generic-arg
204
+ // context (e.g. a MemberRef into a closed generic we can't resolve).
205
+ // The OpCodes-table walker means we can no longer land on a misaligned
206
+ // 0x28 inside a wider operand, so this catch only protects against
207
+ // legitimate-but-unbindable tokens; we swallow and continue scanning.
208
+ }
209
+ i += 4;
210
+ continue;
211
+ }
212
+
213
+ // Step over the operand based on the opcode's declared operand type. Every
214
+ // CIL operand size is decided by OperandType, which is exactly why the table
215
+ // walker is misalignment-proof.
216
+ i += GetOperandSize(op, il, i);
217
+ }
218
+ return false;
219
+ }
220
+ catch
221
+ {
222
+ // Any reflection failure → assume clean. We never want the scanner itself to
223
+ // become the source of a phantom warning.
224
+ return true;
225
+ }
226
+ }
227
+
228
+ // Returns the number of operand bytes that follow an opcode of the given OperandType,
229
+ // given the operand-start offset (needed for InlineSwitch's variable-length jump table).
230
+ private static int GetOperandSize(OpCode op, byte[] il, int operandStart)
231
+ {
232
+ switch (op.OperandType)
233
+ {
234
+ case OperandType.InlineNone:
235
+ return 0;
236
+ case OperandType.ShortInlineBrTarget:
237
+ case OperandType.ShortInlineI:
238
+ case OperandType.ShortInlineVar:
239
+ return 1;
240
+ case OperandType.InlineVar:
241
+ return 2;
242
+ case OperandType.InlineBrTarget:
243
+ case OperandType.InlineField:
244
+ case OperandType.InlineI:
245
+ case OperandType.InlineMethod:
246
+ case OperandType.InlineSig:
247
+ case OperandType.InlineString:
248
+ case OperandType.InlineTok:
249
+ case OperandType.InlineType:
250
+ case OperandType.ShortInlineR:
251
+ return 4;
252
+ case OperandType.InlineI8:
253
+ case OperandType.InlineR:
254
+ return 8;
255
+ case OperandType.InlineSwitch:
256
+ // 4-byte case count, then N × 4-byte branch targets. Truncated stream → bail
257
+ // by consuming the rest defensively (the outer loop's bounds check then ends
258
+ // the walk).
259
+ if (operandStart + 4 > il.Length)
260
+ {
261
+ return il.Length - operandStart;
262
+ }
263
+ int caseCount = BitConverter.ToInt32(il, operandStart);
264
+ if (caseCount < 0)
265
+ {
266
+ // Negative case-count is malformed IL; bail conservatively.
267
+ return il.Length - operandStart;
268
+ }
269
+ return 4 + caseCount * 4;
270
+ default:
271
+ // Unknown OperandType; bail conservatively by consuming the rest of the
272
+ // stream so the outer loop terminates without misaligning further.
273
+ return il.Length - operandStart;
274
+ }
275
+ }
276
+ }
277
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: f713afea6aef44cab4d711a4f2d17d6e
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
@@ -0,0 +1,295 @@
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.Globalization;
10
+ using System.Text.RegularExpressions;
11
+
12
+ /// <summary>
13
+ /// Single parsed diagnostic line emitted by the <c>MessageAwareComponentBaseCallAnalyzer</c>.
14
+ /// </summary>
15
+ /// <remarks>
16
+ /// Empty <see cref="MethodName"/> is the documented sentinel for DXMSG008 (which carries no
17
+ /// method name in its message format). Empty <see cref="FilePath"/> / zero <see cref="Line"/>
18
+ /// indicate the bare (Unity-prefix-less) form of the message.
19
+ /// </remarks>
20
+ public readonly struct ParsedEntry
21
+ {
22
+ public string DiagnosticId { get; }
23
+
24
+ public string TypeFullName { get; }
25
+
26
+ public string MethodName { get; }
27
+
28
+ public string FilePath { get; }
29
+
30
+ public int Line { get; }
31
+
32
+ public ParsedEntry(
33
+ string diagnosticId,
34
+ string typeFullName,
35
+ string methodName,
36
+ string filePath,
37
+ int line
38
+ )
39
+ {
40
+ DiagnosticId = diagnosticId;
41
+ TypeFullName = typeFullName;
42
+ MethodName = methodName;
43
+ FilePath = filePath;
44
+ Line = line;
45
+ }
46
+ }
47
+
48
+ /// <summary>
49
+ /// Per-type aggregate produced by <see cref="BaseCallLogMessageParser.Aggregate"/>.
50
+ /// </summary>
51
+ /// <remarks>
52
+ /// <see cref="MissingBaseFor"/> is deduplicated and stored in deterministic ordinal-sorted order
53
+ /// via <see cref="SortedSet{T}"/>. <see cref="DiagnosticIds"/> uses ordinal comparison.
54
+ /// <see cref="FilePath"/> / <see cref="Line"/> hold the FIRST seen non-empty values so the
55
+ /// inspector overlay's "Open Script" jump remains stable across repeated parses.
56
+ /// </remarks>
57
+ public sealed class ParsedTypeReport
58
+ {
59
+ public string TypeFullName { get; set; }
60
+
61
+ public SortedSet<string> MissingBaseFor { get; } = new(StringComparer.Ordinal);
62
+
63
+ public HashSet<string> DiagnosticIds { get; } = new(StringComparer.Ordinal);
64
+
65
+ public string FilePath { get; set; }
66
+
67
+ public int Line { get; set; }
68
+ }
69
+
70
+ /// <summary>
71
+ /// Pure parser for the DXMSG006/DXMSG007/DXMSG008/DXMSG009/DXMSG010 console-log lines emitted
72
+ /// by the <c>MessageAwareComponentBaseCallAnalyzer</c>.
73
+ /// </summary>
74
+ /// <remarks>
75
+ /// <para>
76
+ /// This class lives in the Editor assembly so the harvester can call it directly without
77
+ /// referencing the Roslyn-analyzer DLL (Unity excludes <c>RoslynAnalyzer</c>-labelled
78
+ /// assemblies from asmdef compile-time references).
79
+ /// </para>
80
+ /// <para>
81
+ /// The analyzer-tests project compiles its own copy of this file via a
82
+ /// <c>&lt;Compile Include="...\BaseCallLogMessageParser.cs" Link="..." /&gt;</c> so the
83
+ /// existing dotnet-test coverage continues to run.
84
+ /// </para>
85
+ /// <para>
86
+ /// The regexes are pinned to the analyzer's verbatim message-format strings. Whenever those
87
+ /// formats change, both this parser AND <c>BaseCallLogMessageParserTests</c> must be updated
88
+ /// in lockstep.
89
+ /// </para>
90
+ /// </remarks>
91
+ public static class BaseCallLogMessageParser
92
+ {
93
+ // Roslyn / Unity-style location prefix: path(line,col): warning DXMSG006:
94
+ // We don't anchor to the diagnostic id here beyond the leading "DXMSG"; that lets the
95
+ // same prefix regex serve all five diagnostics (DXMSG006/007/008/009/010). The trailing
96
+ // `: ` is consumed so the diagnostic-specific regexes only see the message body.
97
+ private const RegexOptions SharedOptions =
98
+ RegexOptions.Compiled | RegexOptions.CultureInvariant;
99
+
100
+ private static readonly Regex PrefixRegex = new(
101
+ @"^(?<path>[^()\r\n]+?)\((?<line>\d+),(?<col>\d+)\)\s*:\s*(?:warning|error|info|hidden|message)\s+DXMSG0(?:0[6789]|10)\s*:\s*",
102
+ SharedOptions
103
+ );
104
+
105
+ // DXMSG006 format:
106
+ // '{type}' overrides MessageAwareComponent.{method} but does not call base.{method}();
107
+ // the messaging system may not function correctly on this component.
108
+ // The type name is captured from the first single-quoted token; the method from the first
109
+ // `MessageAwareComponent.{method}` occurrence (the format string repeats `{method}`).
110
+ // Body regexes anchor to the start of the body (^) so a Debug.Log payload that *contains*
111
+ // the analyzer's wording mid-string is not surfaced as a real DXMSG006/007/008. The prefix
112
+ // (when present) is stripped before this match, so ^ here is the start of the message body.
113
+ private static readonly Regex Dxmsg006Regex = new(
114
+ @"^'(?<type>[^']+)'\s+overrides\s+MessageAwareComponent\.(?<method>[A-Za-z_][A-Za-z0-9_]*)\s+but\s+does\s+not\s+call\s+base\.[A-Za-z_][A-Za-z0-9_]*\(\)\s*;\s*the\s+messaging\s+system\s+may\s+not\s+function\s+correctly\s+on\s+this\s+component\.",
115
+ SharedOptions
116
+ );
117
+
118
+ // DXMSG007 format:
119
+ // '{type}' hides MessageAwareComponent.{method} with 'new'; replace with 'override' and
120
+ // call base.{method}() so the messaging system continues to function.
121
+ private static readonly Regex Dxmsg007Regex = new(
122
+ @"^'(?<type>[^']+)'\s+hides\s+MessageAwareComponent\.(?<method>[A-Za-z_][A-Za-z0-9_]*)\s+with\s+'new'\s*;\s*replace\s+with\s+'override'\s+and\s+call\s+base\.[A-Za-z_][A-Za-z0-9_]*\(\)\s+so\s+the\s+messaging\s+system\s+continues\s+to\s+function\.",
123
+ SharedOptions
124
+ );
125
+
126
+ // DXMSG008 format:
127
+ // '{type}' is excluded from the DxMessaging base-call check ({source}).
128
+ // No method name in the message; MethodName is returned as the empty string.
129
+ private static readonly Regex Dxmsg008Regex = new(
130
+ @"^'(?<type>[^']+)'\s+is\s+excluded\s+from\s+the\s+DxMessaging\s+base-call\s+check\s+\([^)]*\)\.",
131
+ SharedOptions
132
+ );
133
+
134
+ // DXMSG009 format:
135
+ // '{type}' declares {method} without 'override' or 'new'; this implicitly hides
136
+ // MessageAwareComponent.{method} (CS0114) and the messaging system will not function. ...
137
+ // We anchor on the head of the message and stop after the modifier-tokens phrase so future
138
+ // wording tweaks to the trailing remediation text don't break the parser.
139
+ private static readonly Regex Dxmsg009Regex = new(
140
+ @"^'(?<type>[^']+)'\s+declares\s+(?<method>[A-Za-z_][A-Za-z0-9_]*)\s+without\s+'override'\s+or\s+'new'",
141
+ SharedOptions
142
+ );
143
+
144
+ // DXMSG010 format:
145
+ // '{type}' calls base.{method}() but the inherited override on '{broken}' does not
146
+ // chain to MessageAwareComponent.{method}; the messaging system will not function
147
+ // correctly on this component.
148
+ // We capture {type} (the class the user is editing), {method}, and the broken-ancestor
149
+ // FQN so the inspector overlay can mention "broken chain via {broken}" if desired.
150
+ private static readonly Regex Dxmsg010Regex = new(
151
+ @"^'(?<type>[^']+)'\s+calls\s+base\.(?<method>[A-Za-z_][A-Za-z0-9_]*)\(\)\s+but\s+the\s+inherited\s+override\s+on\s+'(?<broken>[^']+)'",
152
+ SharedOptions
153
+ );
154
+
155
+ /// <summary>
156
+ /// Parses one console log line. Returns <c>null</c> if the line is not a recognised
157
+ /// DXMSG006/DXMSG007/DXMSG008/DXMSG009/DXMSG010 message.
158
+ /// </summary>
159
+ /// <remarks>
160
+ /// Tolerates both the Roslyn-prefixed form (<c>Path(L,C): warning DXMSG006: ...</c>) and the
161
+ /// bare form (just the message body). The prefix's path/line are captured; the bare form
162
+ /// returns empty path / zero line.
163
+ /// </remarks>
164
+ public static ParsedEntry? ParseLine(string logLine)
165
+ {
166
+ if (string.IsNullOrWhiteSpace(logLine))
167
+ {
168
+ return null;
169
+ }
170
+
171
+ string filePath = string.Empty;
172
+ int line = 0;
173
+ string body = logLine;
174
+
175
+ Match prefixMatch = PrefixRegex.Match(logLine);
176
+ if (prefixMatch.Success)
177
+ {
178
+ filePath = prefixMatch.Groups["path"].Value;
179
+ if (
180
+ !int.TryParse(
181
+ prefixMatch.Groups["line"].Value,
182
+ NumberStyles.Integer,
183
+ CultureInfo.InvariantCulture,
184
+ out line
185
+ )
186
+ )
187
+ {
188
+ line = 0;
189
+ }
190
+ body = logLine.Substring(prefixMatch.Length);
191
+ }
192
+
193
+ Match dxmsg006 = Dxmsg006Regex.Match(body);
194
+ if (dxmsg006.Success)
195
+ {
196
+ string type = dxmsg006.Groups["type"].Value;
197
+ string method = dxmsg006.Groups["method"].Value;
198
+ if (!string.IsNullOrWhiteSpace(type) && !string.IsNullOrWhiteSpace(method))
199
+ {
200
+ return new ParsedEntry("DXMSG006", type, method, filePath, line);
201
+ }
202
+ }
203
+
204
+ Match dxmsg007 = Dxmsg007Regex.Match(body);
205
+ if (dxmsg007.Success)
206
+ {
207
+ string type = dxmsg007.Groups["type"].Value;
208
+ string method = dxmsg007.Groups["method"].Value;
209
+ if (!string.IsNullOrWhiteSpace(type) && !string.IsNullOrWhiteSpace(method))
210
+ {
211
+ return new ParsedEntry("DXMSG007", type, method, filePath, line);
212
+ }
213
+ }
214
+
215
+ Match dxmsg008 = Dxmsg008Regex.Match(body);
216
+ if (dxmsg008.Success)
217
+ {
218
+ string type = dxmsg008.Groups["type"].Value;
219
+ if (!string.IsNullOrWhiteSpace(type))
220
+ {
221
+ return new ParsedEntry("DXMSG008", type, string.Empty, filePath, line);
222
+ }
223
+ }
224
+
225
+ Match dxmsg009 = Dxmsg009Regex.Match(body);
226
+ if (dxmsg009.Success)
227
+ {
228
+ string type = dxmsg009.Groups["type"].Value;
229
+ string method = dxmsg009.Groups["method"].Value;
230
+ if (!string.IsNullOrWhiteSpace(type) && !string.IsNullOrWhiteSpace(method))
231
+ {
232
+ return new ParsedEntry("DXMSG009", type, method, filePath, line);
233
+ }
234
+ }
235
+
236
+ Match dxmsg010 = Dxmsg010Regex.Match(body);
237
+ if (dxmsg010.Success)
238
+ {
239
+ string type = dxmsg010.Groups["type"].Value;
240
+ string method = dxmsg010.Groups["method"].Value;
241
+ if (!string.IsNullOrWhiteSpace(type) && !string.IsNullOrWhiteSpace(method))
242
+ {
243
+ return new ParsedEntry("DXMSG010", type, method, filePath, line);
244
+ }
245
+ }
246
+
247
+ return null;
248
+ }
249
+
250
+ /// <summary>
251
+ /// Aggregates many log lines into a per-type report keyed by FQN. Deduplicates methods and
252
+ /// diagnostic ids ordinally; method names are stored in deterministic ordinal-sorted order.
253
+ /// Preserves first-occurrence for <see cref="ParsedTypeReport.FilePath"/> /
254
+ /// <see cref="ParsedTypeReport.Line"/>.
255
+ /// </summary>
256
+ public static Dictionary<string, ParsedTypeReport> Aggregate(IEnumerable<string> logLines)
257
+ {
258
+ Dictionary<string, ParsedTypeReport> result = new(StringComparer.Ordinal);
259
+ if (logLines == null)
260
+ {
261
+ return result;
262
+ }
263
+
264
+ foreach (string logLine in logLines)
265
+ {
266
+ ParsedEntry? parsed = ParseLine(logLine);
267
+ if (parsed is not ParsedEntry entry)
268
+ {
269
+ continue;
270
+ }
271
+
272
+ if (!result.TryGetValue(entry.TypeFullName, out ParsedTypeReport report))
273
+ {
274
+ report = new ParsedTypeReport { TypeFullName = entry.TypeFullName };
275
+ result[entry.TypeFullName] = report;
276
+ }
277
+
278
+ report.DiagnosticIds.Add(entry.DiagnosticId);
279
+
280
+ if (!string.IsNullOrEmpty(entry.MethodName))
281
+ {
282
+ report.MissingBaseFor.Add(entry.MethodName);
283
+ }
284
+
285
+ if (string.IsNullOrEmpty(report.FilePath) && !string.IsNullOrEmpty(entry.FilePath))
286
+ {
287
+ report.FilePath = entry.FilePath;
288
+ report.Line = entry.Line;
289
+ }
290
+ }
291
+
292
+ return result;
293
+ }
294
+ }
295
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: a17c13ebe9604cee9bfa08d2517a3fad
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant: