Skip to content

Commit faba900

Browse files
committed
Internalized DeclarationComputer from Roslyn, bump to v1.3
Actually copied the files from CodeGeneration.Roslyn: https://github.com/AArnott/CodeGeneration.Roslyn/tree/782053e8db9122c1de994f6e973c6be36c4cac85/src/CodeGeneration.Roslyn/Roslyn These files will be dropped from future versions of that package, and are only internally needed for this project. So the files were copied, had type visibility changed to internal, and namespace changed to ImmutableObjectGraph.Generation.Roslyn. See: * AArnott/CodeGeneration.Roslyn#94 * AArnott/CodeGeneration.Roslyn#93
1 parent b5f7958 commit faba900

File tree

4 files changed

+375
-1
lines changed

4 files changed

+375
-1
lines changed
Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
#pragma warning disable // this came from Roslyn code
2+
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Immutable;
6+
using System.Linq;
7+
using System.Threading;
8+
using Microsoft.CodeAnalysis;
9+
using Microsoft.CodeAnalysis.CSharp;
10+
using Microsoft.CodeAnalysis.CSharp.Syntax;
11+
using Microsoft.CodeAnalysis.Text;
12+
using Roslyn.Utilities;
13+
14+
namespace ImmutableObjectGraph.Generation.Roslyn
15+
{
16+
internal class CSharpDeclarationComputer : DeclarationComputer
17+
{
18+
public static ImmutableArray<DeclarationInfo> GetDeclarationsInSpan(SemanticModel model, TextSpan span, bool getSymbol, CancellationToken cancellationToken)
19+
{
20+
var builder = ImmutableArray.CreateBuilder<DeclarationInfo>();
21+
ComputeDeclarations(model, model.SyntaxTree.GetRoot(cancellationToken),
22+
(node, level) => !node.Span.OverlapsWith(span) || InvalidLevel(level),
23+
getSymbol, builder, null, cancellationToken);
24+
return builder.ToImmutable();
25+
}
26+
27+
public static ImmutableArray<DeclarationInfo> GetDeclarationsInNode(SemanticModel model, SyntaxNode node, bool getSymbol, CancellationToken cancellationToken, int? levelsToCompute = null)
28+
{
29+
var builder = ImmutableArray.CreateBuilder<DeclarationInfo>();
30+
ComputeDeclarations(model, node, (n, level) => InvalidLevel(level), getSymbol, builder, levelsToCompute, cancellationToken);
31+
return builder.ToImmutable();
32+
}
33+
34+
private static bool InvalidLevel(int? level)
35+
{
36+
return level.HasValue && level.Value <= 0;
37+
}
38+
39+
private static int? DecrementLevel(int? level)
40+
{
41+
return level.HasValue ? level - 1 : level;
42+
}
43+
44+
internal static void ComputeDeclarations(
45+
SemanticModel model,
46+
SyntaxNode node,
47+
Func<SyntaxNode, int?, bool> shouldSkip,
48+
bool getSymbol,
49+
ImmutableArray<DeclarationInfo>.Builder builder,
50+
int? levelsToCompute,
51+
CancellationToken cancellationToken)
52+
{
53+
if (shouldSkip(node, levelsToCompute))
54+
{
55+
return;
56+
}
57+
58+
var newLevel = DecrementLevel(levelsToCompute);
59+
60+
switch (node.Kind())
61+
{
62+
case SyntaxKind.NamespaceDeclaration:
63+
{
64+
var ns = (NamespaceDeclarationSyntax)node;
65+
foreach (var decl in ns.Members) ComputeDeclarations(model, decl, shouldSkip, getSymbol, builder, newLevel, cancellationToken);
66+
builder.Add(GetDeclarationInfo(model, node, getSymbol, cancellationToken));
67+
68+
NameSyntax name = ns.Name;
69+
while (name.Kind() == SyntaxKind.QualifiedName)
70+
{
71+
name = ((QualifiedNameSyntax)name).Left;
72+
var declaredSymbol = getSymbol ? model.GetSymbolInfo(name, cancellationToken).Symbol : null;
73+
builder.Add(new DeclarationInfo(name, ImmutableArray<SyntaxNode>.Empty, declaredSymbol));
74+
}
75+
76+
return;
77+
}
78+
79+
case SyntaxKind.ClassDeclaration:
80+
case SyntaxKind.StructDeclaration:
81+
case SyntaxKind.InterfaceDeclaration:
82+
{
83+
var t = (TypeDeclarationSyntax)node;
84+
foreach (var decl in t.Members) ComputeDeclarations(model, decl, shouldSkip, getSymbol, builder, newLevel, cancellationToken);
85+
builder.Add(GetDeclarationInfo(model, node, getSymbol, cancellationToken));
86+
return;
87+
}
88+
89+
case SyntaxKind.EnumDeclaration:
90+
{
91+
var t = (EnumDeclarationSyntax)node;
92+
foreach (var decl in t.Members) ComputeDeclarations(model, decl, shouldSkip, getSymbol, builder, newLevel, cancellationToken);
93+
builder.Add(GetDeclarationInfo(model, node, getSymbol, cancellationToken));
94+
return;
95+
}
96+
97+
case SyntaxKind.EnumMemberDeclaration:
98+
{
99+
var t = (EnumMemberDeclarationSyntax)node;
100+
builder.Add(GetDeclarationInfo(model, node, getSymbol, t.EqualsValue, cancellationToken));
101+
return;
102+
}
103+
104+
case SyntaxKind.DelegateDeclaration:
105+
{
106+
var t = (DelegateDeclarationSyntax)node;
107+
builder.Add(GetDeclarationInfo(model, node, getSymbol, cancellationToken));
108+
return;
109+
}
110+
111+
case SyntaxKind.EventDeclaration:
112+
{
113+
var t = (EventDeclarationSyntax)node;
114+
foreach (var decl in t.AccessorList.Accessors) ComputeDeclarations(model, decl, shouldSkip, getSymbol, builder, newLevel, cancellationToken);
115+
builder.Add(GetDeclarationInfo(model, node, getSymbol, cancellationToken));
116+
return;
117+
}
118+
119+
case SyntaxKind.EventFieldDeclaration:
120+
case SyntaxKind.FieldDeclaration:
121+
{
122+
var t = (BaseFieldDeclarationSyntax)node;
123+
foreach (var decl in t.Declaration.Variables)
124+
{
125+
builder.Add(GetDeclarationInfo(model, decl, getSymbol, decl.Initializer, cancellationToken));
126+
}
127+
128+
return;
129+
}
130+
131+
case SyntaxKind.ArrowExpressionClause:
132+
{
133+
// Arrow expression clause declares getter symbol for properties and indexers.
134+
var parentProperty = node.Parent as BasePropertyDeclarationSyntax;
135+
if (parentProperty != null)
136+
{
137+
builder.Add(GetExpressionBodyDeclarationInfo(parentProperty, (ArrowExpressionClauseSyntax)node, model, getSymbol, cancellationToken));
138+
}
139+
140+
return;
141+
}
142+
143+
case SyntaxKind.PropertyDeclaration:
144+
{
145+
var t = (PropertyDeclarationSyntax)node;
146+
if (t.AccessorList != null)
147+
{
148+
foreach (var decl in t.AccessorList.Accessors) ComputeDeclarations(model, decl, shouldSkip, getSymbol, builder, newLevel, cancellationToken);
149+
}
150+
151+
if (t.ExpressionBody != null)
152+
{
153+
ComputeDeclarations(model, t.ExpressionBody, shouldSkip, getSymbol, builder, levelsToCompute, cancellationToken);
154+
}
155+
156+
builder.Add(GetDeclarationInfo(model, node, getSymbol, cancellationToken, t.Initializer));
157+
return;
158+
}
159+
160+
case SyntaxKind.IndexerDeclaration:
161+
{
162+
var t = (IndexerDeclarationSyntax)node;
163+
if (t.AccessorList != null)
164+
{
165+
foreach (var decl in t.AccessorList.Accessors)
166+
{
167+
ComputeDeclarations(model, decl, shouldSkip, getSymbol, builder, newLevel, cancellationToken);
168+
}
169+
}
170+
171+
if (t.ExpressionBody != null)
172+
{
173+
ComputeDeclarations(model, t.ExpressionBody, shouldSkip, getSymbol, builder, levelsToCompute, cancellationToken);
174+
}
175+
176+
var codeBlocks = t.ParameterList != null ? t.ParameterList.Parameters.Select(p => p.Default) : Enumerable.Empty<SyntaxNode>();
177+
178+
builder.Add(GetDeclarationInfo(model, node, getSymbol, codeBlocks, cancellationToken));
179+
return;
180+
}
181+
182+
case SyntaxKind.AddAccessorDeclaration:
183+
case SyntaxKind.RemoveAccessorDeclaration:
184+
case SyntaxKind.SetAccessorDeclaration:
185+
case SyntaxKind.GetAccessorDeclaration:
186+
{
187+
var t = (AccessorDeclarationSyntax)node;
188+
builder.Add(GetDeclarationInfo(model, node, getSymbol, t.Body, cancellationToken));
189+
return;
190+
}
191+
192+
case SyntaxKind.ConstructorDeclaration:
193+
case SyntaxKind.ConversionOperatorDeclaration:
194+
case SyntaxKind.DestructorDeclaration:
195+
case SyntaxKind.MethodDeclaration:
196+
case SyntaxKind.OperatorDeclaration:
197+
{
198+
var t = (BaseMethodDeclarationSyntax)node;
199+
var codeBlocks = t.ParameterList != null ? t.ParameterList.Parameters.Select(p => p.Default) : Enumerable.Empty<SyntaxNode>();
200+
codeBlocks = codeBlocks.Concat(new[] { t.Body });
201+
202+
var ctorDecl = t as ConstructorDeclarationSyntax;
203+
if (ctorDecl != null && ctorDecl.Initializer != null)
204+
{
205+
codeBlocks = codeBlocks.Concat(new[] { ctorDecl.Initializer });
206+
}
207+
208+
var expressionBody = GetExpressionBodySyntax(t);
209+
if (expressionBody != null)
210+
{
211+
codeBlocks = codeBlocks.Concat(new[] { expressionBody });
212+
}
213+
214+
builder.Add(GetDeclarationInfo(model, node, getSymbol, codeBlocks, cancellationToken));
215+
return;
216+
}
217+
218+
case SyntaxKind.CompilationUnit:
219+
{
220+
var t = (CompilationUnitSyntax)node;
221+
foreach (var decl in t.Members) ComputeDeclarations(model, decl, shouldSkip, getSymbol, builder, newLevel, cancellationToken);
222+
return;
223+
}
224+
225+
default:
226+
return;
227+
}
228+
}
229+
230+
private static DeclarationInfo GetExpressionBodyDeclarationInfo(
231+
BasePropertyDeclarationSyntax declarationWithExpressionBody,
232+
ArrowExpressionClauseSyntax expressionBody,
233+
SemanticModel model,
234+
bool getSymbol,
235+
CancellationToken cancellationToken)
236+
{
237+
// TODO: use 'model.GetDeclaredSymbol(expressionBody)' when compiler is fixed to return the getter symbol for it.
238+
var declaredAccessor = getSymbol ? (model.GetDeclaredSymbol(declarationWithExpressionBody, cancellationToken) as IPropertySymbol)?.GetMethod : null;
239+
240+
return new DeclarationInfo(
241+
declaredNode: expressionBody,
242+
executableCodeBlocks: ImmutableArray.Create<SyntaxNode>(expressionBody),
243+
declaredSymbol: declaredAccessor);
244+
}
245+
246+
/// <summary>
247+
/// Gets the expression-body syntax from an expression-bodied member. The
248+
/// given syntax must be for a member which could contain an expression-body.
249+
/// </summary>
250+
internal static ArrowExpressionClauseSyntax GetExpressionBodySyntax(CSharpSyntaxNode node)
251+
{
252+
ArrowExpressionClauseSyntax arrowExpr = null;
253+
switch (node.Kind())
254+
{
255+
// The ArrowExpressionClause is the declaring syntax for the
256+
// 'get' SourcePropertyAccessorSymbol of properties and indexers.
257+
case SyntaxKind.ArrowExpressionClause:
258+
arrowExpr = (ArrowExpressionClauseSyntax)node;
259+
break;
260+
case SyntaxKind.MethodDeclaration:
261+
arrowExpr = ((MethodDeclarationSyntax)node).ExpressionBody;
262+
break;
263+
case SyntaxKind.OperatorDeclaration:
264+
arrowExpr = ((OperatorDeclarationSyntax)node).ExpressionBody;
265+
break;
266+
case SyntaxKind.ConversionOperatorDeclaration:
267+
arrowExpr = ((ConversionOperatorDeclarationSyntax)node).ExpressionBody;
268+
break;
269+
case SyntaxKind.PropertyDeclaration:
270+
arrowExpr = ((PropertyDeclarationSyntax)node).ExpressionBody;
271+
break;
272+
case SyntaxKind.IndexerDeclaration:
273+
arrowExpr = ((IndexerDeclarationSyntax)node).ExpressionBody;
274+
break;
275+
case SyntaxKind.ConstructorDeclaration:
276+
case SyntaxKind.DestructorDeclaration:
277+
return null;
278+
default:
279+
// Don't throw, just use for the assert in case this is used in the semantic model
280+
////ExceptionUtilities.UnexpectedValue(node.Kind());
281+
break;
282+
}
283+
return arrowExpr;
284+
}
285+
}
286+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#pragma warning disable // this came from Roslyn code
2+
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
namespace ImmutableObjectGraph.Generation.Roslyn
5+
{
6+
using System.Collections.Generic;
7+
using System.Collections.Immutable;
8+
using System.Diagnostics;
9+
using System.Linq;
10+
using System.Threading;
11+
using Microsoft.CodeAnalysis;
12+
13+
internal class DeclarationComputer
14+
{
15+
internal static DeclarationInfo GetDeclarationInfo(SemanticModel model, SyntaxNode node, bool getSymbol, IEnumerable<SyntaxNode> executableCodeBlocks, CancellationToken cancellationToken)
16+
{
17+
var declaredSymbol = getSymbol ? model.GetDeclaredSymbol(node, cancellationToken) : null;
18+
var codeBlocks = executableCodeBlocks?.Where(c => c != null).ToImmutableArray() ?? ImmutableArray<SyntaxNode>.Empty;
19+
return new DeclarationInfo(node, codeBlocks, declaredSymbol);
20+
}
21+
22+
internal static DeclarationInfo GetDeclarationInfo(SemanticModel model, SyntaxNode node, bool getSymbol, CancellationToken cancellationToken)
23+
{
24+
return GetDeclarationInfo(model, node, getSymbol, (IEnumerable<SyntaxNode>)null, cancellationToken);
25+
}
26+
27+
internal static DeclarationInfo GetDeclarationInfo(SemanticModel model, SyntaxNode node, bool getSymbol, SyntaxNode executableCodeBlock, CancellationToken cancellationToken)
28+
{
29+
var declaredSymbol = getSymbol ? model.GetDeclaredSymbol(node, cancellationToken) : null;
30+
var codeBlock = executableCodeBlock == null ? ImmutableArray<SyntaxNode>.Empty : ImmutableArray.Create(executableCodeBlock);
31+
return new DeclarationInfo(node, codeBlock, declaredSymbol);
32+
}
33+
34+
internal static DeclarationInfo GetDeclarationInfo(SemanticModel model, SyntaxNode node, bool getSymbol, CancellationToken cancellationToken, params SyntaxNode[] executableCodeBlocks)
35+
{
36+
return GetDeclarationInfo(model, node, getSymbol, executableCodeBlocks.AsEnumerable(), cancellationToken);
37+
}
38+
}
39+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#pragma warning disable // this came from Roslyn code
2+
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Collections.Immutable;
5+
using System.Diagnostics;
6+
using System.Linq;
7+
using Microsoft.CodeAnalysis;
8+
using Validation;
9+
10+
namespace ImmutableObjectGraph.Generation.Roslyn
11+
{
12+
/// <summary>
13+
/// Struct containing information about a source declaration.
14+
/// </summary>
15+
internal struct DeclarationInfo
16+
{
17+
private readonly SyntaxNode _declaredNode;
18+
private readonly ImmutableArray<SyntaxNode> _executableCodeBlocks;
19+
private readonly ISymbol _declaredSymbol;
20+
21+
internal DeclarationInfo(SyntaxNode declaredNode, ImmutableArray<SyntaxNode> executableCodeBlocks, ISymbol declaredSymbol)
22+
{
23+
Requires.NotNull(declaredNode, nameof(declaredNode));
24+
25+
// TODO: Below assert has been commented out as is not true for VB field decls where multiple variables can share same initializer.
26+
// Declared node is the identifier, which doesn't contain the initializer. Can we tweak the assert somehow to handle this case?
27+
// Debug.Assert(executableCodeBlocks.All(n => n.Ancestors().Contains(declaredNode)));
28+
29+
_declaredNode = declaredNode;
30+
_executableCodeBlocks = executableCodeBlocks;
31+
_declaredSymbol = declaredSymbol;
32+
}
33+
34+
/// <summary>
35+
/// Topmost syntax node for this declaration.
36+
/// </summary>
37+
public SyntaxNode DeclaredNode { get { return _declaredNode; } }
38+
39+
/// <summary>
40+
/// Syntax nodes for executable code blocks (method body, initializers, etc.) associated with this declaration.
41+
/// </summary>
42+
public ImmutableArray<SyntaxNode> ExecutableCodeBlocks { get { return _executableCodeBlocks; } }
43+
44+
/// <summary>
45+
/// Symbol declared by this declaration.
46+
/// </summary>
47+
public ISymbol DeclaredSymbol { get { return _declaredSymbol; } }
48+
}
49+
}

src/version.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
3-
"version": "1.2-beta",
3+
"version": "1.3-beta",
44
"publicReleaseRefSpec": [
55
"^refs/heads/master$", // we release out of master
66
"^refs/heads/v\\d\\.\\d" // we also release from release branches

0 commit comments

Comments
 (0)