Skip to content

Commit

Permalink
Merge pull request #382 from zziger/dev
Browse files Browse the repository at this point in the history
Async CodeGen nesting classes support
  • Loading branch information
FabianTerhorst committed Nov 9, 2021
2 parents 493cbe1 + 2414e7b commit 3d56dd2
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 7 deletions.
80 changes: 73 additions & 7 deletions api/AltV.Net.Async.CodeGen/AsyncEntityGenerator.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.ComponentModel.Design.Serialization;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
Expand Down Expand Up @@ -53,6 +54,48 @@ public void Initialize(GeneratorInitializationContext context)
context.RegisterForSyntaxNotifications(() => new SyntaxTreeReceiver());
}

private static IEnumerable<ISymbol> GetInterfaceMembers(INamedTypeSymbol @interface)
{
var members = @interface.GetMembers().ToList();
foreach (var namedTypeSymbol in @interface.Interfaces)
{
if (namedTypeSymbol.ToString().StartsWith("AltV.Net.Elements.Entities.")) continue;
foreach (var interfaceMember in GetInterfaceMembers(namedTypeSymbol))
{
if (members.All(e => e.Name != interfaceMember.Name)) members.Add(interfaceMember);
}
}

return members;
}

private static IEnumerable<ISymbol> GetClassMembers(INamedTypeSymbol @class)
{
var members = @class.GetMembers().ToList();
if (@class.BaseType is null || @class.BaseType.ToString().StartsWith("AltV.Net.Elements.Entities."))
return members;
foreach (var classMember in GetClassMembers(@class.BaseType))
{
if (members.All(e => e.Name != classMember.Name)) members.Add(classMember);
}

return members;
}

private static IList<INamedTypeSymbol> GetBaseTypes(INamedTypeSymbol @class)
{
var list = new List<INamedTypeSymbol>();
var currentClass = @class;

while (currentClass.BaseType is { } type)
{
list.Add(type);
currentClass = type;
}

return list;
}

public void Execute(GeneratorExecutionContext context)
{
var receiver = (SyntaxTreeReceiver) context.SyntaxReceiver!;
Expand All @@ -66,6 +109,8 @@ public void Execute(GeneratorExecutionContext context)

var attributeSymbol = compilation.GetTypeByMetadataName("AltV.Net.Async.CodeGen.AsyncEntityAttribute");

var validClasses = new List<INamedTypeSymbol>();

foreach (var classSyntax in receiver.Classes)
{
var @class = compilation.GetSemanticModel(classSyntax.SyntaxTree).GetDeclaredSymbol(classSyntax)!;
Expand Down Expand Up @@ -99,22 +144,33 @@ public void Execute(GeneratorExecutionContext context)
}

var baseType = @class.BaseType;
if (baseType is null || !baseType.ToString().StartsWith("AltV.Net.Elements.Entities."))

if (baseType is not null)
while (!baseType!.ToString().StartsWith("AltV.Net.Elements.Entities."))
baseType = baseType.BaseType;

if (baseType is null)
{
context.ReportDiagnostic(Diagnostic.Create(Diagnostics.AsyncEntityShouldImplementEntity,
@class.Locations.FirstOrDefault(), @class.ToString()));
continue;
}

validClasses.Add(@class);

var classBaseDeclaration = @class.BaseType!.ToString().StartsWith("AltV.Net.Elements.Entities.")
? ""
: " : " + @class.BaseType;

var members = new List<string>();

var interfaceMembers = @interface.GetMembers();
var classMembers = @class.GetMembers();
var interfaceMembers = GetInterfaceMembers(@interface);
var classMembers = GetClassMembers(@class).ToArray();

foreach (var member in interfaceMembers)
{
var classMember = classMembers.FirstOrDefault(m => m.Name == member.Name);

// does member hide base class property (new keyword)
var isNew = classMember?.DeclaringSyntaxReferences.Any(s =>
s.GetSyntax() is MemberDeclarationSyntax declarationSyntax &&
Expand Down Expand Up @@ -163,7 +219,7 @@ public void Execute(GeneratorExecutionContext context)
var formattedAttributes =
attributes.Length == 0 ? "" : FormatAttributes(attributes) + "\n";
var @new = isNew ? "new " : "";

members.Add(formattedAttributes +
$"public {@new}{property.Type} {member.Name} {{ {propertyValue}}}");
}
Expand Down Expand Up @@ -219,7 +275,7 @@ public void Execute(GeneratorExecutionContext context)
var attributes = classMethod.GetAttributes();
var formattedAttributes = attributes.Length == 0 ? "" : FormatAttributes(attributes) + "\n";
var @new = isNew ? "new " : "";

members.Add(formattedAttributes +
$"public {@new}{classMethod.ReturnType} {name}({arguments})\n{{\n {returnAction}BaseObject.{name}({callArguments});\n}}");

Expand All @@ -236,7 +292,7 @@ public void Execute(GeneratorExecutionContext context)
interfaceDeclaration = $"namespace {interfaceNamespace} {{\n{Indent(interfaceDeclaration)}\n}}";

var classDeclaration = $@"
public partial class {@class.Name} {{
public partial class {@class.Name}{classBaseDeclaration} {{
public {@interface} ToAsync(AltV.Net.Async.IAsyncContext asyncContext) {{
return new Async(this, asyncContext);
}}
Expand All @@ -257,6 +313,16 @@ private class Async : {asyncEntityType}<{@interface}>, {@interface} {{
sourceBuilder.Append($"\n\n{interfaceDeclaration}\n{classDeclaration}");
}

foreach (var @class in validClasses)
{
var types = GetBaseTypes(@class);
var invalid = types.FirstOrDefault(e => validClasses.Contains(e, SymbolEqualityComparer.Default));
if (invalid is not { } invalidClass) continue;

context.ReportDiagnostic(Diagnostic.Create(Diagnostics.AsyncEntityCannotBeNested,
@class.Locations.FirstOrDefault(), @class.ToString(), invalidClass.ToString()));
}

context.AddSource("GeneratedAsyncEntities.cs", SourceText.From(sourceBuilder.ToString(), Encoding.UTF8));
}
}
Expand Down
10 changes: 10 additions & 0 deletions api/AltV.Net.Async.CodeGen/Diagnostics.cs
Expand Up @@ -85,5 +85,15 @@ public static class Diagnostics
DiagnosticSeverity.Error,
true
);

public static DiagnosticDescriptor AsyncEntityCannotBeNested =
new(
"ALTV0009",
"Invalid async entity",
"Async entity class {0} cannot extend another async entity class {1}, async entities cannot be nested",
"AsyncEntity",
DiagnosticSeverity.Error,
true
);
}
}

0 comments on commit 3d56dd2

Please sign in to comment.