Skip to content

Commit

Permalink
Merge pull request #38 from dotnet-campus/t/lindexi/Generator
Browse files Browse the repository at this point in the history
通过 SourceGenerator 生成代码
  • Loading branch information
walterlv authored Jun 30, 2023
2 parents 1c5446a + 725ab65 commit 1957721
Show file tree
Hide file tree
Showing 17 changed files with 778 additions and 3 deletions.
7 changes: 6 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@
<LangVersion>latest</LangVersion>
<DevelopmentDependency>true</DevelopmentDependency>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<Copyright>Copyright (c) 2018-2021 dotnet-campus</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<Copyright>Copyright (c) 2018-2023 dotnet-campus</Copyright>
<PackageProjectUrl>https://github.com/dotnet-campus/SourceFusion</PackageProjectUrl>
<RepositoryUrl>https://github.com/dotnet-campus/SourceFusion.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>source;dotnet;nuget;msbuild;compile</PackageTags>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
<!-- 现在 VisualStudio 2022 没有带上 .NET Framework 4.5 的负载,为了让大家打开项目就能构建,于是加上这个引用。详细请参阅 https://blog.walterlv.com/post/support-old-netfx-on-vs2022-or-later.html -->
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
</ItemGroup>
</Project>
34 changes: 32 additions & 2 deletions SourceFusion.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28606.126
# Visual Studio Version 17
VisualStudioVersion = 17.6.33723.286
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SourceFusion.Tool", "src\SourceFusion.Tool\SourceFusion.Tool.csproj", "{EFDCE47F-3FCE-4428-9F63-340806876FE5}"
EndProject
Expand Down Expand Up @@ -37,6 +37,21 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnetCampus.Telescope.NuGe
{556913DF-6532-4532-B514-2D1184ADC75B} = {556913DF-6532-4532-B514-2D1184ADC75B}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SourceGenerator", "SourceGenerator", "{A9879CB9-D164-4A0F-9F7A-D4B86C66BD68}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnetCampus.Telescope.SourceGeneratorAnalyzers", "src\TelescopeSourceGenerator\Analyzers\dotnetCampus.Telescope.SourceGeneratorAnalyzers.csproj", "{C8BB3F7F-344C-40B4-BF00-005575C8C91A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnetCampus.Telescope.SourceGeneratorAnalyzers.Demo", "src\TelescopeSourceGenerator\Demo\TelescopeSourceGeneratorDemo\dotnetCampus.Telescope.SourceGeneratorAnalyzers.Demo.csproj", "{350C2DAF-7AD8-4285-9C8F-737D4D078B65}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnetCampus.Telescope.SourceGeneratorAnalyzers.NuGet", "src\TelescopeSourceGenerator\NuGet\dotnetCampus.Telescope.SourceGeneratorAnalyzers.NuGet.csproj", "{F1E2D63D-A05A-4647-ADB3-750A9F651D22}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GitHub Actions", "GitHub Actions", "{5A85E9ED-415C-4720-BF26-8CF8A97CB832}"
ProjectSection(SolutionItems) = preProject
..\Ipc\.github\workflows\dotnet-core.yml = ..\Ipc\.github\workflows\dotnet-core.yml
..\Ipc\.github\workflows\dotnet-format.yml = ..\Ipc\.github\workflows\dotnet-format.yml
..\Ipc\.github\workflows\nuget-tag-publish.yml = ..\Ipc\.github\workflows\nuget-tag-publish.yml
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -71,13 +86,28 @@ Global
{84DA7EE9-B511-407E-A510-DF390C742F0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{84DA7EE9-B511-407E-A510-DF390C742F0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{84DA7EE9-B511-407E-A510-DF390C742F0D}.Release|Any CPU.Build.0 = Release|Any CPU
{C8BB3F7F-344C-40B4-BF00-005575C8C91A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C8BB3F7F-344C-40B4-BF00-005575C8C91A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C8BB3F7F-344C-40B4-BF00-005575C8C91A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C8BB3F7F-344C-40B4-BF00-005575C8C91A}.Release|Any CPU.Build.0 = Release|Any CPU
{350C2DAF-7AD8-4285-9C8F-737D4D078B65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{350C2DAF-7AD8-4285-9C8F-737D4D078B65}.Debug|Any CPU.Build.0 = Debug|Any CPU
{350C2DAF-7AD8-4285-9C8F-737D4D078B65}.Release|Any CPU.ActiveCfg = Release|Any CPU
{350C2DAF-7AD8-4285-9C8F-737D4D078B65}.Release|Any CPU.Build.0 = Release|Any CPU
{F1E2D63D-A05A-4647-ADB3-750A9F651D22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F1E2D63D-A05A-4647-ADB3-750A9F651D22}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F1E2D63D-A05A-4647-ADB3-750A9F651D22}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F1E2D63D-A05A-4647-ADB3-750A9F651D22}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{5978AAB0-C4C1-44AF-AB92-27F8ECBF6F69} = {02D03DE5-1A19-4002-92BD-0E3DC61A53C4}
{50AE25A2-DF1B-4DE9-A7BE-C406F6497376} = {78213588-C3B8-4E86-A19B-73DECD1ABA7B}
{C8BB3F7F-344C-40B4-BF00-005575C8C91A} = {A9879CB9-D164-4A0F-9F7A-D4B86C66BD68}
{350C2DAF-7AD8-4285-9C8F-737D4D078B65} = {A9879CB9-D164-4A0F-9F7A-D4B86C66BD68}
{F1E2D63D-A05A-4647-ADB3-750A9F651D22} = {A9879CB9-D164-4A0F-9F7A-D4B86C66BD68}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {22702A10-22E1-4670-8FCA-9581202E6CAB}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System.Collections.Immutable;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace dotnetCampus.Telescope.SourceGeneratorAnalyzers;

/// <summary>
/// 程序集里可能被标记导出的类型
/// </summary>
readonly struct AssemblyCandidateClassParseResult
{
public AssemblyCandidateClassParseResult(INamedTypeSymbol exportedTypeSymbol, ImmutableArray<AttributeData> attributes, GeneratorSyntaxContext generatorSyntaxContext)
{
ExportedTypeSymbol = exportedTypeSymbol;
Attributes = attributes;
GeneratorSyntaxContext = generatorSyntaxContext;
Success = true;
}

public AssemblyCandidateClassParseResult()
{
Success = false;
ExportedTypeSymbol = default!; // 这个瞬间就被过滤掉了,先不考虑可空的复杂写法了
Attributes = default;
GeneratorSyntaxContext = default;
}

/// <summary>
/// 导出类型的语义符号
/// </summary>
public INamedTypeSymbol ExportedTypeSymbol { get; }

/// <summary>
/// 导出类型的语法符号
/// </summary>
public ClassDeclarationSyntax ExportedTypeClassDeclarationSyntax => (ClassDeclarationSyntax) GeneratorSyntaxContext.Node;

/// <summary>
/// 类型标记的特性
/// </summary>
public ImmutableArray<AttributeData> Attributes { get; }

public GeneratorSyntaxContext GeneratorSyntaxContext { get; }

/// <summary>
/// 是否成功,用于过滤掉不满足条件的对象
/// </summary>
public bool Success { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace dotnetCampus.Telescope.SourceGeneratorAnalyzers;

/// <summary>
/// 被标记的类型的转换结果,约等于生成代码前的最终结果
/// </summary>
readonly struct MarkClassParseResult
{
public MarkClassParseResult(INamedTypeSymbol exportedTypeSymbol, ClassDeclarationSyntax exportedTypeClassDeclarationSyntax,
AttributeData matchAssemblyMarkAttributeData, AttributeSyntax matchAssemblyMarkAttributeSyntax,
MarkExportAttributeParseResult markExportAttributeParseResult,
GeneratorSyntaxContext generatorSyntaxContext)
{
ExportedTypeSymbol = exportedTypeSymbol;
ExportedTypeClassDeclarationSyntax = exportedTypeClassDeclarationSyntax;
MatchAssemblyMarkAttributeData = matchAssemblyMarkAttributeData;
MatchAssemblyMarkAttributeSyntax = matchAssemblyMarkAttributeSyntax;
MarkExportAttributeParseResult = markExportAttributeParseResult;
GeneratorSyntaxContext = generatorSyntaxContext;
}

/// <summary>
/// 导出的 class 类型的语义
/// </summary>
public INamedTypeSymbol ExportedTypeSymbol { get; }
/// <summary>
/// 导出的 class 类型的语法
/// </summary>
public ClassDeclarationSyntax ExportedTypeClassDeclarationSyntax { get; }
/// <summary>
/// 类型上标记的程序集指定特性的语义
/// </summary>
public AttributeData MatchAssemblyMarkAttributeData { get; }
/// <summary>
/// 类型上标记的程序集指定特性的语法
/// </summary>
public AttributeSyntax MatchAssemblyMarkAttributeSyntax { get; }

/// <summary>
/// 程序集特性里面的定义结果
/// </summary>
public MarkExportAttributeParseResult MarkExportAttributeParseResult { get; }

public GeneratorSyntaxContext GeneratorSyntaxContext { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Microsoft.CodeAnalysis;

namespace dotnetCampus.Telescope.SourceGeneratorAnalyzers;

readonly struct MarkExportAttributeParseResult
{
public MarkExportAttributeParseResult(bool success, ITypeSymbol baseClassOrInterfaceTypeInfo,
ITypeSymbol attributeTypeInfo)
{
Success = success;
BaseClassOrInterfaceTypeInfo = baseClassOrInterfaceTypeInfo;
AttributeTypeInfo = attributeTypeInfo;
}

public bool Success { get; }
public ITypeSymbol BaseClassOrInterfaceTypeInfo { get; }

public ITypeSymbol AttributeTypeInfo { get; }

/// <summary>
/// 获取表示失败的特性解析结果。
/// </summary>
public static MarkExportAttributeParseResult Failure { get; } = new MarkExportAttributeParseResult(false, default!, default!);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;

namespace dotnetCampus.Telescope.SourceGeneratorAnalyzers.Core;

static class AttributeCodeReWriter
{
/// <summary>
/// 从 <paramref name="attributeData"/> 转换为特性生成代码。从 `[Foo(xx, xxx)]` 语义转换为 `new Foo(xx, xxx)` 的生成代码
/// </summary>
/// <param name="attributeData"></param>
/// <returns></returns>
public static string GetAttributeCreatedCode(AttributeData attributeData)
{
// 放在特性的构造函数的参数列表,例如 [Foo(1,2,3)] 将会获取到 `1` `2` `3` 三个参数
var constructorArgumentCodeList = new List<string>();
foreach (TypedConstant constructorArgument in attributeData.ConstructorArguments)
{
var constructorArgumentCode = TypedConstantToCodeString(constructorArgument);

constructorArgumentCodeList.Add(constructorArgumentCode);
}

var namedArgumentCodeList = new List<(string propertyName, string valueCode)>();
foreach (var keyValuePair in attributeData.NamedArguments)
{
var key = keyValuePair.Key;

var typedConstant = keyValuePair.Value;
var argumentCode = TypedConstantToCodeString(typedConstant);

namedArgumentCodeList.Add((key, argumentCode));
}

return
$@"new {TypeSymbolHelper.TypeSymbolToFullName(attributeData.AttributeClass!)}({string.Join(",", constructorArgumentCodeList)})
{{
{string.Join(@",
", namedArgumentCodeList.Select(x => $"{x.propertyName} = {x.valueCode}"))}
}}";

static string TypedConstantToCodeString(TypedConstant typedConstant)
{
var constructorArgumentType = typedConstant.Type;
var constructorArgumentValue = typedConstant.Value;

string constructorArgumentCode;
switch (typedConstant.Kind)
{
case TypedConstantKind.Enum:
{
// "(Foo.Enum1) 1"
constructorArgumentCode =
$"({TypeSymbolHelper.TypeSymbolToFullName(typedConstant.Type!)}) {typedConstant.Value}";
break;
}
case TypedConstantKind.Type:
{
var typeSymbol = (ITypeSymbol?)constructorArgumentValue;
if (typeSymbol is null)
{
constructorArgumentCode = "null";
}
else
{
constructorArgumentCode = $"typeof({TypeSymbolHelper.TypeSymbolToFullName(typeSymbol)})";
}

break;
}
default:
{
constructorArgumentCode = typedConstant.Value?.ToString() ?? "null";
break;
}
}

return constructorArgumentCode;
}
}
}
Loading

0 comments on commit 1957721

Please sign in to comment.