2025-03-08 12:10:12 +03:00
// #define DONT_ADD
2025-02-22 09:28:59 +03:00
using System ;
2025-02-04 22:10:48 +03:00
using System.Collections.Generic ;
2025-02-04 19:02:39 +03:00
using System.Collections.Immutable ;
using System.Linq ;
using System.Text ;
using Microsoft.CodeAnalysis ;
using Microsoft.CodeAnalysis.CSharp.Syntax ;
using Microsoft.CodeAnalysis.Text ;
2025-02-20 16:10:09 +03:00
namespace mROA.Codegen
2025-02-04 19:02:39 +03:00
{
2025-02-20 16:10:09 +03:00
/// <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 mROASourceGenerator : ISourceGenerator
{
private void GenerateCode ( GeneratorExecutionContext context , Compilation compilation ,
ImmutableArray < InterfaceDeclarationSyntax > classes )
{
2025-03-07 09:01:21 +03:00
// For future
// var asm = Assembly.GetAssembly(typeof(mROASourceGenerator));
// var files = asm.GetManifestResourceNames();
// var test = asm.GetManifestResourceStream("mROA.Codegen.test.tpt");
// var reader = new StreamReader(test);
// var allText = reader.ReadToEnd();
2025-02-20 16:10:09 +03:00
var frontendContextRepo = new List < string >();
2025-03-08 22:31:40 +03:00
List < IMethodSymbol > totalMethods = new List < IMethodSymbol >();
2025-03-06 14:10:52 +03:00
2025-03-08 19:46:59 +03:00
List < string > invokers = new List < string >();
2025-02-20 16:10:09 +03:00
var declarations = classes . ToList (). OrderBy ( i => i . Identifier . Text ). ToList ();
foreach ( var classDeclarationSyntax in declarations )
2025-03-07 09:01:21 +03:00
{
2025-02-20 16:10:09 +03:00
var semanticModel = compilation . GetSemanticModel ( classDeclarationSyntax . SyntaxTree );
2025-02-04 19:02:39 +03:00
2025-02-20 16:10:09 +03:00
if ( semanticModel . GetDeclaredSymbol ( classDeclarationSyntax ) is not INamedTypeSymbol classSymbol )
continue ;
2025-03-08 22:31:40 +03:00
List < IMethodSymbol > innerMethods = new List < IMethodSymbol >();
2025-03-07 09:01:21 +03:00
2025-03-06 14:10:52 +03:00
var namespaceName = classSymbol . ContainingNamespace . ToDisplayString ();
2025-02-20 16:10:09 +03:00
var className = classDeclarationSyntax . Identifier . Text ;
2025-03-08 22:31:40 +03:00
innerMethods = CollectMembers ( classSymbol );
2025-03-08 12:10:12 +03:00
var associated = innerMethods . Select ( i => i . AssociatedSymbol ). Where ( i => i != null )
. Distinct ( SymbolEqualityComparer . Default ). Cast < ISymbol >(). ToList ();
innerMethods = innerMethods . Where ( i =>
i . MethodKind != MethodKind . EventAdd && i . MethodKind != MethodKind . EventRemove )
2025-03-07 15:46:22 +03:00
. ToList ();
2025-03-08 22:31:40 +03:00
totalMethods . AddRange ( innerMethods );
2025-02-20 16:10:09 +03:00
var originalName = className ;
2025-02-04 22:10:48 +03:00
2025-03-07 09:01:21 +03:00
className = className . TrimStart ( 'I' ) + "RemoteEndpoint" ;
2025-03-06 14:10:52 +03:00
2025-02-04 19:02:39 +03:00
2025-03-07 15:46:22 +03:00
var declaredMethods = new List < string >();
var propertiesAccessMethods = new List <( string , IMethodSymbol )>();
2025-03-08 19:46:59 +03:00
2025-03-07 15:46:22 +03:00
foreach ( var method in innerMethods )
{
switch ( method . MethodKind )
2025-03-06 14:10:52 +03:00
{
2025-03-07 15:46:22 +03:00
case MethodKind . PropertyGet or MethodKind . PropertySet :
2025-03-08 22:31:40 +03:00
GeneratePropertyMethod ( method , totalMethods , propertiesAccessMethods , invokers );
2025-03-06 14:10:52 +03:00
continue ;
2025-03-07 15:46:22 +03:00
default :
2025-03-08 22:31:40 +03:00
GenerateDeclaredMethod ( method , declaredMethods , totalMethods , invokers );
2025-03-07 15:46:22 +03:00
break ;
2025-03-06 14:10:52 +03:00
}
2025-03-07 15:46:22 +03:00
}
2025-02-04 23:19:24 +03:00
2025-03-08 12:10:12 +03:00
foreach ( var symbol in associated )
2025-03-07 15:46:22 +03:00
{
2025-03-08 12:10:12 +03:00
switch ( symbol )
{
case IPropertySymbol propertySymbol :
var setter = propertiesAccessMethods . FirstOrDefault ( i =>
i . Item2 . AssociatedSymbol !. Name == propertySymbol . Name && i . Item2 . ReturnsVoid );
var getter = propertiesAccessMethods . FirstOrDefault ( i =>
i . Item2 . AssociatedSymbol !. Name == propertySymbol . Name && ! i . Item2 . ReturnsVoid );
string impl ;
if ( propertySymbol . IsIndexer )
{
impl =
$"public {propertySymbol.Type.ToDisplayString()} this[{string.Join(" , ", propertySymbol.Parameters.Select(p => p.ToDisplayString()))}] {{ {getter.Item1} {setter.Item1} }}" ;
}
else
{
impl =
$"public {propertySymbol.Type.ToDisplayString()} {symbol.Name} {{ {getter.Item1} {setter.Item1} }}" ;
}
declaredMethods . Add ( impl );
break ;
case IEventSymbol eventSymbol :
declaredMethods . Add (
$"public event {eventSymbol.Type.ToDisplayString()}? {eventSymbol.Name};" );
break ;
}
2025-02-20 16:10:09 +03:00
}
var code = $@"// <auto-generated/>
2025-02-04 19:02:39 +03:00
2025-02-05 14:50:07 +03:00
using mROA;
2025-02-04 19:02:39 +03:00
using System;
2025-02-04 20:57:04 +03:00
using mROA.Implementation;
2025-02-04 19:02:39 +03:00
using System.Collections.Generic;
2025-02-07 17:11:58 +03:00
using mROA.Abstract;
2025-02-04 19:02:39 +03:00
2025-02-20 16:10:09 +03:00
namespace {namespaceName}
2025-02-04 19:02:39 +03:00
{{
2025-02-20 16:10:09 +03:00
partial class {className} : RemoteObjectBase, {originalName}
2025-02-18 00:10:53 +03:00
{{
2025-02-20 16:10:09 +03:00
public {className}(int id, IRepresentationModule representationModule) : base(id, representationModule)
{{
}}
2025-02-04 23:19:24 +03:00
2025-03-08 12:10:12 +03:00
{string.Join(" \ r \ n \ t \ t ", declaredMethods)}
2025-02-20 16:10:09 +03:00
}}
2025-02-04 19:02:39 +03:00
}}
";
2025-02-04 22:10:48 +03:00
2025-02-20 16:10:09 +03:00
// Add the source code to the compilation.
2025-03-08 12:10:12 +03:00
#if ! DONT_ADD
context . AddSource ( $"{className}.g.cs" , SourceText . From ( code , Encoding . UTF8 ));
#endif
2025-02-20 16:10:09 +03:00
frontendContextRepo . Add (
$"{{ typeof({classSymbol.ToDisplayString()}), typeof({namespaceName}.{className}) }}" );
}
2025-03-08 22:31:40 +03:00
if ( totalMethods . Count != 0 )
2025-02-20 16:10:09 +03:00
{
2025-03-08 19:46:59 +03:00
var methodsStringed = invokers ;
2025-03-08 22:31:40 +03:00
2025-02-20 16:10:09 +03:00
var coCodegenRepoCode = @ $"// <auto-generated/>
2025-02-04 22:10:48 +03:00
using System.Collections.Generic;
using System.Reflection;
2025-02-07 17:11:58 +03:00
using mROA.Abstract;
2025-03-06 14:10:52 +03:00
using mROA.Implementation;
2025-02-20 16:10:09 +03:00
using System;
2025-03-08 19:46:59 +03:00
using System.Threading;
2025-02-04 22:10:48 +03:00
2025-02-20 16:10:09 +03:00
namespace mROA.Codegen
2025-02-04 22:10:48 +03:00
{{
2025-02-20 16:10:09 +03:00
public class CoCodegenMethodRepository : IMethodRepository
2025-02-04 22:10:48 +03:00
{{
2025-03-06 14:10:52 +03:00
private readonly List<IMethodInvoker> _methods = new () {{
2025-02-20 16:10:09 +03:00
{string.Join(" , \ r \ n \ t \ t \ t ", methodsStringed)}
}};
2025-03-06 14:10:52 +03:00
public IMethodInvoker GetMethod ( int id )
2025-02-20 16:10:09 +03:00
{{
2025-02-23 10:18:38 +03:00
if ( id == - 1 )
2025-03-06 14:10:52 +03:00
return mROA . Implementation . MethodInvoker . Dispose ;
2025-02-23 10:18:38 +03:00
2025-02-20 16:10:09 +03:00
if ( _methods . Count <= id )
return null ;
return _methods [ id ];
}}
public void Inject < T >( T dependency )
{{
}}
2025-02-04 22:10:48 +03:00
}}
2025-02-20 16:10:09 +03:00
}}
";
2025-03-08 19:46:59 +03:00
#if ! DONT_ADD
2025-03-06 14:10:52 +03:00
context . AddSource ( "CoCodegenMethodRepository.g.cs" , SourceText . From ( coCodegenRepoCode , Encoding . UTF8 ));
2025-03-08 19:46:59 +03:00
#endif
2025-02-20 16:10:09 +03:00
}
2025-03-08 22:31:40 +03:00
2025-02-20 16:10:09 +03:00
if ( frontendContextRepo . Count != 0 )
{
var fronendRepoCode = @ $"// <auto-generated/>
using mROA.Implementation;
using mROA.Abstract;
using System.Collections.Generic;
using System;
using System.Reflection;
2025-02-05 10:58:50 +03:00
2025-02-20 16:10:09 +03:00
namespace mROA.Codegen
{{
public sealed class RemoteTypeBinder
2025-02-05 10:58:50 +03:00
{{
2025-02-20 16:10:09 +03:00
static RemoteTypeBinder(){{
RemoteContextRepository.RemoteTypes = new Dictionary<Type, Type> {{
{string.Join(" , \ r \ n \ t \ t \ t ", frontendContextRepo)}}};
}}
2025-02-05 10:58:50 +03:00
}}
2025-02-04 22:10:48 +03:00
}}
";
2025-03-08 12:10:12 +03:00
#if ! DONT_ADD
context . AddSource ( "RemoteTypeBinder.g.cs" , SourceText . From ( fronendRepoCode , Encoding . UTF8 ));
#endif
2025-02-20 16:10:09 +03:00
}
2025-02-06 09:18:52 +03:00
}
2025-03-08 19:46:59 +03:00
public static string Caster ( ITypeSymbol type , string inner )
{
if (! type . IsValueType )
return inner +
" as " +
type . ToDisplayString ();
return $"({type.ToDisplayString()})" + inner ;
}
private void GenerateDeclaredMethod ( IMethodSymbol method , List < string > declaredMethods ,
List < IMethodSymbol > methods , List < string > invokers )
2025-03-07 15:46:22 +03:00
{
var index = methods . IndexOf ( method );
var sb = new StringBuilder ();
bool isParametrized ;
bool isAsync ;
bool isVoid ;
List < IParameterSymbol >? parameters ;
switch ( method . ReturnType )
{
case INamedTypeSymbol namedType :
isAsync = namedType . Name == "Task" ;
isVoid = isAsync && namedType . TypeParameters . Length == 0 || namedType . Name == "Void" ;
parameters = method . Parameters . ToList ();
2025-03-08 12:10:12 +03:00
parameters . RemoveAll ( ParameterFilter );
2025-03-07 15:46:22 +03:00
isParametrized = parameters . Count != 0 ;
break ;
case IArrayTypeSymbol :
isAsync = false ;
isVoid = false ;
2025-03-08 12:10:12 +03:00
parameters = new List < IParameterSymbol >();
2025-03-07 15:46:22 +03:00
isParametrized = method . Parameters . Length != 0 ;
break ;
default :
return ;
}
//Creating signature
sb . AppendLine ( "public" + ( isAsync
? " async "
: " " ) +
$"{method.ReturnType.ToDisplayString()} {method.Name}({string.Join(" , ", method.Parameters.Select(ToFullString))}){{" );
var prefix = isAsync ? "await " : "" ;
var postfix = ! isAsync ? isVoid ? ".Wait()" : ".GetAwaiter().GetResult()" : "" ;
var parameterLink = isParametrized
2025-03-08 12:10:12 +03:00
? ", new object[] {" + string . Join ( ", " , parameters . Select ( i => i . Name )) + "}"
2025-03-07 15:46:22 +03:00
: string . Empty ;
2025-03-08 12:10:12 +03:00
var tokenInsert = isAsync && method . Parameters . FirstOrDefault ( i => i . Type . Name == "CancellationToken" ) is
{ } tokenSymbol
? ", cancellationToken : " + tokenSymbol . Name
2025-03-07 15:46:22 +03:00
: String . Empty ;
var caller = isVoid
? $"CallAsync({index}{parameterLink}{tokenInsert})"
: isAsync
? $"GetResultAsync<{ExtractTaskType(method.ReturnType)}>({index}{parameterLink}{tokenInsert})"
: $"GetResultAsync<{ToFullString(method.ReturnType)}>({index}{parameterLink}{tokenInsert})" ;
if (! isVoid )
prefix = "return " + prefix ;
sb . AppendLine ( "\t\t\t" + prefix + caller + postfix + ";" );
sb . AppendLine ( "\t\t}" );
2025-03-08 19:46:59 +03:00
declaredMethods . Add ( sb . ToString ());
var parameterTypes = string . Join ( ", " ,
$"{string.Join(" , ", parameters.Select(p => $" typeof ({ p . Type . ToDisplayString ()}) "))}" );
var level = "\t\t\t" ;
var parametersInsertList = new List < string >();
for ( var i = 0 ; i < method . Parameters . Length ; i ++)
{
var parameter = method . Parameters [ i ];
switch ( parameter . Type . Name )
{
case "CancellationToken" :
parametersInsertList . Add ( "(CancellationToken)special[1]" );
break ;
case "RequestContext" :
parametersInsertList . Add ( "special[1] as RequestContext" );
break ;
default :
parametersInsertList . Add ( Caster ( parameter . Type ,
$"parameters[{parameters.IndexOf(parameter)}]" ));
break ;
}
}
var parametersInsert = string . Join ( ", " , parametersInsertList );
var backend = string . Empty ;
var funcInvoking = string . Empty ;
if ( isAsync && ! isVoid )
funcInvoking =
$"(i as {method.ContainingType.ToDisplayString()}).{method.Name}({parametersInsert}).ContinueWith(t => {{ post(t.Result); }})" ;
else if ( isAsync && isVoid )
funcInvoking =
$"(i as {method.ContainingType.ToDisplayString()}).{method.Name}({parametersInsert}).ContinueWith(t => {{ post(null); }})" ;
else if (! isAsync && isVoid )
{
funcInvoking = $@"{{
{level} (i as {method.ContainingType.ToDisplayString()}).{method.Name}({parametersInsert});
{level} return null;
{level} }}" ;
}
else
{
funcInvoking = $"(i as {method.ContainingType.ToDisplayString()}).{method.Name}({parametersInsert})" ;
}
if ( isAsync )
backend = $@"new mROA.Implementation.AsyncMethodInvoker
{level}{{
{level} IsVoid = {isVoid.ToString().ToLower()},
{(isVoid ? String.Empty : (level + " \ t " + " ReturnType = typeof ( " + ExtractTaskType(method.ReturnType)) + " ), ")}
{ level } ParameterTypes = new Type [] {{ { parameterTypes } }},
2025-03-08 22:31:40 +03:00
{ level } SuitableType = typeof ({ method . ContainingType . ToDisplayString ()}),
2025-03-08 19:46:59 +03:00
{ level } Invoking = ( i , parameters , special , post ) => { funcInvoking },
{ level }}} ";
else
backend = $@"new mROA.Implementation.MethodInvoker
{level}{{
{level} IsVoid = {isVoid.ToString().ToLower()},
{level} ReturnType = typeof({method.ReturnType.ToDisplayString()}),
{level} ParameterTypes = new Type[] {{ {parameterTypes} }},
2025-03-08 22:31:40 +03:00
{level} SuitableType = typeof({method.ContainingType.ToDisplayString()}),
2025-03-08 19:46:59 +03:00
{level} Invoking = (i, parameters, special) => {funcInvoking}
{level}}}" ;
invokers . Add ( backend );
2025-03-07 15:46:22 +03:00
}
2025-03-08 12:10:12 +03:00
private static Predicate < IParameterSymbol > ParameterFilter =
i => i . Type . Name is "CancellationToken" or "RequestContext" ;
2025-03-07 15:46:22 +03:00
public void GeneratePropertyMethod ( IMethodSymbol method , List < IMethodSymbol > methods ,
2025-03-08 19:46:59 +03:00
List <( string , IMethodSymbol )> propsCollection , List < string > invokers )
2025-03-07 15:46:22 +03:00
{
2025-03-08 19:46:59 +03:00
var level = "\t\t\t" ;
2025-03-07 15:46:22 +03:00
var index = methods . IndexOf ( method );
2025-03-08 19:46:59 +03:00
string frontend ;
string backend = string . Empty ;
2025-03-07 15:46:22 +03:00
if ( method . MethodKind == MethodKind . PropertyGet )
{
var parametersArray = "" ;
if ( method . Parameters . Length != 0 )
{
parametersArray = $", new object[] {{{string.Join(" , ", method.Parameters.Select(p => p.Name))}}}" ;
2025-03-08 19:46:59 +03:00
var parameterTypes = string . Join ( ", " ,
$"{string.Join(" , ", method.Parameters.Select(p => " typeof ( " + p.Type.ToDisplayString() + " ) "))}" );
var parameterInserts = string . Join ( ", " ,
method . Parameters . Select ( p =>
{
return Caster ( p . Type , "parameters[" + method . Parameters . IndexOf ( p ) + "]" );
// if (!p.Type.IsValueType)
// return "parameters[" + method.Parameters.IndexOf(p) + "] as " +
// p.Type.ToDisplayString();
// return $"({p.Type.ToDisplayString()})parameters[{method.Parameters.IndexOf(p)}]";
}
));
backend = $@"new mROA.Implementation.MethodInvoker
{level}{{
{level} IsVoid = false,
{level} ReturnType = typeof({method.ReturnType.ToDisplayString()}),
{level} ParameterTypes = new Type[] {{ {parameterTypes} }},
2025-03-08 22:31:40 +03:00
{level} SuitableType = typeof({method.ContainingType.ToDisplayString()}),
2025-03-08 19:46:59 +03:00
{level} Invoking = (i, parameters, _) => (i as {method.ContainingType.ToDisplayString()})[{parameterInserts}],
{level}}}" ;
}
else
{
backend = $@"new mROA.Implementation.MethodInvoker
{level}{{
{level} IsVoid = false,
{level} ReturnType = typeof({method.ReturnType.ToDisplayString()}),
2025-03-08 22:31:40 +03:00
{level} SuitableType = typeof({method.ContainingType.ToDisplayString()}),
2025-03-08 19:46:59 +03:00
{level} Invoking = (i, _, _) => (i as {method.ContainingType.ToDisplayString()}).{(method.AssociatedSymbol as IPropertySymbol)!.Name},
{level}}}" ;
2025-03-07 15:46:22 +03:00
}
2025-03-08 19:46:59 +03:00
frontend =
2025-03-08 12:10:12 +03:00
$"get => GetResultAsync<{method.ReturnType.ToDisplayString()}>({index}{parametersArray}).GetAwaiter().GetResult();" ;
2025-03-07 15:46:22 +03:00
}
else
{
2025-03-08 12:10:12 +03:00
var parametersArray = "value" ;
2025-03-08 19:46:59 +03:00
if ( method . Parameters . Length != 1 )
2025-03-07 15:46:22 +03:00
{
2025-03-08 12:10:12 +03:00
parametersArray = string . Join ( ", " , method . Parameters . Select ( p => p . Name ));
2025-03-08 19:46:59 +03:00
var parameterTypes = string . Join ( ", " ,
$"{string.Join(" , ", method.Parameters.Select(p => $" typeof ({ p . Type . ToDisplayString ()}) "))}" );
var parameterInserts = string . Join ( ", " ,
method . Parameters . Take ( method . Parameters . Length - 1 ). Select ( p =>
{
return Caster ( p . Type , "parameters[" + method . Parameters . IndexOf ( p ) + "]" );
// if (!p.Type.IsValueType)
// return "parameters[" + method.Parameters.IndexOf(p) + "] as " +
// p.Type.ToDisplayString();
// return $"({p.Type.ToDisplayString()})parameters[{method.Parameters.IndexOf(p)}]";
}
));
// var valueInsert = !method.Parameters.Last().Type.IsValueType
// ? "parameters[" + (method.Parameters.Length - 1) + "] as " +
// method.Parameters.Last().Type.ToDisplayString()
// : $"({method.Parameters.Last().Type.ToDisplayString()})parameters[{method.Parameters.Length - 1}]";
var valueInsert = Caster ( method . Parameters . Last (). Type ,
"parameters[" + ( method . Parameters . Length - 1 ) + "]" );
backend = $@"new mROA.Implementation.MethodInvoker
{level}{{
{level} IsVoid = false,
{level} ReturnType = typeof({method.ReturnType.ToDisplayString()}),
{level} ParameterTypes = new Type[] {{ {parameterTypes} }},
2025-03-08 22:31:40 +03:00
{level} SuitableType = typeof({method.ContainingType.ToDisplayString()}),
2025-03-08 19:46:59 +03:00
{level} Invoking = (i, parameters, _) => (i as {method.ContainingType.ToDisplayString()})[{parameterInserts}] = {valueInsert},
{level}}}" ;
}
else
{
backend = $@"new mROA.Implementation.MethodInvoker
{level}{{
{level} IsVoid = false,
{level} ReturnType = typeof({method.ReturnType.ToDisplayString()}),
2025-03-08 23:22:59 +03:00
{level} ParameterTypes = new Type[] {{ typeof({method.Parameters.First().Type.ToDisplayString()}) }},
2025-03-08 22:31:40 +03:00
{level} SuitableType = typeof({method.ContainingType.ToDisplayString()}),
2025-03-08 19:46:59 +03:00
{level} Invoking = (i, parameters, _) => (i as {method.ContainingType.ToDisplayString()}).{(method.AssociatedSymbol as IPropertySymbol)!.Name} = {Caster((method.AssociatedSymbol as IPropertySymbol)!.Type, " parameters [ 0 ] ")},
{ level }}} ";
2025-03-07 15:46:22 +03:00
}
2025-03-08 19:46:59 +03:00
frontend = $"set => CallAsync({index}, new object[] {{ {parametersArray} }}).Wait();" ;
2025-03-07 15:46:22 +03:00
}
2025-03-08 19:46:59 +03:00
propsCollection . Add (( frontend , method ));
invokers . Add ( backend );
2025-03-07 15:46:22 +03:00
}
2025-02-20 16:10:09 +03:00
private static string ToFullString ( IParameterSymbol parameter )
=> /*parameter.Type.ContainingNamespace is null*/
/*?*/ parameter . ToDisplayString ();
/*: $"{parameter.Type.ContainingNamespace.ToDisplayString()}.{parameter.Type.MetadataName} {parameter.Name}";*/
private static string ToFullString ( ITypeSymbol type ) =>
// => type.ContainingNamespace is null || type.Name == "Void"
type . ToDisplayString ();
// : $"{type.ContainingNamespace.ToDisplayString()}.{type.MetadataName}";
private string ExtractTaskType ( ITypeSymbol taskType )
2025-02-06 09:18:52 +03:00
{
2025-03-08 19:46:59 +03:00
return ( taskType as INamedTypeSymbol ). TypeArguments [ 0 ]. ToDisplayString ();
2025-02-20 16:10:09 +03:00
}
2025-02-05 00:26:28 +03:00
2025-02-20 16:10:09 +03:00
public void Initialize ( GeneratorInitializationContext context )
{
}
2025-02-05 00:26:28 +03:00
2025-02-20 16:10:09 +03:00
public void Execute ( GeneratorExecutionContext context )
{
var trees = context . Compilation . SyntaxTrees ;
var interfaces = new List < InterfaceDeclarationSyntax >();
foreach ( var tree in trees )
{
var node = tree . GetRoot () as CompilationUnitSyntax ;
foreach ( var member in node . Members )
{
if ( member is InterfaceDeclarationSyntax ids )
{
interfaces . Add ( ids );
}
else if ( member is NamespaceDeclarationSyntax nds )
{
foreach ( var inside in nds . Members )
if ( inside is InterfaceDeclarationSyntax ids2 )
if ( ContainsSOIAttribute ( ids2 . AttributeLists , context , ids2 ))
interfaces . Add ( ids2 );
}
}
}
GenerateCode ( context , context . Compilation , interfaces . ToImmutableArray ());
2025-02-06 09:18:52 +03:00
}
2025-02-22 09:28:59 +03:00
2025-02-20 16:10:09 +03:00
private bool ContainsSOIAttribute ( SyntaxList < AttributeListSyntax > attributes , GeneratorExecutionContext context ,
InterfaceDeclarationSyntax interfaceDeclarationSyntax )
{
2025-03-07 09:01:21 +03:00
foreach ( var attributeSyntax in
attributes . SelectMany ( attributeListSyntax => attributeListSyntax . Attributes ))
2025-02-20 16:10:09 +03:00
{
if ( context . Compilation . GetSemanticModel ( interfaceDeclarationSyntax . SyntaxTree )
. GetSymbolInfo ( attributeSyntax ). Symbol is not IMethodSymbol attributeSymbol )
continue ; // if we can't get the symbol, ignore it
2025-02-18 00:10:53 +03:00
2025-02-20 16:10:09 +03:00
string attributeName = attributeSymbol . ContainingType . ToDisplayString ();
2025-02-18 00:10:53 +03:00
2025-02-20 16:10:09 +03:00
// Check the full name of the [Report] attribute.
if ( attributeName == "mROA.Implementation.Attributes.SharedObjectInterfaceAttribute" )
return true ;
}
2025-02-18 00:10:53 +03:00
2025-02-20 16:10:09 +03:00
return false ;
}
2025-03-06 14:10:52 +03:00
2025-03-07 09:01:21 +03:00
private List < IMethodSymbol > CollectMembers ( INamedTypeSymbol type )
2025-03-06 14:10:52 +03:00
{
var methods = type . GetMembers (). OfType < IMethodSymbol >(). ToList ();
foreach ( var inner in type . AllInterfaces )
{
methods . AddRange ( inner . GetMembers (). OfType < IMethodSymbol >());
}
2025-03-07 09:01:21 +03:00
methods . RemoveAll ( m => m . Name == "Dispose" );
2025-03-06 14:10:52 +03:00
return methods . OrderBy ( i => i . Name ). ToList ();
}
2025-02-18 00:10:53 +03:00
}
2025-02-04 19:02:39 +03:00
}