You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

76 lines
2.8 KiB

using System.Collections.Generic;
using System.Linq;
using gaemstone.SourceGen.Utility;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace gaemstone.SourceGen.Generators;
[Generator]
public class ModuleGenerator
: ISourceGenerator
{
private static readonly DiagnosticDescriptor ModuleMayNotBeNested = new(
"gaem0001", "Module may not be nested",
"Type {0} marked with [Module] may not be a nested type",
nameof(ModuleGenerator), DiagnosticSeverity.Error, true);
private static readonly DiagnosticDescriptor ModuleMustBePartial = new(
"gaem0002", "Module must be partial",
"Type {0} marked with [Module] must be a partial type",
nameof(ModuleGenerator), DiagnosticSeverity.Error, true);
private static readonly DiagnosticDescriptor ModuleBuiltInMustHavePath = new(
"gaem0003", "Built-in module must have [Path]",
"Type {0} marked with [Module] is a built-in module (static), and therefore must have [Path] set",
nameof(ModuleGenerator), DiagnosticSeverity.Error, true);
public void Initialize(GeneratorInitializationContext context)
=> context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
private class SyntaxReceiver
: ISyntaxContextReceiver
{
public HashSet<INamedTypeSymbol> Symbols { get; }
= new(SymbolEqualityComparer.Default);
public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
{
if (context.Node is not AttributeSyntax attrNode) return;
var model = context.SemanticModel;
var attrType = model.GetTypeInfo(attrNode).Type!;
if (attrType.GetFullName(true) != "gaemstone.ECS.ModuleAttribute") return;
var memberNode = attrNode.Parent?.Parent!;
var memberSymbol = model.GetDeclaredSymbol(memberNode) as INamedTypeSymbol;
Symbols.Add(memberSymbol!);
}
}
public void Execute(GeneratorExecutionContext context)
{
if (context.SyntaxContextReceiver is not SyntaxReceiver receiver) return;
foreach (var symbol in receiver.Symbols) {
var isNested = (symbol.ContainingType != null);
if (isNested)
context.ReportDiagnostic(Diagnostic.Create(ModuleMayNotBeNested,
symbol.Locations.FirstOrDefault(), symbol.GetFullName()));
var isPartial = symbol.DeclaringSyntaxReferences
.Any(r => (r.GetSyntax() as ClassDeclarationSyntax)?.Modifiers
.Any(t => t.IsKind(SyntaxKind.PartialKeyword)) ?? false);
if (!isPartial)
context.ReportDiagnostic(Diagnostic.Create(ModuleMustBePartial,
symbol.Locations.FirstOrDefault(), symbol.GetFullName()));
if (symbol.IsStatic && (symbol.GetAttribute("gaemstone.ECS.PathAttribute")?
.ConstructorArguments.FirstOrDefault().Value == null))
context.ReportDiagnostic(Diagnostic.Create(ModuleBuiltInMustHavePath,
symbol.Locations.FirstOrDefault(), symbol.GetFullName()));
}
}
}