182 lines
6.5 KiB
C#
182 lines
6.5 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CSharp;
|
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
|
|
namespace InnovEnergy.Lib.SrcGen;
|
|
|
|
public static class SyntaxUtils
|
|
{
|
|
// const String DebugFile = "/home/eef/sync/work/Code/innovenergy/git/csharp/Lib/SrcGen/Debug.txt";
|
|
//
|
|
// static SyntaxUtils() => File.WriteAllText(DebugFile, "");
|
|
|
|
public static Boolean HasExtendsAttribute(this TypeDeclarationSyntax td)
|
|
{
|
|
return td
|
|
.GetAttributes()
|
|
.Select(a => a.Name)
|
|
.OfType<GenericNameSyntax>()
|
|
.Any(n => n.Identifier.ValueText == "Extends");
|
|
}
|
|
|
|
|
|
public static T GetAncestor<T>(this SyntaxNode sn) where T: SyntaxNode
|
|
{
|
|
return sn
|
|
.Ancestors()
|
|
.OfType<T>()
|
|
.First();
|
|
}
|
|
|
|
|
|
public static IEnumerable<AttributeSyntax> GetGenerateAttributes(this SyntaxNode sn)
|
|
{
|
|
return sn
|
|
.DescendantNodes()
|
|
.Where(n => n is AttributeSyntax { Name : GenericNameSyntax { Identifier.ValueText: "Generate" } })
|
|
.OfType<AttributeSyntax>();
|
|
}
|
|
|
|
|
|
public static IEnumerable<AttributeSyntax> GetNestPropertiesAttributes(this SyntaxNode sn)
|
|
{
|
|
return sn
|
|
.DescendantNodes()
|
|
.Where(n => n is AttributeSyntax { Name : IdentifierNameSyntax { Identifier.ValueText: "NestProperties" } })
|
|
.OfType<AttributeSyntax>();
|
|
}
|
|
|
|
|
|
|
|
|
|
public static IEnumerable<AttributeSyntax> GetAttributes(this MemberDeclarationSyntax member)
|
|
{
|
|
return member
|
|
.AttributeLists
|
|
.SelectMany(al => al.Attributes);
|
|
}
|
|
|
|
// public static T Debug<T>(this T t, String? title = null)
|
|
// {
|
|
// var prefix = title is null ? "" : title + ": ";
|
|
// var contents = t is null ? "<null>" : t.ToString();
|
|
//
|
|
// File.AppendAllText(DebugFile, prefix + contents + '\n');
|
|
// return t;
|
|
// }
|
|
|
|
public static IEnumerable<AttributeSyntax> GetAttributes(this SyntaxNode node, String attributeName)
|
|
{
|
|
return node
|
|
.DescendantTokens()
|
|
.Where(st => st.ValueText == attributeName)
|
|
.Where(st => st.IsKind(SyntaxKind.IdentifierToken))
|
|
.Select(st => st.Parent?.Parent)
|
|
.OfType<AttributeSyntax>();
|
|
}
|
|
|
|
public static IEnumerable<T> ChildNodes<T>(this SyntaxNode node) where T : SyntaxNode
|
|
{
|
|
return node.ChildNodes().OfType<T>();
|
|
}
|
|
|
|
public static IEnumerable<T> DescendantNodes<T>(this SyntaxNode node) where T : SyntaxNode
|
|
{
|
|
return node.DescendantNodes().OfType<T>();
|
|
}
|
|
|
|
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
|
public static T ReplaceIdentifiers<T>(this T root, String idToReplace, String replacementId) where T: SyntaxNode
|
|
{
|
|
var newIdentifier = SyntaxFactory.Identifier(replacementId);
|
|
|
|
return root
|
|
.EditNodes()
|
|
.OfType<IdentifierNameSyntax>()
|
|
.Where(n => n.Identifier.ValueText == idToReplace)
|
|
.Replace(n => n.WithIdentifier(newIdentifier).WithTriviaFrom(n))
|
|
|
|
.EditNodes()
|
|
.OfType<ConstructorDeclarationSyntax>()
|
|
.Where(n => n.Identifier.ValueText == idToReplace)
|
|
.Replace(n => n.WithIdentifier(newIdentifier).WithTriviaFrom(n))
|
|
|
|
.EditNodes()
|
|
.OfType<TypeDeclarationSyntax>()
|
|
.Where(n => n.Identifier.ValueText == idToReplace)
|
|
.Replace(n => n.WithIdentifier(newIdentifier.WithTrailingTrivia(NewLine)));
|
|
}
|
|
|
|
|
|
private static readonly SyntaxTrivia NewLine = SyntaxFactory.SyntaxTrivia(SyntaxKind.EndOfLineTrivia, "\n");
|
|
private static readonly SyntaxTrivia WhiteSpace = SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " ");
|
|
|
|
private static readonly SyntaxToken PartialKeyword = SyntaxFactory
|
|
.Token(SyntaxKind.PartialKeyword)
|
|
.WithTrailingTrivia(WhiteSpace);
|
|
|
|
public static CompilationUnitSyntax AddPartialModifier(this CompilationUnitSyntax baseFile)
|
|
{
|
|
return baseFile
|
|
.EditNodes()
|
|
.OfType<StructDeclarationSyntax>()
|
|
.Where(s => !s.Modifiers.Contains(PartialKeyword))
|
|
.Replace(s => s.AddModifiers(PartialKeyword));
|
|
}
|
|
|
|
|
|
public static T GetDefinition<T>(this Compilation compilation, TypeSyntax baseTypeDeclaration) where T : SyntaxNode
|
|
{
|
|
return compilation
|
|
.GetDefinitions(baseTypeDeclaration)
|
|
.OfType<T>()
|
|
.Single();
|
|
}
|
|
|
|
public static IEnumerable<T> GetDefinitions<T>(this Compilation compilation, TypeSyntax baseTypeDeclaration) where T : SyntaxNode
|
|
{
|
|
return compilation
|
|
.GetDefinitions(baseTypeDeclaration)
|
|
.OfType<T>();
|
|
}
|
|
|
|
public static IEnumerable<SyntaxNode> GetDefinitions(this Compilation compilation, TypeSyntax baseTypeDeclaration)
|
|
{
|
|
return compilation
|
|
.GetSemanticModel(baseTypeDeclaration.SyntaxTree)
|
|
.GetTypeInfo(baseTypeDeclaration)
|
|
.Type?
|
|
.DeclaringSyntaxReferences
|
|
.Select(r => r.GetSyntax())
|
|
?? Enumerable.Empty<SyntaxNode>();
|
|
}
|
|
|
|
public static IEnumerable<TypeSyntax> GetGenericArguments(this AttributeSyntax attribute) => attribute.Name switch
|
|
{
|
|
GenericNameSyntax gns => gns.TypeArgumentList.Arguments,
|
|
_ => Enumerable.Empty<TypeSyntax>()
|
|
};
|
|
|
|
public static TypeSyntax GetGenericArgument(this AttributeSyntax attribute) => attribute.GetGenericArguments().Single();
|
|
|
|
public static CompilationUnitSyntax RemoveNotImplemented(this CompilationUnitSyntax baseFile)
|
|
{
|
|
return baseFile
|
|
.EditNodes()
|
|
.OfType<IdentifierNameSyntax>()
|
|
.Where(i => i.Identifier.ValueText == nameof(NotImplementedException))
|
|
.HasAncestor<ThrowExpressionSyntax>()
|
|
.GetAncestor<MemberDeclarationSyntax>()
|
|
.Remove();
|
|
}
|
|
|
|
public static String GetGeneratedPath(this String path, String postfix = "generated")
|
|
{
|
|
var ext = Path.GetExtension(path);
|
|
var name = Path.GetFileNameWithoutExtension(path);
|
|
var dir = Path.GetDirectoryName(path);
|
|
|
|
return $"{dir}/{name}.{postfix}{ext}";
|
|
}
|
|
} |