-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #38 from dotnet-campus/t/lindexi/Generator
通过 SourceGenerator 生成代码
- Loading branch information
Showing
17 changed files
with
778 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
50 changes: 50 additions & 0 deletions
50
src/TelescopeSourceGenerator/Analyzers/Context_/AssemblyCandidateClassParseResult.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; } | ||
} |
47 changes: 47 additions & 0 deletions
47
src/TelescopeSourceGenerator/Analyzers/Context_/MarkClassParseResult.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; } | ||
} |
24 changes: 24 additions & 0 deletions
24
src/TelescopeSourceGenerator/Analyzers/Context_/MarkExportAttributeParseResult.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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!); | ||
} |
82 changes: 82 additions & 0 deletions
82
src/TelescopeSourceGenerator/Analyzers/Core/AttributeCodeReWriter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |
Oops, something went wrong.