* Implicit handlers building extensions generator refactored

* Telegram.Bot package updated
This commit is contained in:
2025-08-18 20:32:24 +04:00
parent cf598ea91e
commit c809470fb0
33 changed files with 654 additions and 150 deletions
@@ -4,14 +4,36 @@ using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Immutable;
using System.Text;
using Telegrator.RoslynExtensions;
using Telegrator.RoslynGenerators.RoslynExtensions;
#if DEBUG
using System.Diagnostics;
#endif
namespace Telegrator.RoslynGenerators
{
[Generator(LanguageNames.CSharp)]
public class ImplicitHandlerBuilderExtensionsGenerator : IIncrementalGenerator
{
private static readonly string[] DefaultUsings =
[
"Telegrator.Handlers.Building",
"Telegrator.Handlers.Building.Components"
];
private static readonly ParameterSyntax ExtensionMethodThisParam = SyntaxFactory.Parameter(SyntaxFactory.Identifier("builder")).WithType(SyntaxFactory.IdentifierName("TBuilder").WithLeadingTrivia(SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " ")).WithTrailingTrivia(WhitespaceTrivia)).WithModifiers([SyntaxFactory.Token(SyntaxKind.ThisKeyword)]);
private static readonly MemberAccessExpressionSyntax BuilderAdderMethodAccessExpression = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName("builder"), SyntaxFactory.IdentifierName("AddTargetedFilters"));
private static readonly IEqualityComparer<UsingDirectiveSyntax> UsingEqualityComparer = new UsingDirectiveEqualityComparer();
private static SyntaxTrivia TabulationTrivia => SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, "\t");
private static SyntaxTrivia WhitespaceTrivia => SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " ");
private static SyntaxTrivia NewLineTrivia => SyntaxFactory.SyntaxTrivia(SyntaxKind.EndOfLineTrivia, "\n");
public void Initialize(IncrementalGeneratorInitializationContext context)
{
#if DEBUG
Debugger.Launch();
#endif
IncrementalValueProvider<ImmutableArray<ClassDeclarationSyntax>> pipeline = context.SyntaxProvider
.CreateSyntaxProvider(SyntaxPredicate, SyntaxTransform)
.Where(declaration => declaration != null)
@@ -20,12 +42,10 @@ namespace Telegrator.RoslynGenerators
context.RegisterImplementationSourceOutput(pipeline, GenerateSource);
}
private static bool SyntaxPredicate(SyntaxNode node, CancellationToken _)
private static bool SyntaxPredicate(SyntaxNode node, CancellationToken cancellationToken)
{
if (node is not ClassDeclarationSyntax)
return false;
return true;
cancellationToken.ThrowIfCancellationRequested();
return node is ClassDeclarationSyntax;
}
private static ClassDeclarationSyntax SyntaxTransform(GeneratorSyntaxContext context, CancellationToken _)
@@ -45,59 +65,246 @@ namespace Telegrator.RoslynGenerators
private static void GenerateSource(SourceProductionContext context, ImmutableArray<ClassDeclarationSyntax> declarations)
{
StringBuilder source = new StringBuilder();
StringBuilder debugExport = new StringBuilder("/*");
List<UsingDirectiveSyntax> usings = ParseUsings(DefaultUsings).ToList();
/*
Dictionary<string, string> targeters = [];
List<string> usingDirectives =
[
"using Telegrator.Handlers.Building;",
"using Telegrator.Handlers.Building.Components;"
];
*/
/*
StringBuilder sourceBuilder = new StringBuilder()
.AppendLine("namespace Telegrator")
.AppendLine("{")
.Append("\t//").Append(string.Join(", ", declarations.Select(decl => decl.Identifier.ToString()))).AppendLine()
.AppendLine("\tpublic static partial class HandlerBuilderExtensions")
.AppendLine("\t{");
*/
List<ClassDeclarationSyntax> lateTargeterClasses = [];
Dictionary<string, MethodDeclarationSyntax> targetters = [];
foreach (ClassDeclarationSyntax classDeclaration in declarations)
{
try
{
usingDirectives.UnionAdd(classDeclaration.FindAncestor<CompilationUnitSyntax>().Usings.Select(use => use.ToString()));
ParseClassDeclaration(sourceBuilder, classDeclaration, targeters);
}
catch (TargteterNotFoundException)
{
lateTargeterClasses.Add(classDeclaration);
string className = classDeclaration.Identifier.ToString();
if (className == "FilterAnnotation")
continue;
MethodDeclarationSyntax? targeter = classDeclaration.Members.OfType<MethodDeclarationSyntax>().SingleOrDefault(IsTargeterMethod);
if (targeter != null)
{
try
{
MethodDeclarationSyntax genTargeter = GenerateTargetterMethod(classDeclaration, targeter);
targetters.Add(className, genTargeter);
}
catch (Exception exc)
{
string errorFormat = string.Format("\nFailed to generate for {0} : {1}\n", classDeclaration.Identifier.ToString(), exc.ToString());
debugExport.AppendLine(errorFormat);
}
}
}
catch (Exception exc)
{
string errorFormat = string.Format("\t\t// failed to generate for {0} : {1}", classDeclaration.Identifier.ToString(), exc.GetType().Name);
sourceBuilder.AppendLine(errorFormat);
string errorFormat = string.Format("\nFailed to generate for {0} : {1}\n", classDeclaration.Identifier.ToString(), exc.ToString());
debugExport.AppendLine(errorFormat);
}
}
foreach (ClassDeclarationSyntax classDeclaration in lateTargeterClasses)
List<MethodDeclarationSyntax> extensions = [];
foreach (ClassDeclarationSyntax classDeclaration in declarations)
{
try
if (classDeclaration.Modifiers.HasModifiers("abstract"))
continue;
usings.UnionAdd(classDeclaration.FindAncestor<CompilationUnitSyntax>().Usings, UsingEqualityComparer);
MethodDeclarationSyntax targeter = FindTargetterMethod(targetters, classDeclaration);
if (classDeclaration.ParameterList != null && classDeclaration.BaseList != null)
{
usingDirectives.UnionAdd(classDeclaration.FindAncestor<CompilationUnitSyntax>().Usings.Select(use => use.ToString()));
ParseClassDeclaration(sourceBuilder, classDeclaration, targeters);
try
{
PrimaryConstructorBaseTypeSyntax primaryConstructor = (PrimaryConstructorBaseTypeSyntax)classDeclaration.BaseList.Types.ElementAt(0);
MethodDeclarationSyntax genExtension = GeneratedExtensionsMethod(classDeclaration, classDeclaration.ParameterList, primaryConstructor.ArgumentList, targeter);
extensions.Add(genExtension);
}
catch (Exception exc)
{
string errorFormat = string.Format("\nFailed to generate for {0} : {1}\n", classDeclaration.Identifier.ToString(), exc.ToString());
debugExport.AppendLine(errorFormat);
}
}
catch (Exception exc)
foreach (ConstructorDeclarationSyntax ctor in GetConstructors(classDeclaration))
{
string errorFormat = string.Format("\t\t// failed to generate for {0} : {1}", classDeclaration.Identifier.ToString(), exc.GetType().Name);
sourceBuilder.AppendLine(errorFormat);
try
{
if (ctor.Initializer == null)
continue;
MethodDeclarationSyntax genExtension = GeneratedExtensionsMethod(classDeclaration, ctor.ParameterList, ctor.Initializer.ArgumentList, targeter);
extensions.Add(genExtension);
}
catch (Exception exc)
{
string errorFormat = string.Format("\nFailed to generate for {0} : {1}\n", classDeclaration.Identifier.ToString(), exc.ToString());
debugExport.AppendLine(errorFormat);
}
}
}
sourceBuilder.AppendLine("\t}\n}");
sourceBuilder.Insert(0, string.Join("\n", usingDirectives.Select(use => use.ToString()).OrderBy(use => use)) + "\n\n");
context.AddSource("GeneratedHandlerBuilderExtensions.cs", sourceBuilder.ToString());
try
{
ClassDeclarationSyntax extensionsClass = SyntaxFactory.ClassDeclaration("HandlerBuilderExtensions")
.WithModifiers(Modifiers(SyntaxKind.PublicKeyword, SyntaxKind.StaticKeyword, SyntaxKind.PartialKeyword))
.AddMembers([.. targetters.Values, .. extensions])
.DecorateType(1);
NamespaceDeclarationSyntax namespaceDeclaration = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName("Telegrator"))
.WithMembers([extensionsClass])
.Decorate();
CompilationUnitSyntax compilationUnit = SyntaxFactory.CompilationUnit()
.WithUsings([.. usings])
.WithMembers([namespaceDeclaration]);
context.AddSource("GeneratedHandlerBuilderExtensions.cs", compilationUnit.ToFullString());
}
catch (Exception exc)
{
string errorFormat = string.Format("\nFailed to generate : {0}\n", exc.ToString());
debugExport.AppendLine(errorFormat);
}
context.AddSource("GeneratedHandlerBuilderExtensions.Debug.cs", debugExport.AppendLine("*/").ToString());
}
private static MethodDeclarationSyntax GenerateTargetterMethod(ClassDeclarationSyntax classDeclaration, MethodDeclarationSyntax targetterMethod)
{
SyntaxToken identifier = SyntaxFactory.Identifier(classDeclaration.Identifier.ToString() + "_" + targetterMethod.Identifier.ToString());
MethodDeclarationSyntax method = SyntaxFactory.MethodDeclaration(targetterMethod.ReturnType, identifier)
.WithParameterList(targetterMethod.ParameterList)
.WithModifiers(Modifiers(SyntaxKind.PrivateKeyword, SyntaxKind.StaticKeyword));
if (targetterMethod.Body != null)
method = method.WithBody(targetterMethod.Body);
if (targetterMethod.ExpressionBody != null)
method = method.WithExpressionBody(targetterMethod.ExpressionBody).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken));
return method.DecorateMember(2);
}
private static MethodDeclarationSyntax GeneratedExtensionsMethod(ClassDeclarationSyntax classDeclaration, ParameterListSyntax methodParameters, ArgumentListSyntax invokerArguments, MethodDeclarationSyntax targetterMethod)
{
ParameterListSyntax parameters = SyntaxFactory.ParameterList([ExtensionMethodThisParam, ..methodParameters.Parameters]);
TypeParameterListSyntax typeParameters = SyntaxFactory.TypeParameterList([SyntaxFactory.TypeParameter("TBuilder")]);
InvocationExpressionSyntax invocationExpression = SyntaxFactory.InvocationExpression(BuilderAdderMethodAccessExpression, AddTargeter(invokerArguments, targetterMethod));
BlockSyntax body = SyntaxFactory.Block(new StatementSyntax[]
{
SyntaxFactory.ExpressionStatement(invocationExpression),
SyntaxFactory.ReturnStatement(SyntaxFactory.IdentifierName("builder").WithLeadingTrivia(WhitespaceTrivia))
});
TypeParameterConstraintClauseSyntax typeParameterConstraint = SyntaxFactory.TypeParameterConstraintClause(SyntaxFactory.IdentifierName("TBuilder").WithLeadingTrivia(WhitespaceTrivia).WithTrailingTrivia(WhitespaceTrivia))
.WithConstraints([SyntaxFactory.TypeConstraint(SyntaxFactory.ParseTypeName("IHandlerBuilder").WithLeadingTrivia(WhitespaceTrivia))])
.WithLeadingTrivia(WhitespaceTrivia);
string filterName = classDeclaration.Identifier.ToString().Replace("Attribute", string.Empty);
if (filterName == "ChatType")
filterName = "InChatType"; // Because it conflicting
SyntaxToken identifier = SyntaxFactory.Identifier(filterName);
TypeSyntax returnType = SyntaxFactory.ParseTypeName("TBuilder").WithTrailingTrivia(WhitespaceTrivia);
SyntaxTriviaList xmlDoc = BuildExtensionXmlDocTrivia(classDeclaration, methodParameters);
MethodDeclarationSyntax method = SyntaxFactory.MethodDeclaration(returnType, identifier)
.WithParameterList(parameters)
.WithBody(body.DecorateBlock(2))
.WithTypeParameterList(typeParameters)
.WithModifiers(Modifiers(SyntaxKind.PublicKeyword, SyntaxKind.StaticKeyword))
.WithConstraintClauses([typeParameterConstraint])
.DecorateMember(2)
.WithLeadingTrivia(xmlDoc);
return method;
}
private static SyntaxTokenList Modifiers(params SyntaxKind[] kinds)
=> new SyntaxTokenList(kinds.Select(SyntaxFactory.Token).Select(mod => mod.WithTrailingTrivia(WhitespaceTrivia)));
private static IEnumerable<UsingDirectiveSyntax> ParseUsings(params string[] names) => names
.Select(name => SyntaxFactory.IdentifierName(name).WithLeadingTrivia(WhitespaceTrivia))
.Select(name => SyntaxFactory.UsingDirective(name).WithTrailingTrivia(NewLineTrivia));
private static ArgumentListSyntax AddTargeter(ArgumentListSyntax invokerArguments, MethodDeclarationSyntax targetterMethod)
=> SyntaxFactory.ArgumentList([SyntaxFactory.Argument(SyntaxFactory.IdentifierName(targetterMethod.Identifier)), ..invokerArguments.Arguments]);
private static bool IsTargeterMethod(MethodDeclarationSyntax method)
=> method.Identifier.ToString() == "GetFilterringTarget";
private static IEnumerable<ConstructorDeclarationSyntax> GetConstructors(ClassDeclarationSyntax classDeclaration)
=> classDeclaration.Members.OfType<ConstructorDeclarationSyntax>().Where(ctor => ctor.Modifiers.HasModifiers("public"));
private static MethodDeclarationSyntax FindTargetterMethod(Dictionary<string, MethodDeclarationSyntax> targeters, ClassDeclarationSyntax classDeclaration)
{
if (targeters.TryGetValue(classDeclaration.Identifier.ValueText, out MethodDeclarationSyntax targeter))
return targeter;
if (classDeclaration.BaseList != null && targeters.TryGetValue(classDeclaration.BaseList.Types.ElementAt(0).Type.ToString(), out targeter))
return targeter;
throw new TargteterNotFoundException();
}
private static SyntaxTriviaList BuildExtensionXmlDocTrivia(ClassDeclarationSyntax classDeclaration, ParameterListSyntax methodParameters)
{
StringBuilder summaryBuilder = new StringBuilder();
summaryBuilder
.Append("\t\t/// <summary>\n")
.Append("\t\t/// Adds a ").Append(classDeclaration.Identifier.ToString()).Append(" target filter to the handler builder.\n")
.Append("\t\t/// </summary>\n");
summaryBuilder
.AppendLine("\t\t/// <typeparam name=\"TBuilder\">The builder type.</typeparam>")
.AppendLine("\t\t/// <param name=\"builder\">The handler builder.</param>");
foreach (ParameterSyntax param in methodParameters.Parameters)
{
string name = param.Identifier.ToString();
summaryBuilder
.Append("\t\t/// <param name=\"").Append(name).Append("\">")
.Append("The ").Append(name)
.AppendLine(".</param>");
}
summaryBuilder.AppendLine("\t\t/// <returns>The same builder instance.</returns>");
summaryBuilder.Append("\t\t");
return SyntaxFactory.ParseLeadingTrivia(summaryBuilder.ToString());
}
private class UsingDirectiveEqualityComparer : IEqualityComparer<UsingDirectiveSyntax>
{
public bool Equals(UsingDirectiveSyntax x, UsingDirectiveSyntax y)
{
return x.ToString() == y.ToString();
}
public int GetHashCode(UsingDirectiveSyntax obj)
{
return obj.GetHashCode();
}
}
/*
private static void ParseClassDeclaration(StringBuilder sourceBuilder, ClassDeclarationSyntax classDeclaration, Dictionary<string, string> targeters)
{
string className = classDeclaration.Identifier.ToString();
@@ -130,7 +337,7 @@ namespace Telegrator.RoslynGenerators
classTargetterMethodName = targeters[baseClassName];
}
if (classDeclaration.Modifiers.Any(keyword => keyword.ValueText == "abstract"))
if (classDeclaration.Modifiers.HasModifiers("abstract"))
return;
if (classDeclaration.ParameterList != null)
@@ -157,7 +364,9 @@ namespace Telegrator.RoslynGenerators
sourceBuilder.AppendLine();
}
}
*/
/*
private static void RenderExtensionMethod(StringBuilder sourceBuilder, string filterName, string classTargetterMethodName, SeparatedSyntaxList<ParameterSyntax> parameters, SeparatedSyntaxList<ArgumentSyntax> arguments)
{
if (filterName == "ChatType")
@@ -205,5 +414,6 @@ namespace Telegrator.RoslynGenerators
sourceBuilder.Append(targeterMethod.Body.ToFullString());
}
}
*/
}
}
@@ -0,0 +1,64 @@
namespace Telegrator.RoslynExtensions
{
public static class CollectionsExtensions
{
public static IEnumerable<TSource> Combine<TSource>(params IEnumerable<TSource>[] collections)
=> collections.SelectMany(x => x);
public static IEnumerable<TSource> IntersectBy<TSource, TValue>(this IEnumerable<TSource> first, IEnumerable<TValue> second, Func<TSource, TValue> selector)
{
foreach (TSource item in first)
{
TValue value = selector(item);
if (second.Contains(value))
yield return item;
}
}
public static IList<TValue> UnionAdd<TValue>(this IList<TValue> source, IEnumerable<TValue> toUnion, IEqualityComparer<TValue> comparer)
{
foreach (TValue toUnionValue in toUnion)
{
if (!source.Contains(toUnionValue, comparer))
source.Add(toUnionValue);
}
return source;
}
public static void UnionAdd<TSource>(this ICollection<TSource> collection, IEnumerable<TSource> target)
{
foreach (TSource item in target)
{
if (!collection.Contains(item))
collection.Add(item);
}
}
public static void UnionAdd<TSource>(this SortedList<TSource, TSource> collection, IEnumerable<TSource> target)
{
foreach (TSource item in target)
{
if (!collection.Values.Contains(item))
collection.Add(item, item);
}
}
public static int IndexOf<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
int index = 0;
foreach (T item in source)
{
if (predicate.Invoke(item))
return index;
index++;
}
return -1;
}
public static IEnumerable<T> Repeat<T>(this T item, int times)
=> Enumerable.Range(0, times).Select(_ => item);
}
}
@@ -0,0 +1,16 @@
using Microsoft.CodeAnalysis;
namespace Telegrator.RoslynExtensions
{
public static class DiagnosticsHelper
{
public static Diagnostic Create(this DiagnosticDescriptor descriptor, Location? location, params object[] messageArgs)
=> Diagnostic.Create(descriptor, location, messageArgs);
public static void Report(this Diagnostic diagnostic, SourceProductionContext context)
=> context.ReportDiagnostic(diagnostic);
public static void Report(this DiagnosticDescriptor descriptor, SourceProductionContext context, Location? location, params object[] messageArgs)
=> descriptor.Create(location, messageArgs).Report(context);
}
}
@@ -0,0 +1,7 @@
namespace Telegrator.RoslynExtensions;
public class TargteterNotFoundException() : Exception() { }
public class BaseClassTypeNotFoundException() : Exception() { }
public class AncestorNotFoundException : Exception { }
@@ -0,0 +1,39 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Telegrator.RoslynExtensions;
namespace Telegrator.RoslynGenerators.RoslynExtensions
{
public static class MemberDeclarationSyntaxExtensions
{
private static SyntaxTrivia TabulationTrivia => SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, "\t");
private static SyntaxTrivia WhitespaceTrivia => SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " ");
private static SyntaxTrivia NewLineTrivia => SyntaxFactory.SyntaxTrivia(SyntaxKind.EndOfLineTrivia, "\n");
public static SyntaxTokenList Decorate(this SyntaxTokenList tokens)
=> new SyntaxTokenList(tokens.Select(token => token.WithoutTrivia().WithTrailingTrivia(WhitespaceTrivia)).ToArray());
public static BlockSyntax DecorateBlock(this BlockSyntax block, int times) => block
.WithStatements([.. block.Statements.Select(statement => statement.DecorateStatememnt(times + 1))])
.WithOpenBraceToken(SyntaxFactory.Token(SyntaxKind.OpenBraceToken).WithLeadingTrivia(WhitespaceTrivia).WithTrailingTrivia(NewLineTrivia))
.WithCloseBraceToken(SyntaxFactory.Token(SyntaxKind.CloseBraceToken).WithLeadingTrivia(TabulationTrivia.Repeat(times)).WithTrailingTrivia(NewLineTrivia));
public static T DecorateStatememnt<T>(this T statememnt, int times) where T : StatementSyntax => statememnt
.WithoutTrivia().WithLeadingTrivia(TabulationTrivia.Repeat(times)).WithTrailingTrivia(NewLineTrivia);
public static T DecorateMember<T>(this T typeDeclaration, int times) where T : MemberDeclarationSyntax => typeDeclaration
.WithoutTrivia().WithLeadingTrivia(TabulationTrivia.Repeat(times)).WithTrailingTrivia(NewLineTrivia);
public static NamespaceDeclarationSyntax Decorate(this NamespaceDeclarationSyntax namespaceDeclaration) => namespaceDeclaration
.WithName(namespaceDeclaration.Name.WithoutTrivia().WithLeadingTrivia(WhitespaceTrivia))
.WithOpenBraceToken(SyntaxFactory.Token(SyntaxKind.OpenBraceToken).WithLeadingTrivia(NewLineTrivia).WithTrailingTrivia(NewLineTrivia))
.WithCloseBraceToken(SyntaxFactory.Token(SyntaxKind.CloseBraceToken));
public static T DecorateType<T>(this T typeDeclaration, int times = 1) where T : TypeDeclarationSyntax => (T)typeDeclaration
.WithoutTrivia().WithLeadingTrivia(TabulationTrivia.Repeat(times))
.WithIdentifier(typeDeclaration.Identifier.WithoutTrivia().WithLeadingTrivia(WhitespaceTrivia).WithTrailingTrivia(NewLineTrivia))
.WithOpenBraceToken(SyntaxFactory.Token(SyntaxKind.OpenBraceToken).WithLeadingTrivia(TabulationTrivia.Repeat(times)).WithTrailingTrivia(NewLineTrivia))
.WithCloseBraceToken(SyntaxFactory.Token(SyntaxKind.CloseBraceToken).WithLeadingTrivia(TabulationTrivia.Repeat(times)).WithTrailingTrivia(NewLineTrivia));
}
}
@@ -0,0 +1,10 @@
using System.Text;
namespace Telegrator.RoslynExtensions
{
public static class StringBuilderExtensions
{
public static StringBuilder AppendTabs(this StringBuilder builder, int count)
=> builder.Append(new string('\t', count));
}
}
@@ -0,0 +1,21 @@
namespace Telegrator.RoslynExtensions
{
public static class StringExtensions
{
public static string FirstLetterToUpper(this string target)
{
char[] chars = target.ToCharArray();
int index = chars.IndexOf(char.IsLetter);
chars[index] = char.ToUpper(chars[index]);
return new string(chars);
}
public static string FirstLetterToLower(this string target)
{
char[] chars = target.ToCharArray();
int index = chars.IndexOf(char.IsLetter);
chars[index] = char.ToLower(chars[index]);
return new string(chars);
}
}
}
@@ -0,0 +1,28 @@
using Microsoft.CodeAnalysis;
namespace Telegrator.RoslynExtensions;
public static class SymbolsExtensions
{
public static bool IsAssignableFrom(this ITypeSymbol symbol, string className)
{
if (symbol.BaseType == null)
return false;
if (symbol.BaseType.Name == className)
return true;
return symbol.BaseType.IsAssignableFrom(className);
}
public static ITypeSymbol? Cast(this ITypeSymbol symbol, string className)
{
if (symbol.BaseType == null)
return null;
if (symbol.BaseType.Name == className)
return symbol.BaseType;
return symbol.BaseType.Cast(className);
}
}
@@ -0,0 +1,74 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Telegrator.RoslynExtensions
{
public static class SyntaxNodesExtensions
{
public static T FindAncestor<T>(this SyntaxNode node) where T : SyntaxNode
{
if (node.Parent == null)
throw new AncestorNotFoundException();
if (node.Parent is T found)
return found;
return node.Parent.FindAncestor<T>();
}
public static bool TryFindAncestor<T>(this SyntaxNode node, out T syntax) where T : SyntaxNode
{
if (node.Parent == null)
{
syntax = null!;
return false;
}
if (node.Parent is T found)
{
syntax = found;
return true;
}
return node.Parent.TryFindAncestor(out syntax);
}
public static INamedTypeSymbol TryGetNamedType(this BaseTypeDeclarationSyntax syntax, Compilation compilation)
{
SemanticModel semanticModel = compilation.GetSemanticModel(syntax.SyntaxTree);
return semanticModel.GetDeclaredSymbol(syntax)!;
}
public static string GetBaseTypeSyntaxName(this BaseTypeSyntax baseClassSyntax)
{
if (baseClassSyntax is PrimaryConstructorBaseTypeSyntax parimaryConstructor)
return parimaryConstructor.Type.ToString();
if (baseClassSyntax is SimpleBaseTypeSyntax simpleBaseType)
return simpleBaseType.Type.ToString();
throw new BaseClassTypeNotFoundException();
}
public static int CountParentTree(this SyntaxNode node)
{
int count = 0;
SyntaxNode inspectNode = node;
while (inspectNode.Parent != null)
{
inspectNode = inspectNode.Parent;
count++;
}
return count;
}
public static SeparatedSyntaxList<TNode> ToSeparatedSyntaxList<TNode>(this IEnumerable<TNode> elements) where TNode : SyntaxNode
=> new SeparatedSyntaxList<TNode>().AddRange(elements);
public static SyntaxList<TNode> ToSyntaxList<TNode>(this IEnumerable<TNode> elements) where TNode : SyntaxNode
=> new SyntaxList<TNode>().AddRange(elements);
}
}
@@ -0,0 +1,12 @@
using Microsoft.CodeAnalysis;
namespace Telegrator.RoslynExtensions
{
public static class SyntaxTokenExtensions
{
public static bool HasModifiers(this SyntaxTokenList modifiers, params string[] expected)
{
return modifiers.Count(mod => expected.Contains(mod.ToString())) == expected.Length;
}
}
}
@@ -6,9 +6,6 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<IsRoslynComponent>True</IsRoslynComponent>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
@@ -19,10 +16,5 @@
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.14.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Telegrator.RoslynExtensions\Telegrator.RoslynExtensions.csproj" PrivateAssets="all" />
<None Include="$(OutputPath)\*.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>
</Project>