Skip to content

Commit d6f1105

Browse files
SourceGen & Analysis: More progress on identifying network methods, objects, and type resolvers.
1 parent 3f22d09 commit d6f1105

File tree

11 files changed

+494
-78
lines changed

11 files changed

+494
-78
lines changed

EppNet-SourceGen/AnalyzerReleases.Unshipped.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,8 @@
22

33
Rule ID | Category | Severity | Notes
44
--------|----------|----------|--------------------
5-
EPN001 | SourceGenerator | Warning | Example diagnostic
5+
EPN001 | SourceGenerator | Warning | Debug message
6+
EPN002 | SourceGenerator | Error | Unspecified analysis error
7+
EPN003 | SourceGenerator | Error | Network Type Resolver related error
8+
EPN004 | SourceGenerator | Error | Network Object related error
9+
EPN005 | SourceGenerator | Error | Network Method related error

EppNet-SourceGen/RegisterObjectsGenerator.cs

Lines changed: 59 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -32,61 +32,87 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
3232
transform: static (ctx, ct) => LocateResolver(ctx.TargetNode, ctx.SemanticModel, ct).Item1
3333
);
3434

35-
IncrementalValuesProvider<NetworkObjectModel?> netObjProvider = context.SyntaxProvider
35+
var resolvers = resolverProvider
36+
.Where(static r => r is not null)
37+
.Collect()
38+
.Select(static (list, _) => list.ToDictionary(r => r.Value.ResolvedTypeFullName, r => r.Value.Name));
39+
40+
var netObjects = context.SyntaxProvider
3641
.ForAttributeWithMetadataName(
3742
NetObjectAttrFullName,
3843
predicate: static (node, _) => node is ClassDeclarationSyntax,
39-
transform: static (ctx, ct) => LocateNetObject(ctx.TargetNode, ctx.SemanticModel, ct).Item1
40-
);
44+
transform: static (ctx, ct) => LocateNetObject(ctx.TargetNode, ctx.SemanticModel, ct)
45+
)
46+
//.Combine(resolvers)
47+
.Where(static o => o.Item1 is not null)
48+
.Collect()
49+
.Select(static (list, _) => list.ToDictionary(o => o.Item1.Value.FullyQualifiedName, o => o.Item1));
4150

42-
//resolverProvider.Select((model, token) => model.HasValue && )
51+
var combined = netObjects.Combine(resolvers);
4352

44-
context.RegisterSourceOutput(netObjProvider, static (spc, obj) =>
53+
var netMethods = context.SyntaxProvider
54+
.ForAttributeWithMetadataName(
55+
NetMethodAttrFullName,
56+
predicate: static (node, _) => node is MethodDeclarationSyntax,
57+
transform: static (ctx, ct) => (ctx, ct)
58+
)
59+
.Combine(combined)
60+
.Select(static (pair, _) =>
61+
{
62+
var ((ctx, ct), (netObjects, resolvers)) = pair;
63+
return LocateNetMethod(ctx.TargetNode, ctx.SemanticModel, resolvers, netObjects, ct);
64+
});
65+
66+
context.RegisterSourceOutput(netObjects, static (spc, objs) =>
4567
{
4668

47-
if (!obj.HasValue)
48-
return;
69+
foreach (NetworkObjectModel? obj in objs.Values)
70+
{
71+
if (!obj.HasValue)
72+
return;
4973

50-
NetworkObjectModel model = obj.Value;
51-
StringBuilder builder = new($$"""
52-
// <auto-generated/>
53-
// full {{model.FullNamespace}}
74+
NetworkObjectModel model = obj.Value;
75+
StringBuilder builder = new($$"""
76+
// <auto-generated/>
77+
// full {{model.FullNamespace}}
5478
55-
using EppNet.Logging;
56-
using EppNet.Node;
57-
using EppNet.Objects;
58-
using EppNet.Utilities;
79+
using EppNet.Logging;
80+
using EppNet.Node;
81+
using EppNet.Objects;
82+
using EppNet.Utilities;
5983
60-
using System.Diagnostics.CodeAnalysis;
84+
using System.Diagnostics.CodeAnalysis;
6185
62-
namespace {{model.FullNamespace}}
63-
{
64-
65-
public partial class {{model.Name}} : {{NetworkObjectInternalInterfaceName}}
86+
namespace {{model.FullNamespace}}
6687
{
88+
89+
public partial class {{model.Name}} : {{NetworkObjectInternalInterfaceName}}
90+
{
6791
68-
public ILoggable Notify { get => this; }
92+
public ILoggable Notify { get => this; }
6993
70-
public NetworkNode Node { get; }
71-
public ObjectService Service { get; }
94+
public NetworkNode Node { get; }
95+
public ObjectService Service { get; }
7296
73-
public {{model.Name}}([NotNull] ObjectService service)
74-
{
75-
Guard.AgainstNull(service);
76-
Node = service.Node;
77-
Service = service;
78-
}
97+
public {{model.Name}}([NotNull] ObjectService service)
98+
{
99+
Guard.AgainstNull(service);
100+
Node = service.Node;
101+
Service = service;
102+
}
79103
104+
}
105+
80106
}
81107
82-
}
83108
84109
110+
""");
85111

86-
""");
112+
string filename = $"{model.Name}.g.cs";
113+
spc.AddSource(filename, builder.ToString());
114+
}
87115

88-
string filename = $"{model.Name}.g.cs";
89-
spc.AddSource(filename, builder.ToString());
90116
});
91117

92118
}

EppNet-SourceGen/Source/Analysis/NetworkObjectAnalysis.cs

Lines changed: 128 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,52 +13,159 @@
1313
using Microsoft.CodeAnalysis.Diagnostics;
1414

1515
using System;
16+
using System.Collections.Concurrent;
1617
using System.Collections.Immutable;
18+
using System.Reflection;
1719

1820
namespace EppNet.Source.Analysis
1921
{
2022
[DiagnosticAnalyzer(LanguageNames.CSharp)]
2123
public class NetworkObjectAnalysis : DiagnosticAnalyzer
2224
{
2325

26+
public static ConcurrentDictionary<string, string> Resolvers = new();
27+
public static ConcurrentDictionary<string, NetworkObjectModel?> Objects = new();
28+
2429
public override void Initialize(AnalysisContext context)
2530
{
26-
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.ReportDiagnostics);
31+
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
2732
context.EnableConcurrentExecution();
2833

29-
context.RegisterSyntaxNodeAction((snac) =>
34+
35+
context.RegisterCompilationStartAction(compContext =>
3036
{
3137

32-
ClassDeclarationSyntax classNode = snac.Node as ClassDeclarationSyntax;
38+
compContext.RegisterSyntaxNodeAction(snac =>
39+
{
40+
41+
ClassDeclarationSyntax classNode = snac.Node as ClassDeclarationSyntax;
42+
43+
if (!Globals.HasAttribute(classNode, Globals.NetTypeResolverAttr))
44+
return;
3345

34-
if (!Globals.HasAttribute(classNode, Globals.NetObjectAttr))
35-
return;
46+
(ResolverModel?, ResolverAnalysisError) data = Globals.LocateResolver(snac.Node, snac.SemanticModel, snac.CancellationToken);
47+
ResolverAnalysisError results = data.Item2;
3648

37-
NetworkObjectAnalysisError results = Globals.LocateNetObject(snac.Node, snac.SemanticModel, snac.CancellationToken).Item2;
49+
foreach (ResolverAnalysisError error in Enum.GetValues(typeof(ResolverAnalysisError)))
50+
{
51+
if (error == ResolverAnalysisError.None)
52+
continue;
3853

39-
foreach (NetworkObjectAnalysisError error in Enum.GetValues(typeof(NetworkObjectAnalysisError)))
54+
if (results.HasFlag(error))
55+
{
56+
string message = error switch
57+
{
58+
ResolverAnalysisError.LacksSingleton => $"{classNode.Identifier.Text}: Resolvers must define a public static singleton field or property \"Instance\"",
59+
ResolverAnalysisError.LacksInheritance => $"{classNode.Identifier.Text}: Resolvers must inherit from {Globals.TypeResolverFullGenericName}",
60+
ResolverAnalysisError.NotClass => $"{classNode.Identifier.Text}: Resolvers must be a class!",
61+
_ => ToString(),
62+
};
63+
snac.ReportDiagnostic(Diagnostic.Create(Globals.DescTypeResolverError,
64+
classNode.Identifier.GetLocation(), message));
65+
}
66+
}
67+
68+
if (results == ResolverAnalysisError.None && data.Item1.HasValue)
69+
{
70+
Resolvers[data.Item1.Value.ResolvedTypeFullName] = data.Item1.Value.Name;
71+
}
72+
73+
}, SyntaxKind.ClassDeclaration);
74+
75+
compContext.RegisterSyntaxNodeAction(snac =>
4076
{
41-
if (error == NetworkObjectAnalysisError.None)
42-
continue;
77+
ClassDeclarationSyntax classNode = snac.Node as ClassDeclarationSyntax;
78+
79+
if (!Globals.HasAttribute(classNode, Globals.NetObjectAttr))
80+
return;
81+
82+
var data = Globals.LocateNetObject(
83+
classNode, snac.SemanticModel, snac.CancellationToken);
84+
85+
NetworkObjectModel? netModel = data.Item1;
86+
NetworkObjectAnalysisError results = data.Item2;
4387

44-
if (results.HasFlag(error))
88+
foreach (NetworkObjectAnalysisError error in Enum.GetValues(typeof(NetworkObjectAnalysisError)))
4589
{
46-
string message = error switch
90+
if (error == NetworkObjectAnalysisError.None)
91+
continue;
92+
93+
if (results.HasFlag(error))
4794
{
48-
NetworkObjectAnalysisError.NotPartial => $"{classNode.Identifier.Text}: Network Object definitions must be partial!",
49-
NetworkObjectAnalysisError.LacksInheritance => $"{classNode.Identifier.Text}: Network Object definitions must inherit from {Globals.NetworkObjectInterfaceName}",
50-
NetworkObjectAnalysisError.NotClass => $"{classNode.Identifier.Text}: Network Object definitions must be a class!",
51-
_ => ToString(),
52-
};
53-
snac.ReportDiagnostic(Diagnostic.Create(Globals.DescTypeResolverError,
54-
classNode.Identifier.GetLocation(), message));
95+
string message = error switch
96+
{
97+
NetworkObjectAnalysisError.NotPartial => $"{classNode.Identifier.Text}: Network Object definitions must be partial! Num Resolvers: {Resolvers.Count}",
98+
NetworkObjectAnalysisError.LacksInheritance => $"{classNode.Identifier.Text}: Network Object definitions must inherit from {Globals.NetworkObjectInterfaceName}",
99+
NetworkObjectAnalysisError.NotClass => $"{classNode.Identifier.Text}: Network Object definitions must be a class!",
100+
_ => ToString(),
101+
};
102+
103+
snac.ReportDiagnostic(Diagnostic.Create(Globals.DescNetObjError,
104+
classNode.Identifier.GetLocation(), message));
105+
}
106+
}
107+
108+
if (results == NetworkObjectAnalysisError.None && data.Item1.HasValue)
109+
{
110+
Objects[data.Item1.Value.FullyQualifiedName] = data.Item1;
55111
}
56-
}
57112

58-
}, SyntaxKind.ClassDeclaration);
113+
}, SyntaxKind.ClassDeclaration);
114+
115+
compContext.RegisterSyntaxNodeAction(snac =>
116+
{
117+
118+
MethodDeclarationSyntax methodNode = snac.Node as MethodDeclarationSyntax;
119+
120+
if (!Globals.HasAttribute(methodNode, Globals.NetMethodAttr))
121+
return;
122+
123+
var data = Globals.LocateNetMethod(methodNode, snac.SemanticModel, Resolvers, Objects);
124+
NetworkMethodAnalysisError results = data.Item2;
125+
126+
foreach (NetworkMethodAnalysisError error in Enum.GetValues(typeof(NetworkMethodAnalysisError)))
127+
{
128+
if (error == NetworkMethodAnalysisError.None)
129+
continue;
130+
131+
if (results.HasFlag(error))
132+
{
133+
134+
if (error == NetworkMethodAnalysisError.MissingResolver)
135+
{
136+
ITypeSymbol typeSymbol = snac.SemanticModel.GetTypeInfo(data.Item4).Type;
137+
138+
if (typeSymbol == null)
139+
continue;
140+
141+
string fullTypeName = $"{typeSymbol.ContainingNamespace.ToDisplayString()}.{typeSymbol.Name}";
142+
143+
snac.ReportDiagnostic(Diagnostic.Create(Globals.DescNetMethodError,
144+
data.Item3.GetLocation(), [data.Item4.GetLocation()], $"{methodNode.Identifier.Text}: Type {fullTypeName} does not have a registered Resolver!"));
145+
146+
continue;
147+
}
148+
149+
string message = error switch
150+
{
151+
NetworkMethodAnalysisError.NotNetworkObjectClass => $"{methodNode.Identifier.Text}: Network method must be within a class decorated with the NetworkObject attribute!",
152+
NetworkMethodAnalysisError.Inaccessible => $"{methodNode.Identifier.Text}: Network method must be public!",
153+
_ => $"{ToString()}",
154+
};
155+
156+
snac.ReportDiagnostic(Diagnostic.Create(Globals.DescNetMethodError,
157+
methodNode.Identifier.GetLocation(), message));
158+
}
159+
}
160+
161+
}, SyntaxKind.MethodDeclaration);
162+
});
163+
164+
59165
}
60166

61-
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Globals.DescTypeResolverError);
167+
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
168+
ImmutableArray.Create(Globals.DescTypeResolverError, Globals.DescNetObjError, Globals.DescNetMethodError);
62169

63170
}
64171

EppNet-SourceGen/Source/Analysis/ResolverAnalysis.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
using System;
1616
using System.Collections.Immutable;
1717

18+
/*
1819
namespace EppNet.Source.Analysis
1920
{
2021
[DiagnosticAnalyzer(LanguageNames.CSharp)]
@@ -63,3 +64,4 @@ public override void Initialize(AnalysisContext context)
6364
}
6465
6566
}
67+
*/

0 commit comments

Comments
 (0)