This commit is contained in:
Stef Heyenrath
2022-05-07 09:35:40 +02:00
10 changed files with 116 additions and 50 deletions
@@ -1,4 +1,5 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Diagnostics.CodeAnalysis;
namespace ProxyInterfaceSourceGenerator.Extensions;
@@ -37,4 +38,54 @@ internal static class SyntaxNodeExtensions
return false;
}
}
/// <summary>
/// determine the namespace the class/enum/struct is declared in, if any
/// https://andrewlock.net/creating-a-source-generator-part-5-finding-a-type-declarations-namespace-and-type-hierarchy/
/// </summary>
/// <param name="syntaxNode"></param>
/// <returns>NameSpace</returns>
public static string GetNamespace(this SyntaxNode syntaxNode)
{
// If we don't have a namespace at all we'll return an empty string
// This accounts for the "default namespace" case
string nameSpace = string.Empty;
// Get the containing syntax node for the type declaration
// (could be a nested type, for example)
SyntaxNode? potentialNamespaceParent = syntaxNode.Parent;
// Keep moving "out" of nested classes etc until we get to a namespace
// or until we run out of parents
while (potentialNamespaceParent != null &&
potentialNamespaceParent is not NamespaceDeclarationSyntax
&& potentialNamespaceParent is not FileScopedNamespaceDeclarationSyntax)
{
potentialNamespaceParent = potentialNamespaceParent.Parent;
}
// Build up the final namespace by looping until we no longer have a namespace declaration
if (potentialNamespaceParent is BaseNamespaceDeclarationSyntax namespaceParent)
{
// We have a namespace. Use that as the type
nameSpace = namespaceParent.Name.ToString();
// Keep moving "out" of the namespace declarations until we
// run out of nested namespace declarations
while (true)
{
if (namespaceParent.Parent is not NamespaceDeclarationSyntax parent)
{
break;
}
// Add the outer namespace as a prefix to the final namespace
nameSpace = $"{namespaceParent.Name}.{nameSpace}";
namespaceParent = parent;
}
}
// return the final namespace
return nameSpace;
}
}
@@ -1,13 +1,13 @@
namespace ProxyInterfaceSourceGenerator.Models;
internal record ProxyData
(
string Namespace,
string ShortInterfaceName,
string FullInterfaceName,
string FullRawTypeName,
string ShortTypeName,
string FullTypeName,
List<string> Usings,
bool ProxyBaseClasses
);
internal class ProxyData
{
public string Namespace { get; init; }
public string ShortInterfaceName { get; init; }
public string FullInterfaceName { get; init; }
public string FullRawTypeName { get; init; }
public string ShortTypeName { get; init; }
public string FullTypeName { get; init; }
public List<string> Usings { get; init; }
public bool ProxyBaseClasses { get; init; }
}
@@ -15,10 +15,12 @@ internal class ProxyInterfaceCodeGenerator : ISourceGenerator
public void Initialize(GeneratorInitializationContext context)
{
//if (!System.Diagnostics.Debugger.IsAttached)
//{
// System.Diagnostics.Debugger.Launch();
//}
#if DEBUGATTACH
if (!System.Diagnostics.Debugger.IsAttached)
{
System.Diagnostics.Debugger.Launch();
}
#endif
context.RegisterForSyntaxNotifications(() => new ProxySyntaxReceiver());
}
@@ -25,6 +25,7 @@
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)Generated</CompilerGeneratedFilesOutputPath>
<DevelopmentDependency>true</DevelopmentDependency>
<ImplicitUsings>enable</ImplicitUsings>
<Configurations>Debug;Release;DebugAttach</Configurations>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
@@ -37,12 +38,12 @@
<ItemGroup>
<!--<PackageReference Include="AutoMapper" Version="10.1.1" />-->
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2">
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="3.10.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.10.0" PrivateAssets="all" />
<!--<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="3.10.0" PrivateAssets="all" />-->
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.1.0" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
@@ -57,8 +58,10 @@
</ItemGroup>
<ItemGroup>
<None Remove="bin\Debug\netstandard2.0\\ProxyInterfaceGenerator.dll" />
<None Remove="bin\Release\netstandard2.0\\ProxyInterfaceGenerator.dll" />
<None Remove="bin\Release\netstandard2.0\\ProxyInterfaceSourceGenerator.dll" />
</ItemGroup>
<ItemGroup>
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>
@@ -46,10 +46,9 @@ internal class ProxySyntaxReceiver : ISyntaxReceiver
var usings = new List<string>();
string ns = string.Empty;
if (interfaceDeclarationSyntax.TryGetParentSyntax(out NamespaceDeclarationSyntax? namespaceDeclarationSyntax))
string ns = interfaceDeclarationSyntax.GetNamespace();
if (!string.IsNullOrEmpty(ns))
{
ns = namespaceDeclarationSyntax.Name.ToString();
usings.Add(ns);
}
@@ -73,18 +72,18 @@ internal class ProxySyntaxReceiver : ISyntaxReceiver
{
proxyAllClasses = false;
}
data = new
(
ns,
interfaceDeclarationSyntax.Identifier.ToString(),
$"{ns}.{interfaceDeclarationSyntax.Identifier}",
rawTypeName,
ConvertTypeName(rawTypeName).Split('.').Last(), // ShortTypeName
ConvertTypeName(rawTypeName), // FullTypeName
usings,
proxyAllClasses
);
data = new ProxyData
{
Namespace = ns,
ShortInterfaceName = interfaceDeclarationSyntax.Identifier.ToString(),
FullInterfaceName = CreateFullBuilderClassName(ns, interfaceDeclarationSyntax), // $"{ns}.{interfaceDeclarationSyntax.Identifier}",
FullRawTypeName = rawTypeName,
ShortTypeName = ConvertTypeName(rawTypeName).Split('.').Last(),
FullTypeName = ConvertTypeName(rawTypeName),
Usings = usings,
ProxyBaseClasses = proxyAllClasses
};
return true;
}
@@ -95,4 +94,9 @@ internal class ProxySyntaxReceiver : ISyntaxReceiver
typeName :
$"{typeName.Replace("<", string.Empty).Replace(">", string.Empty).Replace(",", string.Empty).Trim()}`{typeName.Count(c => c == ',') + 1}";
}
private static string CreateFullBuilderClassName(string ns, BaseTypeDeclarationSyntax classDeclarationSyntax)
{
return !string.IsNullOrEmpty(ns) ? $"{ns}.{classDeclarationSyntax.Identifier}" : classDeclarationSyntax.Identifier.ToString();
}
}