распознование типов
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
|
||||
namespace mROA.Codegen;
|
||||
|
||||
/// <summary>
|
||||
/// A sample source generator that creates a custom report based on class properties. The target class should be annotated with the 'Generators.ReportAttribute' attribute.
|
||||
/// When using the source code as a baseline, an incremental source generator is preferable because it reduces the performance overhead.
|
||||
/// </summary>
|
||||
[Generator]
|
||||
public class SampleIncrementalSourceGenerator : IIncrementalGenerator
|
||||
{
|
||||
private const string Namespace = "mROA.Implementation";
|
||||
private const string AttributeName = "SharedObjectInterafceAttribute";
|
||||
|
||||
private const string AttributeSourceCode = $@"// <auto-generated/>
|
||||
|
||||
namespace {Namespace}
|
||||
{{
|
||||
[System.AttributeUsage(System.AttributeTargets.Class)]
|
||||
public class {AttributeName} : System.Attribute
|
||||
{{
|
||||
}}
|
||||
}}";
|
||||
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
|
||||
// Filter classes annotated with the [Report] attribute. Only filtered Syntax Nodes can trigger code generation.
|
||||
var provider = context.SyntaxProvider
|
||||
.CreateSyntaxProvider(
|
||||
(s, _) => s is InterfaceDeclarationSyntax,
|
||||
(ctx, _) => GetClassDeclarationForSourceGen(ctx))
|
||||
.Where(t => t.reportAttributeFound)
|
||||
.Select((t, _) => t.Item1);
|
||||
|
||||
// Generate the source code.
|
||||
context.RegisterSourceOutput(context.CompilationProvider.Combine(provider.Collect()),
|
||||
((ctx, t) => GenerateCode(ctx, t.Left, t.Right)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the Node is annotated with the [Report] attribute and maps syntax context to the specific node type (ClassDeclarationSyntax).
|
||||
/// </summary>
|
||||
/// <param name="context">Syntax context, based on CreateSyntaxProvider predicate</param>
|
||||
/// <returns>The specific cast and whether the attribute was found.</returns>
|
||||
private static (InterfaceDeclarationSyntax, bool reportAttributeFound) GetClassDeclarationForSourceGen(
|
||||
GeneratorSyntaxContext context)
|
||||
{
|
||||
var classDeclarationSyntax = (InterfaceDeclarationSyntax)context.Node;
|
||||
|
||||
// Go through all attributes of the class.
|
||||
foreach (AttributeListSyntax attributeListSyntax in classDeclarationSyntax.AttributeLists)
|
||||
foreach (AttributeSyntax attributeSyntax in attributeListSyntax.Attributes)
|
||||
{
|
||||
if (context.SemanticModel.GetSymbolInfo(attributeSyntax).Symbol is not IMethodSymbol attributeSymbol)
|
||||
continue; // if we can't get the symbol, ignore it
|
||||
|
||||
string attributeName = attributeSymbol.ContainingType.ToDisplayString();
|
||||
|
||||
// Check the full name of the [Report] attribute.
|
||||
if (attributeName == "mROA.Implementation.SharedObjectInterfaceAttribute")
|
||||
return (classDeclarationSyntax, true);
|
||||
}
|
||||
|
||||
return (classDeclarationSyntax, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate code action.
|
||||
/// It will be executed on specific nodes (ClassDeclarationSyntax annotated with the [Report] attribute) changed by the user.
|
||||
/// </summary>
|
||||
/// <param name="context">Source generation context used to add source files.</param>
|
||||
/// <param name="compilation">Compilation used to provide access to the Semantic Model.</param>
|
||||
/// <param name="classDeclarations">Nodes annotated with the [Report] attribute that trigger the generate action.</param>
|
||||
private void GenerateCode(SourceProductionContext context, Compilation compilation,
|
||||
ImmutableArray<InterfaceDeclarationSyntax> classDeclarations)
|
||||
{
|
||||
// Go through all filtered class declarations.
|
||||
foreach (var classDeclarationSyntax in classDeclarations)
|
||||
{
|
||||
// We need to get semantic model of the class to retrieve metadata.
|
||||
var semanticModel = compilation.GetSemanticModel(classDeclarationSyntax.SyntaxTree);
|
||||
|
||||
// Symbols allow us to get the compile-time information.
|
||||
if (semanticModel.GetDeclaredSymbol(classDeclarationSyntax) is not INamedTypeSymbol classSymbol)
|
||||
continue;
|
||||
|
||||
var namespaceName = classSymbol.ContainingNamespace.ToDisplayString();
|
||||
|
||||
// 'Identifier' means the token of the node. Get class name from the syntax node.
|
||||
var className = classDeclarationSyntax.Identifier.Text;
|
||||
|
||||
// Go through all class members with a particular type (property) to generate method lines.
|
||||
var methodBody = classSymbol.GetMembers()
|
||||
.OfType<IMethodSymbol>();
|
||||
Console.WriteLine(methodBody.Select(i => i.ToDisplayString()));
|
||||
|
||||
// Build up the source code
|
||||
className = className.TrimStart('I') + "RemoteEndpoint";
|
||||
var code = $@"// <auto-generated/>
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace {namespaceName};
|
||||
|
||||
partial class {className} (int id, ISerialisationModule.IFrontendSerialisationModule serialisationModule) : ITestParameter, IRemoteObject
|
||||
{{
|
||||
public int Id => id;
|
||||
}}
|
||||
";
|
||||
|
||||
// Add the source code to the compilation.
|
||||
context.AddSource($"{className}.g.cs", SourceText.From(code, Encoding.UTF8));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user