Skip to content

Commit 499b25e

Browse files
authored
Merge pull request #113 from amis92/feature/internalize-declaration-computer
Internalized DeclarationComputer from Roslyn, bump to v1.3
2 parents b5f7958 + faba900 commit 499b25e

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)