-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Expand file tree
/
Copy pathSyntaxGeneratorExtensions.cs
More file actions
143 lines (129 loc) · 7.28 KB
/
SyntaxGeneratorExtensions.cs
File metadata and controls
143 lines (129 loc) · 7.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Transactions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.DotNet.ApiSymbolExtensions;
using Microsoft.DotNet.ApiSymbolExtensions.Filtering;
namespace Microsoft.DotNet.GenAPI
{
internal static class SyntaxGeneratorExtensions
{
// Creates a declaration matching an existing symbol.
// The reason of having this similar to `SyntaxGenerator.Declaration` extension method is that
// SyntaxGenerator does not generates attributes neither for types, neither for members.
public static SyntaxNode DeclarationExt(this SyntaxGenerator syntaxGenerator, ISymbol symbol, ISymbolFilter symbolFilter)
{
if (symbol.Kind == SymbolKind.NamedType)
{
INamedTypeSymbol type = (INamedTypeSymbol)symbol;
switch (type.TypeKind)
{
case TypeKind.Class:
case TypeKind.Struct:
case TypeKind.Interface:
TypeDeclarationSyntax typeDeclaration = (TypeDeclarationSyntax)syntaxGenerator.Declaration(symbol);
if (type.IsRecord && type.TryGetRecordConstructor(out IMethodSymbol? recordConstructor))
{
// if the type is a record and we can find it's parameters, use `record Name(parameters...)` syntax.
typeDeclaration = typeDeclaration.WithParameterList(
SyntaxFactory.ParameterList(
SyntaxFactory.SeparatedList<ParameterSyntax>(
recordConstructor.Parameters.Select(p => (ParameterSyntax)syntaxGenerator.ParameterDeclaration(p)))));
}
return typeDeclaration
.WithBaseList(syntaxGenerator.GetBaseTypeList(type, symbolFilter))
.WithMembers(new SyntaxList<MemberDeclarationSyntax>());
case TypeKind.Enum:
EnumDeclarationSyntax enumDeclaration = (EnumDeclarationSyntax)syntaxGenerator.Declaration(symbol);
return enumDeclaration.WithMembers(new SeparatedSyntaxList<EnumMemberDeclarationSyntax>());
}
}
if (symbol.Kind == SymbolKind.Method)
{
IMethodSymbol method = (IMethodSymbol)symbol;
if (method.MethodKind == MethodKind.Constructor)
{
INamedTypeSymbol? baseType = method.ContainingType.BaseType;
if (baseType != null)
{
IEnumerable<IMethodSymbol> baseConstructors = baseType.Constructors.Where(symbolFilter.Include);
// If the base type does not have default constructor.
if (baseConstructors.Any() && baseConstructors.All(c => !c.Parameters.IsEmpty))
{
IOrderedEnumerable<IMethodSymbol> baseTypeConstructors = baseConstructors
.Where(c => c.GetAttributes().All(a => !a.IsObsoleteWithUsageTreatedAsCompilationError()))
.OrderBy(c => c.Parameters.Length);
if (baseTypeConstructors.Any())
{
IMethodSymbol constructor = baseTypeConstructors.First();
ConstructorDeclarationSyntax declaration = (ConstructorDeclarationSyntax)syntaxGenerator.Declaration(method);
if (!declaration.Modifiers.Any(m => m.RawKind == (int)SyntaxKind.UnsafeKeyword) &&
// if at least one parameter of a base constructor is raw pointer type
constructor.Parameters.Any(p => p.Type.TypeKind == TypeKind.Pointer))
{
declaration = declaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.UnsafeKeyword));
}
return declaration.WithInitializer(constructor.GenerateBaseConstructorInitializer());
}
}
}
}
}
if (symbol is IEventSymbol eventSymbol && !eventSymbol.IsAbstract)
{
// adds generation of add & remove accessors for the non abstract events.
return syntaxGenerator.CustomEventDeclaration(eventSymbol.Name,
syntaxGenerator.TypeExpression(eventSymbol.Type),
eventSymbol.DeclaredAccessibility,
DeclarationModifiers.From(eventSymbol));
}
if (symbol is IPropertySymbol propertySymbol)
{
// Explicitly implemented indexers do not set IsIndexer
// https://github.com/dotnet/roslyn/issues/53911
if (!propertySymbol.IsIndexer && propertySymbol.ExplicitInterfaceImplementations.Any(i => i.IsIndexer))
{
return syntaxGenerator.IndexerDeclaration(propertySymbol);
}
}
try
{
return syntaxGenerator.Declaration(symbol);
}
catch (ArgumentException ex)
{
// re-throw the ArgumentException with the symbol that caused it.
throw new ArgumentException(ex.Message, symbol.ToDisplayString(), innerException: ex);
}
}
// Gets the list of base class and interfaces for a given symbol INamedTypeSymbol.
private static BaseListSyntax? GetBaseTypeList(this SyntaxGenerator syntaxGenerator,
INamedTypeSymbol type,
ISymbolFilter symbolFilter)
{
List<BaseTypeSyntax> baseTypes = [];
if (type.TypeKind == TypeKind.Class && type.BaseType != null && symbolFilter.Include(type.BaseType))
{
TypeSyntax baseTypeSyntax = (TypeSyntax)syntaxGenerator.TypeExpression(type.BaseType);
if (type.BaseType.IsRecord && type.BaseType.TryGetRecordConstructor(out IMethodSymbol? recordConstructor))
{
baseTypes.Add(SyntaxFactory.PrimaryConstructorBaseType(baseTypeSyntax, recordConstructor.CreateDefaultArgumentList()));
}
else
{
baseTypes.Add(SyntaxFactory.SimpleBaseType(baseTypeSyntax));
}
}
// includes only interfaces that were not filtered out by the given ISymbolFilter or none of TypeParameters were filtered out.
baseTypes.AddRange(type.Interfaces
.Where(i => symbolFilter.Include(i) && !i.HasInaccessibleTypeArgument(symbolFilter))
.Select(i => SyntaxFactory.SimpleBaseType((TypeSyntax)syntaxGenerator.TypeExpression(i))));
return baseTypes.Count > 0 ?
SyntaxFactory.BaseList(SyntaxFactory.SeparatedList(baseTypes)) :
null;
}
}
}