mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-14 06:34:30 +08:00
Compare commits
No commits in common. "a22e522cf9af7bbad24821a388b815b10cec240c" and "9239e5164464fc75f7e393ef8a0ed6c118274e94" have entirely different histories.
a22e522cf9
...
9239e51644
@ -365,228 +365,6 @@ public sealed class ContextRegistrationAnalyzerTests
|
|||||||
"0"));
|
"0"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task Does_Not_Report_When_Inherited_OnInitialize_Calls_Virtual_Helper_Overridden_In_Derived_Architecture()
|
|
||||||
{
|
|
||||||
await AnalyzerTestDriver<ContextRegistrationAnalyzer>.RunAsync(
|
|
||||||
Wrap("""
|
|
||||||
namespace TestApp
|
|
||||||
{
|
|
||||||
using GFramework.Core.Abstractions.Model;
|
|
||||||
using GFramework.Core.Abstractions.Systems;
|
|
||||||
using GFramework.Core.Architectures;
|
|
||||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
|
||||||
|
|
||||||
public interface IInventoryModel : IModel { }
|
|
||||||
|
|
||||||
public sealed class InventoryModel : IInventoryModel { }
|
|
||||||
|
|
||||||
public abstract class ArchitectureBase : Architecture
|
|
||||||
{
|
|
||||||
protected override void OnInitialize()
|
|
||||||
{
|
|
||||||
RegisterComponents();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void RegisterComponents()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class InventoryPanelSystem : ISystem
|
|
||||||
{
|
|
||||||
[GetModel]
|
|
||||||
private IInventoryModel _model = null!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class GameArchitecture : ArchitectureBase
|
|
||||||
{
|
|
||||||
protected override void RegisterComponents()
|
|
||||||
{
|
|
||||||
RegisterModel(new InventoryModel());
|
|
||||||
RegisterSystem(new InventoryPanelSystem());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task Does_Not_Report_When_Inherited_Module_Install_Calls_Virtual_Helper_Overridden_In_Derived_Module()
|
|
||||||
{
|
|
||||||
await AnalyzerTestDriver<ContextRegistrationAnalyzer>.RunAsync(
|
|
||||||
Wrap("""
|
|
||||||
namespace TestApp
|
|
||||||
{
|
|
||||||
using GFramework.Core.Abstractions.Architectures;
|
|
||||||
using GFramework.Core.Abstractions.Model;
|
|
||||||
using GFramework.Core.Abstractions.Systems;
|
|
||||||
using GFramework.Core.Architectures;
|
|
||||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
|
||||||
|
|
||||||
public interface IInventoryModel : IModel { }
|
|
||||||
|
|
||||||
public sealed class InventoryModel : IInventoryModel { }
|
|
||||||
|
|
||||||
public abstract class ModuleBase : IArchitectureModule
|
|
||||||
{
|
|
||||||
public void Install(IArchitecture architecture)
|
|
||||||
{
|
|
||||||
RegisterComponents(architecture);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void RegisterComponents(IArchitecture architecture)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class DerivedInventoryModule : ModuleBase
|
|
||||||
{
|
|
||||||
protected override void RegisterComponents(IArchitecture architecture)
|
|
||||||
{
|
|
||||||
architecture.RegisterModel(new InventoryModel());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class InventoryPanelSystem : ISystem
|
|
||||||
{
|
|
||||||
[GetModel]
|
|
||||||
private IInventoryModel _model = null!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class GameArchitecture : Architecture
|
|
||||||
{
|
|
||||||
protected override void OnInitialize()
|
|
||||||
{
|
|
||||||
InstallModule(new DerivedInventoryModule());
|
|
||||||
RegisterSystem(new InventoryPanelSystem());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task Reports_Warning_When_Derived_Architecture_Explicitly_Calls_Base_Helper()
|
|
||||||
{
|
|
||||||
var markup = MarkupTestSource.Parse(
|
|
||||||
Wrap("""
|
|
||||||
namespace TestApp
|
|
||||||
{
|
|
||||||
using GFramework.Core.Abstractions.Model;
|
|
||||||
using GFramework.Core.Abstractions.Systems;
|
|
||||||
using GFramework.Core.Architectures;
|
|
||||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
|
||||||
|
|
||||||
public interface IInventoryModel : IModel { }
|
|
||||||
|
|
||||||
public sealed class InventoryModel : IInventoryModel { }
|
|
||||||
|
|
||||||
public sealed class InventoryPanelSystem : ISystem
|
|
||||||
{
|
|
||||||
[GetModel]
|
|
||||||
private IInventoryModel {|#0:_model|} = null!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class ArchitectureBase : Architecture
|
|
||||||
{
|
|
||||||
protected virtual void RegisterComponents()
|
|
||||||
{
|
|
||||||
RegisterSystem(new InventoryPanelSystem());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class GameArchitecture : ArchitectureBase
|
|
||||||
{
|
|
||||||
protected override void OnInitialize()
|
|
||||||
{
|
|
||||||
base.RegisterComponents();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void RegisterComponents()
|
|
||||||
{
|
|
||||||
RegisterModel(new InventoryModel());
|
|
||||||
RegisterSystem(new InventoryPanelSystem());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""));
|
|
||||||
|
|
||||||
await AnalyzerTestDriver<ContextRegistrationAnalyzer>.RunAsync(
|
|
||||||
markup.Source,
|
|
||||||
markup.WithSpan(
|
|
||||||
new DiagnosticResult("GF_ContextRegistration_001", DiagnosticSeverity.Warning)
|
|
||||||
.WithArguments("IInventoryModel", "InventoryPanelSystem", "GameArchitecture"),
|
|
||||||
"0"));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task Reports_Warning_When_Derived_Module_Explicitly_Calls_Base_Helper()
|
|
||||||
{
|
|
||||||
var markup = MarkupTestSource.Parse(
|
|
||||||
Wrap("""
|
|
||||||
namespace TestApp
|
|
||||||
{
|
|
||||||
using GFramework.Core.Abstractions.Architectures;
|
|
||||||
using GFramework.Core.Abstractions.Model;
|
|
||||||
using GFramework.Core.Abstractions.Systems;
|
|
||||||
using GFramework.Core.Architectures;
|
|
||||||
using GFramework.SourceGenerators.Abstractions.Rule;
|
|
||||||
|
|
||||||
public interface IInventoryModel : IModel { }
|
|
||||||
|
|
||||||
public sealed class InventoryModel : IInventoryModel { }
|
|
||||||
|
|
||||||
public sealed class InventoryPanelSystem : ISystem
|
|
||||||
{
|
|
||||||
[GetModel]
|
|
||||||
private IInventoryModel {|#0:_model|} = null!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class ModuleBase : IArchitectureModule
|
|
||||||
{
|
|
||||||
public virtual void Install(IArchitecture architecture)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void RegisterComponents(IArchitecture architecture)
|
|
||||||
{
|
|
||||||
architecture.RegisterSystem(new InventoryPanelSystem());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class DerivedInventoryModule : ModuleBase
|
|
||||||
{
|
|
||||||
public override void Install(IArchitecture architecture)
|
|
||||||
{
|
|
||||||
base.RegisterComponents(architecture);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void RegisterComponents(IArchitecture architecture)
|
|
||||||
{
|
|
||||||
architecture.RegisterModel(new InventoryModel());
|
|
||||||
architecture.RegisterSystem(new InventoryPanelSystem());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class GameArchitecture : Architecture
|
|
||||||
{
|
|
||||||
protected override void OnInitialize()
|
|
||||||
{
|
|
||||||
InstallModule(new DerivedInventoryModule());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""));
|
|
||||||
|
|
||||||
await AnalyzerTestDriver<ContextRegistrationAnalyzer>.RunAsync(
|
|
||||||
markup.Source,
|
|
||||||
markup.WithSpan(
|
|
||||||
new DiagnosticResult("GF_ContextRegistration_001", DiagnosticSeverity.Warning)
|
|
||||||
.WithArguments("IInventoryModel", "InventoryPanelSystem", "GameArchitecture"),
|
|
||||||
"0"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string Wrap(string source)
|
private static string Wrap(string source)
|
||||||
{
|
{
|
||||||
return $"{TestPreamble}{Environment.NewLine}{Environment.NewLine}{source}";
|
return $"{TestPreamble}{Environment.NewLine}{Environment.NewLine}{source}";
|
||||||
|
|||||||
@ -495,7 +495,7 @@ public sealed class ContextRegistrationAnalyzer : DiagnosticAnalyzer
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TryResolveArchitectureHelperMethod(invocation, architectureType, out var helperMethod))
|
if (TryResolveArchitectureHelperMethod(invocation.TargetMethod, architectureType, out var helperMethod))
|
||||||
pendingMethods.Enqueue(helperMethod);
|
pendingMethods.Enqueue(helperMethod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -537,7 +537,7 @@ public sealed class ContextRegistrationAnalyzer : DiagnosticAnalyzer
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TryResolveModuleHelperMethod(invocation, moduleType, out var helperMethod))
|
if (TryResolveModuleHelperMethod(invocation.TargetMethod, moduleType, out var helperMethod))
|
||||||
pendingMethods.Enqueue(helperMethod);
|
pendingMethods.Enqueue(helperMethod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -651,46 +651,41 @@ public sealed class ContextRegistrationAnalyzer : DiagnosticAnalyzer
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryResolveArchitectureHelperMethod(
|
private static bool TryResolveArchitectureHelperMethod(
|
||||||
IInvocationOperation invocation,
|
IMethodSymbol targetMethod,
|
||||||
INamedTypeSymbol architectureType,
|
INamedTypeSymbol architectureType,
|
||||||
out IMethodSymbol helperMethod)
|
out IMethodSymbol helperMethod)
|
||||||
{
|
|
||||||
return TryResolveHelperMethod(invocation, architectureType, out helperMethod);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryResolveModuleHelperMethod(
|
|
||||||
IInvocationOperation invocation,
|
|
||||||
INamedTypeSymbol moduleType,
|
|
||||||
out IMethodSymbol helperMethod)
|
|
||||||
{
|
|
||||||
return TryResolveHelperMethod(invocation, moduleType, out helperMethod);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 解析架构/模块分析中的辅助方法调用。
|
|
||||||
/// 普通虚调用应跟随到具体类型上的 override,而显式 <c>base.Xxx()</c> 必须保留基类语义。
|
|
||||||
/// </summary>
|
|
||||||
private static bool TryResolveHelperMethod(
|
|
||||||
IInvocationOperation invocation,
|
|
||||||
INamedTypeSymbol concreteType,
|
|
||||||
out IMethodSymbol helperMethod)
|
|
||||||
{
|
{
|
||||||
helperMethod = default!;
|
helperMethod = default!;
|
||||||
var targetMethod = invocation.TargetMethod;
|
|
||||||
|
|
||||||
if (targetMethod.MethodKind is not (MethodKind.Ordinary or MethodKind.Constructor))
|
if (targetMethod.MethodKind is not (MethodKind.Ordinary or MethodKind.Constructor))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!SymbolHelpers.IsWithinTypeHierarchy(targetMethod.ContainingType, concreteType))
|
if (!SymbolHelpers.IsWithinTypeHierarchy(targetMethod.ContainingType, architectureType))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (SymbolHelpers.IsExplicitBaseInvocation(invocation))
|
// 对已经具备源码的方法保留原始目标,避免把显式的 base 调用重新折回到当前 override。
|
||||||
{
|
helperMethod = targetMethod.DeclaringSyntaxReferences.Length > 0 && !targetMethod.IsAbstract
|
||||||
helperMethod = targetMethod;
|
? targetMethod
|
||||||
|
: SymbolHelpers.ResolveHierarchyMethodImplementation(targetMethod, architectureType) ?? targetMethod;
|
||||||
return helperMethod.DeclaringSyntaxReferences.Length > 0;
|
return helperMethod.DeclaringSyntaxReferences.Length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
helperMethod = SymbolHelpers.ResolveHierarchyMethodImplementation(targetMethod, concreteType) ?? targetMethod;
|
private static bool TryResolveModuleHelperMethod(
|
||||||
|
IMethodSymbol targetMethod,
|
||||||
|
INamedTypeSymbol moduleType,
|
||||||
|
out IMethodSymbol helperMethod)
|
||||||
|
{
|
||||||
|
helperMethod = default!;
|
||||||
|
|
||||||
|
if (targetMethod.MethodKind is not (MethodKind.Ordinary or MethodKind.Constructor))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!SymbolHelpers.IsWithinTypeHierarchy(targetMethod.ContainingType, moduleType))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
helperMethod = targetMethod.DeclaringSyntaxReferences.Length > 0 && !targetMethod.IsAbstract
|
||||||
|
? targetMethod
|
||||||
|
: SymbolHelpers.ResolveHierarchyMethodImplementation(targetMethod, moduleType) ?? targetMethod;
|
||||||
return helperMethod.DeclaringSyntaxReferences.Length > 0;
|
return helperMethod.DeclaringSyntaxReferences.Length > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -788,17 +783,6 @@ public sealed class ContextRegistrationAnalyzer : DiagnosticAnalyzer
|
|||||||
SymbolEqualityComparer.Default.Equals(type, candidateType));
|
SymbolEqualityComparer.Default.Equals(type, candidateType));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsExplicitBaseInvocation(IInvocationOperation invocation)
|
|
||||||
{
|
|
||||||
return invocation.Syntax is InvocationExpressionSyntax
|
|
||||||
{
|
|
||||||
Expression: MemberAccessExpressionSyntax
|
|
||||||
{
|
|
||||||
Expression: BaseExpressionSyntax
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IMethodSymbol? ResolveHierarchyMethodImplementation(
|
public static IMethodSymbol? ResolveHierarchyMethodImplementation(
|
||||||
IMethodSymbol method,
|
IMethodSymbol method,
|
||||||
INamedTypeSymbol ownerType)
|
INamedTypeSymbol ownerType)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user