com.wallstop-studios.dxmessaging 2.2.0 → 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.
- package/CHANGELOG.md +106 -67
- package/CHANGELOG.md.meta +7 -7
- package/Editor/Analyzers/BaseCallIlInspector.cs +277 -0
- package/Editor/Analyzers/BaseCallIlInspector.cs.meta +11 -0
- package/Editor/Analyzers/BaseCallLogMessageParser.cs +295 -0
- package/Editor/Analyzers/BaseCallLogMessageParser.cs.meta +11 -0
- package/Editor/Analyzers/BaseCallReportAggregator.cs +308 -0
- package/Editor/Analyzers/BaseCallReportAggregator.cs.meta +11 -0
- package/Editor/Analyzers/BaseCallTypeScanner.cs +110 -0
- package/Editor/Analyzers/BaseCallTypeScanner.cs.meta +11 -0
- package/Editor/Analyzers/BaseCallTypeScannerCore.cs +562 -0
- package/Editor/Analyzers/BaseCallTypeScannerCore.cs.meta +11 -0
- package/Editor/Analyzers/DxMessagingConsoleHarvester.cs +1122 -0
- package/Editor/Analyzers/DxMessagingConsoleHarvester.cs.meta +11 -0
- package/Editor/Analyzers/Microsoft.CodeAnalysis.CSharp.dll.meta +44 -44
- package/Editor/Analyzers/Microsoft.CodeAnalysis.dll.meta +44 -44
- package/Editor/Analyzers/System.Collections.Immutable.dll.meta +44 -44
- package/Editor/Analyzers/System.Reflection.Metadata.dll.meta +44 -44
- package/Editor/Analyzers/System.Runtime.CompilerServices.Unsafe.dll.meta +44 -44
- package/Editor/Analyzers/WallstopStudios.DxMessaging.Analyzer.dll +0 -0
- package/Editor/Analyzers/WallstopStudios.DxMessaging.Analyzer.dll.meta +33 -0
- package/Editor/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll +0 -0
- package/Editor/Analyzers/WallstopStudios.DxMessaging.SourceGenerators.dll.meta +72 -72
- package/Editor/Analyzers.meta +8 -8
- package/Editor/AssemblyInfo.cs.meta +3 -3
- package/Editor/CustomEditors/MessageAwareComponentFallbackEditor.cs +81 -0
- package/Editor/CustomEditors/MessageAwareComponentFallbackEditor.cs.meta +11 -0
- package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs +420 -0
- package/Editor/CustomEditors/MessageAwareComponentInspectorOverlay.cs.meta +11 -0
- package/Editor/CustomEditors/MessagingComponentEditor.cs +1 -1
- package/Editor/CustomEditors/MessagingComponentEditor.cs.meta +2 -2
- package/Editor/CustomEditors.meta +2 -2
- package/Editor/DxMessagingEditorInitializer.cs.meta +2 -2
- package/Editor/DxMessagingMenu.cs.meta +11 -11
- package/Editor/DxMessagingSceneBuildProcessor.cs.meta +11 -11
- package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs +190 -0
- package/Editor/Settings/DxMessagingBaseCallIgnoreSync.cs.meta +11 -0
- package/Editor/Settings/DxMessagingSettings.cs +189 -0
- package/Editor/Settings/DxMessagingSettings.cs.meta +2 -2
- package/Editor/Settings/DxMessagingSettingsProvider.cs +50 -33
- package/Editor/Settings/DxMessagingSettingsProvider.cs.meta +2 -2
- package/Editor/Settings.meta +2 -2
- package/Editor/SetupCscRsp.cs +209 -8
- package/Editor/SetupCscRsp.cs.meta +2 -2
- package/Editor/Testing/MessagingComponentEditorHarness.cs +1 -1
- package/Editor/Testing/MessagingComponentEditorHarness.cs.meta +3 -3
- package/Editor/Testing.meta +3 -3
- package/Editor/WallstopStudios.DxMessaging.Editor.asmdef +14 -14
- package/Editor/WallstopStudios.DxMessaging.Editor.asmdef.meta +7 -7
- package/Editor.meta +8 -8
- package/LICENSE.md +9 -9
- package/LICENSE.md.meta +7 -7
- package/README.md +941 -900
- package/README.md.meta +7 -7
- package/Runtime/AssemblyInfo.cs +4 -0
- package/Runtime/AssemblyInfo.cs.meta +2 -2
- package/Runtime/Core/Attributes/DxAutoConstructorAttribute.cs.meta +2 -2
- package/Runtime/Core/Attributes/DxBroadcastMessageAttribute.cs.meta +2 -2
- package/Runtime/Core/Attributes/DxIgnoreMissingBaseCallAttribute.cs +26 -0
- package/Runtime/Core/Attributes/DxIgnoreMissingBaseCallAttribute.cs.meta +11 -0
- package/Runtime/Core/Attributes/DxOptionalParameterAttribute.cs.meta +2 -2
- package/Runtime/Core/Attributes/DxTargetedMessageAttribute.cs.meta +2 -2
- package/Runtime/Core/Attributes/DxUntargetedMessageAttribute.cs.meta +2 -2
- package/Runtime/Core/Attributes.meta +2 -2
- package/Runtime/Core/Configuration/DxMessagingRuntimeSettings.cs +195 -0
- package/Runtime/Core/Configuration/DxMessagingRuntimeSettings.cs.meta +11 -0
- package/Runtime/Core/Configuration/DxMessagingRuntimeSettingsProvider.cs +179 -0
- package/Runtime/Core/Configuration/DxMessagingRuntimeSettingsProvider.cs.meta +11 -0
- package/Runtime/Core/Configuration.meta +9 -0
- package/Runtime/Core/DataStructure/CyclicBuffer.cs +2 -2
- package/Runtime/Core/DataStructure/CyclicBuffer.cs.meta +2 -2
- package/Runtime/Core/DataStructure.meta +2 -2
- package/Runtime/Core/Diagnostics/MessageEmissionData.cs.meta +2 -2
- package/Runtime/Core/Diagnostics/MessageRegistrationData.cs.meta +2 -2
- package/Runtime/Core/Diagnostics/MessageRegistrationType.cs.meta +2 -2
- package/Runtime/Core/Diagnostics.meta +2 -2
- package/Runtime/Core/DxMessagingStaticState.cs +19 -0
- package/Runtime/Core/DxMessagingStaticState.cs.meta +11 -11
- package/Runtime/Core/Extensions/EnumExtensions.cs.meta +2 -2
- package/Runtime/Core/Extensions/IListExtensions.cs.meta +2 -2
- package/Runtime/Core/Extensions/MessageBusExtensions.cs.meta +12 -12
- package/Runtime/Core/Extensions/MessageExtensions.cs.meta +11 -11
- package/Runtime/Core/Extensions.meta +8 -8
- package/Runtime/Core/Helper/MessageCache.cs +32 -0
- package/Runtime/Core/Helper/MessageCache.cs.meta +2 -2
- package/Runtime/Core/Helper/MessageHelperIndexer.cs.meta +2 -2
- package/Runtime/Core/Helper.meta +2 -2
- package/Runtime/Core/IMessage.cs +3 -3
- package/Runtime/Core/IMessage.cs.meta +11 -11
- package/Runtime/Core/InstanceId.cs.meta +11 -11
- package/Runtime/Core/Internal/TypedDispatchLinkIndex.cs +51 -0
- package/Runtime/Core/Internal/TypedDispatchLinkIndex.cs.meta +11 -0
- package/Runtime/Core/Internal/TypedGlobalSlotIndex.cs +38 -0
- package/Runtime/Core/Internal/TypedGlobalSlotIndex.cs.meta +11 -0
- package/Runtime/Core/Internal/TypedSlotIndex.cs +81 -0
- package/Runtime/Core/Internal/TypedSlotIndex.cs.meta +11 -0
- package/Runtime/Core/Internal/TypedSlots.cs +613 -0
- package/Runtime/Core/Internal/TypedSlots.cs.meta +11 -0
- package/Runtime/Core/Internal.meta +9 -0
- package/Runtime/Core/MessageBus/DiagnosticsTarget.cs.meta +11 -11
- package/Runtime/Core/MessageBus/GlobalMessageBusProvider.cs.meta +11 -11
- package/Runtime/Core/MessageBus/IMessageBus.cs +177 -3
- package/Runtime/Core/MessageBus/IMessageBus.cs.meta +11 -11
- package/Runtime/Core/MessageBus/IMessageBusProvider.cs.meta +11 -11
- package/Runtime/Core/MessageBus/IMessageRegistrationBuilder.cs.meta +11 -11
- package/Runtime/Core/MessageBus/Internal/BusContextIndex.cs +16 -0
- package/Runtime/Core/MessageBus/Internal/BusContextIndex.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/BusSinkIndex.cs +40 -0
- package/Runtime/Core/MessageBus/Internal/BusSinkIndex.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/BusSlots.cs +718 -0
- package/Runtime/Core/MessageBus/Internal/BusSlots.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/DispatchKind.cs +38 -0
- package/Runtime/Core/MessageBus/Internal/DispatchKind.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/DispatchPhase.cs +20 -0
- package/Runtime/Core/MessageBus/Internal/DispatchPhase.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/DispatchVariant.cs +28 -0
- package/Runtime/Core/MessageBus/Internal/DispatchVariant.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/IEvictableSlot.cs +48 -0
- package/Runtime/Core/MessageBus/Internal/IEvictableSlot.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/ISweepable.cs +15 -0
- package/Runtime/Core/MessageBus/Internal/ISweepable.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/RegistrationMethodAxes.cs +222 -0
- package/Runtime/Core/MessageBus/Internal/RegistrationMethodAxes.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal/SlotKey.cs +192 -0
- package/Runtime/Core/MessageBus/Internal/SlotKey.cs.meta +11 -0
- package/Runtime/Core/MessageBus/Internal.meta +9 -0
- package/Runtime/Core/MessageBus/MessageBus.cs +2651 -500
- package/Runtime/Core/MessageBus/MessageBus.cs.meta +11 -11
- package/Runtime/Core/MessageBus/MessageBusRebindMode.cs.meta +11 -11
- package/Runtime/Core/MessageBus/MessageRegistrationBuilder.cs.meta +11 -11
- package/Runtime/Core/MessageBus/MessagingRegistration.cs.meta +11 -11
- package/Runtime/Core/MessageBus/RegistrationLog.cs.meta +11 -11
- package/Runtime/Core/MessageBus.meta +8 -8
- package/Runtime/Core/MessageHandler.cs +2019 -542
- package/Runtime/Core/MessageHandler.cs.meta +11 -11
- package/Runtime/Core/MessageRegistrationHandle.cs.meta +11 -11
- package/Runtime/Core/MessageRegistrationToken.cs +7 -0
- package/Runtime/Core/MessageRegistrationToken.cs.meta +11 -11
- package/Runtime/Core/Messages/GlobalStringMessage.cs.meta +2 -2
- package/Runtime/Core/Messages/IBroadcastMessage.cs.meta +11 -11
- package/Runtime/Core/Messages/ITargetedMessage.cs.meta +11 -11
- package/Runtime/Core/Messages/IUntargetedMessage.cs.meta +11 -11
- package/Runtime/Core/Messages/ReflexiveMessage.cs.meta +2 -2
- package/Runtime/Core/Messages/SourcedStringMessage.cs.meta +11 -11
- package/Runtime/Core/Messages/StringMessage.cs.meta +2 -2
- package/Runtime/Core/Messages.meta +8 -8
- package/Runtime/Core/MessagingDebug.cs.meta +11 -11
- package/Runtime/Core/Pooling/CollectionPool.cs +266 -0
- package/Runtime/Core/Pooling/CollectionPool.cs.meta +11 -0
- package/Runtime/Core/Pooling/CollectionPoolDiagnostics.cs +30 -0
- package/Runtime/Core/Pooling/CollectionPoolDiagnostics.cs.meta +11 -0
- package/Runtime/Core/Pooling/DxPools.cs +157 -0
- package/Runtime/Core/Pooling/DxPools.cs.meta +11 -0
- package/Runtime/Core/Pooling/EvictionPlayerLoopHook.cs +106 -0
- package/Runtime/Core/Pooling/EvictionPlayerLoopHook.cs.meta +11 -0
- package/Runtime/Core/Pooling/IDxMessagingClock.cs +18 -0
- package/Runtime/Core/Pooling/IDxMessagingClock.cs.meta +11 -0
- package/Runtime/Core/Pooling/PoolDiagnosticsSnapshot.cs +55 -0
- package/Runtime/Core/Pooling/PoolDiagnosticsSnapshot.cs.meta +11 -0
- package/Runtime/Core/Pooling/StopwatchClock.cs +27 -0
- package/Runtime/Core/Pooling/StopwatchClock.cs.meta +11 -0
- package/Runtime/Core/Pooling/UnityRealtimeClock.cs +31 -0
- package/Runtime/Core/Pooling/UnityRealtimeClock.cs.meta +11 -0
- package/Runtime/Core/Pooling.meta +9 -0
- package/Runtime/Core.meta +8 -8
- package/Runtime/Unity/CurrentGlobalMessageBusProvider.cs.meta +12 -12
- package/Runtime/Unity/DxMessagingRuntimeInitializer.cs.meta +11 -11
- package/Runtime/Unity/InitialGlobalMessageBusProvider.cs.meta +12 -12
- package/Runtime/Unity/Integrations/Reflex/AssemblyInfo.cs.meta +2 -2
- package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs +73 -0
- package/Runtime/Unity/Integrations/Reflex/ReflexRegistrationInstaller.cs.meta +11 -11
- package/Runtime/Unity/Integrations/Reflex/WallstopStudios.DxMessaging.Reflex.asmdef +20 -20
- package/Runtime/Unity/Integrations/Reflex/WallstopStudios.DxMessaging.Reflex.asmdef.meta +7 -7
- package/Runtime/Unity/Integrations/Reflex.meta +8 -8
- package/Runtime/Unity/Integrations/VContainer/AssemblyInfo.cs.meta +2 -2
- package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs +109 -1
- package/Runtime/Unity/Integrations/VContainer/VContainerRegistrationExtensions.cs.meta +11 -11
- package/Runtime/Unity/Integrations/VContainer/WallstopStudios.DxMessaging.VContainer.asmdef +30 -30
- package/Runtime/Unity/Integrations/VContainer/WallstopStudios.DxMessaging.VContainer.asmdef.meta +7 -7
- package/Runtime/Unity/Integrations/VContainer.meta +8 -8
- package/Runtime/Unity/Integrations/Zenject/AssemblyInfo.cs.meta +2 -2
- package/Runtime/Unity/Integrations/Zenject/WallstopStudios.DxMessaging.Zenject.asmdef +30 -30
- package/Runtime/Unity/Integrations/Zenject/WallstopStudios.DxMessaging.Zenject.asmdef.meta +7 -7
- package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs +79 -1
- package/Runtime/Unity/Integrations/Zenject/ZenjectRegistrationInstaller.cs.meta +11 -11
- package/Runtime/Unity/Integrations/Zenject.meta +8 -8
- package/Runtime/Unity/Integrations.meta +8 -8
- package/Runtime/Unity/MessageAwareComponent.cs +29 -0
- package/Runtime/Unity/MessageAwareComponent.cs.meta +11 -11
- package/Runtime/Unity/MessageBusProviderHandle.cs.meta +12 -12
- package/Runtime/Unity/MessagingComponent.cs.meta +11 -11
- package/Runtime/Unity/MessagingComponentInstaller.cs.meta +12 -12
- package/Runtime/Unity/ScriptableMessageBusProvider.cs.meta +12 -12
- package/Runtime/Unity.meta +8 -8
- package/Runtime/WallstopStudios.DxMessaging.asmdef +14 -14
- package/Runtime/WallstopStudios.DxMessaging.asmdef.meta +7 -7
- package/Runtime.meta +8 -8
- package/Samples~/DI/Prefabs/MessagingInstallerSample.prefab +98 -98
- package/Samples~/DI/Prefabs/MessagingInstallerSample.prefab.meta +7 -7
- package/Samples~/DI/Prefabs.meta +8 -8
- package/Samples~/DI/Providers/GlobalMessageBusProvider.asset +14 -14
- package/Samples~/DI/Providers/GlobalMessageBusProvider.asset.meta +8 -8
- package/Samples~/DI/Providers/InitialGlobalMessageBusProvider.asset +14 -14
- package/Samples~/DI/Providers/InitialGlobalMessageBusProvider.asset.meta +8 -8
- package/Samples~/DI/Providers.meta +8 -8
- package/Samples~/DI/README.md +51 -51
- package/Samples~/DI/README.md.meta +7 -7
- package/Samples~/DI/Reflex/SampleInstaller.cs +7 -0
- package/Samples~/DI/Reflex/SampleInstaller.cs.meta +11 -11
- package/Samples~/DI/Reflex.meta +8 -8
- package/Samples~/DI/VContainer/SampleLifetimeScope.cs +6 -1
- package/Samples~/DI/VContainer/SampleLifetimeScope.cs.meta +11 -11
- package/Samples~/DI/VContainer.meta +8 -8
- package/Samples~/DI/Zenject/SampleInstaller.cs +8 -0
- package/Samples~/DI/Zenject/SampleInstaller.cs.meta +11 -11
- package/Samples~/DI/Zenject.meta +8 -8
- package/Samples~/DI.meta +8 -8
- package/Samples~/Mini Combat/Boot.cs.meta +11 -11
- package/Samples~/Mini Combat/Enemy.cs.meta +11 -11
- package/Samples~/Mini Combat/Messages.cs.meta +11 -11
- package/Samples~/Mini Combat/Player.cs.meta +11 -11
- package/Samples~/Mini Combat/README.md +324 -323
- package/Samples~/Mini Combat/README.md.meta +7 -7
- package/Samples~/Mini Combat/UIOverlay.cs.meta +11 -11
- package/Samples~/Mini Combat/Walkthrough.md +430 -430
- package/Samples~/Mini Combat/Walkthrough.md.meta +7 -7
- package/Samples~/Mini Combat/WallstopStudios.DxMessaging.MiniCombat.Sample.asmdef +13 -13
- package/Samples~/Mini Combat/WallstopStudios.DxMessaging.MiniCombat.Sample.asmdef.meta +7 -7
- package/Samples~/Mini Combat.meta +8 -8
- package/Samples~/UI Buttons + Inspector/DiagnosticsEnabler.cs.meta +11 -11
- package/Samples~/UI Buttons + Inspector/Messages.cs.meta +11 -11
- package/Samples~/UI Buttons + Inspector/MessagingObserver.cs.meta +11 -11
- package/Samples~/UI Buttons + Inspector/README.md +210 -209
- package/Samples~/UI Buttons + Inspector/README.md.meta +7 -7
- package/Samples~/UI Buttons + Inspector/UIButtonEmitter.cs.meta +11 -11
- package/Samples~/UI Buttons + Inspector/WallstopStudios.DxMessaging.UIButtons.Sample.asmdef +13 -13
- package/Samples~/UI Buttons + Inspector/WallstopStudios.DxMessaging.UIButtons.Sample.asmdef.meta +7 -7
- package/Samples~/UI Buttons + Inspector.meta +8 -8
- package/SourceGenerators/Directory.Build.props.meta +7 -7
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxAutoConstructorGenerator.cs.meta +11 -11
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/DxMessageIdGenerator.cs.meta +2 -2
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.csproj.meta +7 -7
- package/SourceGenerators/WallstopStudios.DxMessaging.SourceGenerators.meta +8 -8
- package/SourceGenerators.meta +8 -8
- package/Third Party Notices.md +3 -3
- package/Third Party Notices.md.meta +7 -7
- package/package.json +115 -92
- package/package.json.meta +7 -7
|
@@ -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><Compile Include="...\BaseCallLogMessageParser.cs" Link="..." /></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,308 @@
|
|
|
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
|
+
|
|
10
|
+
/// <summary>
|
|
11
|
+
/// Pure-BCL data-transfer object for an aggregated per-FQN report row. Mirrors the public
|
|
12
|
+
/// shape of <see cref="BaseCallReportEntry"/> but uses property-style PascalCase fields so it
|
|
13
|
+
/// does not depend on Unity's <c>JsonUtility</c> serialization conventions and is safely
|
|
14
|
+
/// constructible from <c>dotnet test</c>.
|
|
15
|
+
/// </summary>
|
|
16
|
+
/// <remarks>
|
|
17
|
+
/// The harvester wraps every DTO in a <see cref="BaseCallReportEntry"/> (with the lowercase
|
|
18
|
+
/// Unity-serialisable field names) before the snapshot crosses into Editor code. Keep this
|
|
19
|
+
/// shape in lock-step with that wrapper -- fields added here must also flow through to the
|
|
20
|
+
/// Unity-facing entry or the inspector overlay won't see them.
|
|
21
|
+
/// </remarks>
|
|
22
|
+
public sealed class BaseCallReportEntryDto
|
|
23
|
+
{
|
|
24
|
+
/// <summary>Fully-qualified name of the offending type (dot-form for nested types).</summary>
|
|
25
|
+
public string TypeName;
|
|
26
|
+
|
|
27
|
+
/// <summary>Method names whose overrides are missing the corresponding <c>base.*()</c> call.</summary>
|
|
28
|
+
public readonly SortedSet<string> MissingBaseFor = new(StringComparer.Ordinal);
|
|
29
|
+
|
|
30
|
+
/// <summary>Diagnostic IDs that contributed to this entry (e.g., DXMSG006/007/008/009).</summary>
|
|
31
|
+
public readonly HashSet<string> DiagnosticIds = new(StringComparer.Ordinal);
|
|
32
|
+
|
|
33
|
+
/// <summary>Source file path (best-effort) for "Open Script" actions in the inspector overlay.</summary>
|
|
34
|
+
public string FilePath;
|
|
35
|
+
|
|
36
|
+
/// <summary>1-based line number of the first relevant diagnostic, when known.</summary>
|
|
37
|
+
public int Line;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/// <summary>
|
|
41
|
+
/// Pure (Unity-API-free) aggregator for the per-assembly merge + retirement logic at the heart
|
|
42
|
+
/// of <see cref="DxMessagingConsoleHarvester"/>. Extracted so the merge contract is covered by
|
|
43
|
+
/// <c>dotnet test</c> via the linked-source pattern (see
|
|
44
|
+
/// <c>WallstopStudios.DxMessaging.SourceGenerators.Tests.csproj</c>).
|
|
45
|
+
/// </summary>
|
|
46
|
+
/// <remarks>
|
|
47
|
+
/// <para>
|
|
48
|
+
/// The harvester must own two pieces of cross-call state to do its job correctly:
|
|
49
|
+
/// </para>
|
|
50
|
+
/// <list type="number">
|
|
51
|
+
/// <item><description><c>typesByAssembly</c>: which FQNs each compiled assembly has reported.
|
|
52
|
+
/// When an assembly recompiles WITHOUT reporting a previously-seen FQN, that FQN is retired
|
|
53
|
+
/// -- the user fixed the offending base call.</description></item>
|
|
54
|
+
/// <item><description><c>mergedReports</c>: the per-FQN union of every assembly's latest
|
|
55
|
+
/// report. The final snapshot merges this with whatever the LogEntries scan yielded.</description></item>
|
|
56
|
+
/// </list>
|
|
57
|
+
/// <para>
|
|
58
|
+
/// Both maps mutate in lock-step inside <see cref="ApplyAssemblyReports"/>. Because retirement
|
|
59
|
+
/// crosses assembly boundaries (an FQN may live in two assemblies; only when neither still
|
|
60
|
+
/// reports it does it disappear from <c>mergedReports</c>), the bookkeeping is the most
|
|
61
|
+
/// failure-prone slice of the harvester. Keeping it pure makes it test-driven.
|
|
62
|
+
/// </para>
|
|
63
|
+
/// </remarks>
|
|
64
|
+
public static class BaseCallReportAggregator
|
|
65
|
+
{
|
|
66
|
+
/// <summary>
|
|
67
|
+
/// Apply a single assembly's freshly-parsed report batch. Replaces that assembly's prior
|
|
68
|
+
/// FQN attribution wholesale (so types the user fixed disappear) and rebuilds the merged
|
|
69
|
+
/// per-FQN map by unioning every assembly's latest reports.
|
|
70
|
+
/// </summary>
|
|
71
|
+
/// <param name="assemblyKey">Stable identifier for the source assembly (typically the
|
|
72
|
+
/// assembly path Unity passes via <c>CompilationPipeline.assemblyCompilationFinished</c>).</param>
|
|
73
|
+
/// <param name="latestReportsForAssembly">Reports parsed from this assembly's most recent
|
|
74
|
+
/// compilation. May be empty -- that case is the retirement path (every FQN this assembly
|
|
75
|
+
/// previously reported is dropped).</param>
|
|
76
|
+
/// <param name="typesByAssembly">Per-assembly FQN bookkeeping. Mutated in place.</param>
|
|
77
|
+
/// <param name="mergedReports">Per-FQN union across every assembly. Mutated in place;
|
|
78
|
+
/// rebuilt from scratch on every call so retirement Just Works regardless of which
|
|
79
|
+
/// assembly drops its claim.</param>
|
|
80
|
+
public static void ApplyAssemblyReports(
|
|
81
|
+
string assemblyKey,
|
|
82
|
+
IReadOnlyDictionary<string, ParsedTypeReport> latestReportsForAssembly,
|
|
83
|
+
Dictionary<string, HashSet<string>> typesByAssembly,
|
|
84
|
+
Dictionary<string, ParsedTypeReport> mergedReports
|
|
85
|
+
)
|
|
86
|
+
{
|
|
87
|
+
if (string.IsNullOrEmpty(assemblyKey))
|
|
88
|
+
{
|
|
89
|
+
throw new ArgumentException("Assembly key must be non-empty.", nameof(assemblyKey));
|
|
90
|
+
}
|
|
91
|
+
if (typesByAssembly is null)
|
|
92
|
+
{
|
|
93
|
+
throw new ArgumentNullException(nameof(typesByAssembly));
|
|
94
|
+
}
|
|
95
|
+
if (mergedReports is null)
|
|
96
|
+
{
|
|
97
|
+
throw new ArgumentNullException(nameof(mergedReports));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 1. Replace this assembly's FQN set with the latest batch. Types absent from the new
|
|
101
|
+
// batch are dropped from the assembly's row; that's the per-assembly retirement.
|
|
102
|
+
if (!typesByAssembly.TryGetValue(assemblyKey, out HashSet<string> typeSet))
|
|
103
|
+
{
|
|
104
|
+
typeSet = new HashSet<string>(StringComparer.Ordinal);
|
|
105
|
+
typesByAssembly[assemblyKey] = typeSet;
|
|
106
|
+
}
|
|
107
|
+
typeSet.Clear();
|
|
108
|
+
if (latestReportsForAssembly is not null)
|
|
109
|
+
{
|
|
110
|
+
foreach (string fqn in latestReportsForAssembly.Keys)
|
|
111
|
+
{
|
|
112
|
+
if (!string.IsNullOrEmpty(fqn))
|
|
113
|
+
{
|
|
114
|
+
typeSet.Add(fqn);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 2. Rebuild mergedReports from the per-assembly view. We can't simply remove "the
|
|
120
|
+
// types this assembly retired" because another assembly may still report them; the
|
|
121
|
+
// only correct algorithm is to start fresh from typesByAssembly + the freshest
|
|
122
|
+
// payload for each (assembly, FQN) pair.
|
|
123
|
+
//
|
|
124
|
+
// Per-FQN merge semantics:
|
|
125
|
+
// - Method list: union, deduplicated ordinally, stored in deterministic
|
|
126
|
+
// ordinal-sorted order.
|
|
127
|
+
// - Diagnostic IDs: union via HashSet.
|
|
128
|
+
// - File path / line: first non-empty wins (stable across recompiles).
|
|
129
|
+
Dictionary<string, ParsedTypeReport> rebuilt = new(StringComparer.Ordinal);
|
|
130
|
+
foreach (KeyValuePair<string, HashSet<string>> assemblyEntry in typesByAssembly)
|
|
131
|
+
{
|
|
132
|
+
string thisAssemblyKey = assemblyEntry.Key;
|
|
133
|
+
HashSet<string> fqns = assemblyEntry.Value;
|
|
134
|
+
if (fqns is null || fqns.Count == 0)
|
|
135
|
+
{
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// For the assembly we just updated, prefer the freshly-parsed payload. For other
|
|
140
|
+
// assemblies, we need the previous merge to still carry their data; but that
|
|
141
|
+
// information is only retrievable from the OUTGOING mergedReports, so we read it
|
|
142
|
+
// before clearing.
|
|
143
|
+
IReadOnlyDictionary<string, ParsedTypeReport> source = string.Equals(
|
|
144
|
+
thisAssemblyKey,
|
|
145
|
+
assemblyKey,
|
|
146
|
+
StringComparison.OrdinalIgnoreCase
|
|
147
|
+
)
|
|
148
|
+
? latestReportsForAssembly
|
|
149
|
+
: mergedReports;
|
|
150
|
+
if (source is null)
|
|
151
|
+
{
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
foreach (string fqn in fqns)
|
|
156
|
+
{
|
|
157
|
+
if (!source.TryGetValue(fqn, out ParsedTypeReport contribution))
|
|
158
|
+
{
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (contribution is null)
|
|
162
|
+
{
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (!rebuilt.TryGetValue(fqn, out ParsedTypeReport existing))
|
|
167
|
+
{
|
|
168
|
+
// Defensive copy so future ApplyAssemblyReports calls don't mutate state
|
|
169
|
+
// that callers may still hold a reference to.
|
|
170
|
+
existing = new ParsedTypeReport
|
|
171
|
+
{
|
|
172
|
+
TypeFullName = contribution.TypeFullName,
|
|
173
|
+
FilePath = contribution.FilePath,
|
|
174
|
+
Line = contribution.Line,
|
|
175
|
+
};
|
|
176
|
+
foreach (string method in contribution.MissingBaseFor)
|
|
177
|
+
{
|
|
178
|
+
if (!string.IsNullOrEmpty(method))
|
|
179
|
+
{
|
|
180
|
+
existing.MissingBaseFor.Add(method);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
foreach (string id in contribution.DiagnosticIds)
|
|
184
|
+
{
|
|
185
|
+
existing.DiagnosticIds.Add(id);
|
|
186
|
+
}
|
|
187
|
+
rebuilt[fqn] = existing;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
foreach (string method in contribution.MissingBaseFor)
|
|
192
|
+
{
|
|
193
|
+
if (!string.IsNullOrEmpty(method))
|
|
194
|
+
{
|
|
195
|
+
existing.MissingBaseFor.Add(method);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
foreach (string id in contribution.DiagnosticIds)
|
|
199
|
+
{
|
|
200
|
+
existing.DiagnosticIds.Add(id);
|
|
201
|
+
}
|
|
202
|
+
if (
|
|
203
|
+
string.IsNullOrEmpty(existing.FilePath)
|
|
204
|
+
&& !string.IsNullOrEmpty(contribution.FilePath)
|
|
205
|
+
)
|
|
206
|
+
{
|
|
207
|
+
existing.FilePath = contribution.FilePath;
|
|
208
|
+
existing.Line = contribution.Line;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
mergedReports.Clear();
|
|
214
|
+
foreach (KeyValuePair<string, ParsedTypeReport> kvp in rebuilt)
|
|
215
|
+
{
|
|
216
|
+
mergedReports[kvp.Key] = kvp.Value;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/// <summary>
|
|
221
|
+
/// Builds the final flat snapshot for the inspector overlay by unioning the LogEntries
|
|
222
|
+
/// scan with the per-assembly merged reports. Both inputs are read-only; the result is a
|
|
223
|
+
/// fresh dictionary the caller owns.
|
|
224
|
+
/// </summary>
|
|
225
|
+
/// <param name="logEntriesReports">Reports harvested from <c>UnityEditor.LogEntries</c>.
|
|
226
|
+
/// May be empty (Unity 2021 path) or null (LogEntries reflection unavailable).</param>
|
|
227
|
+
/// <param name="mergedReports">Per-FQN merged view of every assembly's latest reports
|
|
228
|
+
/// produced by <see cref="ApplyAssemblyReports"/>. May be empty when no compilation
|
|
229
|
+
/// callbacks have fired yet.</param>
|
|
230
|
+
public static Dictionary<string, BaseCallReportEntryDto> BuildSnapshot(
|
|
231
|
+
IReadOnlyDictionary<string, ParsedTypeReport> logEntriesReports,
|
|
232
|
+
IReadOnlyDictionary<string, ParsedTypeReport> mergedReports
|
|
233
|
+
)
|
|
234
|
+
{
|
|
235
|
+
Dictionary<string, BaseCallReportEntryDto> snapshot = new(StringComparer.Ordinal);
|
|
236
|
+
|
|
237
|
+
if (logEntriesReports is not null)
|
|
238
|
+
{
|
|
239
|
+
foreach (KeyValuePair<string, ParsedTypeReport> kvp in logEntriesReports)
|
|
240
|
+
{
|
|
241
|
+
AddOrMerge(snapshot, kvp.Key, kvp.Value);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (mergedReports is not null)
|
|
246
|
+
{
|
|
247
|
+
foreach (KeyValuePair<string, ParsedTypeReport> kvp in mergedReports)
|
|
248
|
+
{
|
|
249
|
+
AddOrMerge(snapshot, kvp.Key, kvp.Value);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return snapshot;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
private static void AddOrMerge(
|
|
257
|
+
Dictionary<string, BaseCallReportEntryDto> snapshot,
|
|
258
|
+
string fqn,
|
|
259
|
+
ParsedTypeReport report
|
|
260
|
+
)
|
|
261
|
+
{
|
|
262
|
+
if (string.IsNullOrEmpty(fqn) || report is null)
|
|
263
|
+
{
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (!snapshot.TryGetValue(fqn, out BaseCallReportEntryDto existing))
|
|
268
|
+
{
|
|
269
|
+
existing = new BaseCallReportEntryDto { TypeName = fqn };
|
|
270
|
+
snapshot[fqn] = existing;
|
|
271
|
+
}
|
|
272
|
+
if (string.IsNullOrEmpty(existing.TypeName))
|
|
273
|
+
{
|
|
274
|
+
existing.TypeName = fqn;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
foreach (string method in report.MissingBaseFor)
|
|
278
|
+
{
|
|
279
|
+
if (!string.IsNullOrEmpty(method))
|
|
280
|
+
{
|
|
281
|
+
// Dedupe across the dual-source merge: LogEntries and CompilerMessage may
|
|
282
|
+
// both surface the same `<type>.<method>` pair on Unity 2022+, where both
|
|
283
|
+
// pipes are wired. MissingBaseFor is a SortedSet<string>, so duplicates are
|
|
284
|
+
// removed and HelpBox output remains stable via deterministic ordinal sorting.
|
|
285
|
+
existing.MissingBaseFor.Add(method);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
foreach (string id in report.DiagnosticIds)
|
|
290
|
+
{
|
|
291
|
+
if (!string.IsNullOrEmpty(id))
|
|
292
|
+
{
|
|
293
|
+
// HashSet dedupes repeated IDs surfaced by the dual-source merge
|
|
294
|
+
// (LogEntries + CompilerMessage on Unity 2022+).
|
|
295
|
+
existing.DiagnosticIds.Add(id);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// First seen file/line wins so "Open Script" jumps to a stable location across
|
|
300
|
+
// rebuilds; which is what the user's eye lands on first in the console.
|
|
301
|
+
if (string.IsNullOrEmpty(existing.FilePath) && !string.IsNullOrEmpty(report.FilePath))
|
|
302
|
+
{
|
|
303
|
+
existing.FilePath = report.FilePath;
|
|
304
|
+
existing.Line = report.Line;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|