Skip to content

Commit 09bf4ab

Browse files
committed
Generate initial syntax wrapper members
1 parent edc4f72 commit 09bf4ab

82 files changed

Lines changed: 1492 additions & 389 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
namespace StyleCop.Analyzers.CodeGeneration
5+
{
6+
using System;
7+
using System.Collections.Generic;
8+
using System.Collections.Immutable;
9+
using System.IO;
10+
using System.Linq;
11+
using System.Text;
12+
using System.Xml.Linq;
13+
using System.Xml.XPath;
14+
using Microsoft.CodeAnalysis;
15+
using Microsoft.CodeAnalysis.CSharp;
16+
using Microsoft.CodeAnalysis.CSharp.Syntax;
17+
using Microsoft.CodeAnalysis.Text;
18+
19+
[Generator]
20+
internal sealed class SyntaxLightupGenerator : ISourceGenerator
21+
{
22+
private enum NodeKind
23+
{
24+
Predefined,
25+
Abstract,
26+
Concrete,
27+
}
28+
29+
public void Initialize(GeneratorInitializationContext context)
30+
{
31+
}
32+
33+
public void Execute(GeneratorExecutionContext context)
34+
{
35+
var syntaxFile = context.AdditionalFiles.Single(x => Path.GetFileName(x.Path) == "Syntax.xml");
36+
var syntaxText = syntaxFile.GetText(context.CancellationToken);
37+
if (syntaxText is null)
38+
{
39+
throw new InvalidOperationException("Failed to read Syntax.xml");
40+
}
41+
42+
var syntaxData = new SyntaxData(in context, XDocument.Parse(syntaxText.ToString()));
43+
this.GenerateSyntaxWrappers(in context, syntaxData);
44+
}
45+
46+
private void GenerateSyntaxWrappers(in GeneratorExecutionContext context, SyntaxData syntaxData)
47+
{
48+
foreach (var node in syntaxData.Nodes)
49+
{
50+
if (node.WrapperName is not null)
51+
{
52+
this.GenerateSyntaxWrapper(in context, syntaxData, node);
53+
}
54+
}
55+
}
56+
57+
private void GenerateSyntaxWrapper(in GeneratorExecutionContext context, SyntaxData syntaxData, NodeData nodeData)
58+
{
59+
var concreteBase = syntaxData.TryGetConcreteBase(nodeData)?.Name ?? nameof(SyntaxNode);
60+
61+
var members = SyntaxFactory.List<MemberDeclarationSyntax>();
62+
63+
// internal const string WrappedTypeName = "Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax";
64+
members = members.Add(SyntaxFactory.FieldDeclaration(
65+
attributeLists: default,
66+
modifiers: SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.InternalKeyword), SyntaxFactory.Token(SyntaxKind.ConstKeyword)),
67+
declaration: SyntaxFactory.VariableDeclaration(
68+
type: SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.StringKeyword)),
69+
variables: SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator(
70+
identifier: SyntaxFactory.Identifier("WrappedTypeName"),
71+
argumentList: null,
72+
initializer: SyntaxFactory.EqualsValueClause(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal("Microsoft.CodeAnalysis.CSharp.Syntax." + nodeData.Name))))))));
73+
74+
// private static readonly Type WrappedType;
75+
members = members.Add(SyntaxFactory.FieldDeclaration(
76+
attributeLists: default,
77+
modifiers: SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)),
78+
declaration: SyntaxFactory.VariableDeclaration(
79+
type: SyntaxFactory.IdentifierName("Type"),
80+
variables: SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator("WrappedType")))));
81+
82+
// private readonly SyntaxNode node;
83+
members = members.Add(SyntaxFactory.FieldDeclaration(
84+
attributeLists: default,
85+
modifiers: SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)),
86+
declaration: SyntaxFactory.VariableDeclaration(
87+
type: SyntaxFactory.IdentifierName(concreteBase),
88+
variables: SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator("node")))));
89+
90+
// private SyntaxNodeWrapper(SyntaxNode node)
91+
// {
92+
// this.node = node;
93+
// }
94+
members = members.Add(SyntaxFactory.ConstructorDeclaration(
95+
attributeLists: default,
96+
modifiers: SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword)),
97+
identifier: SyntaxFactory.Identifier(nodeData.WrapperName),
98+
parameterList: SyntaxFactory.ParameterList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Parameter(
99+
attributeLists: default,
100+
modifiers: default,
101+
type: SyntaxFactory.IdentifierName(concreteBase),
102+
identifier: SyntaxFactory.Identifier("node"),
103+
@default: null))),
104+
initializer: null,
105+
body: SyntaxFactory.Block(
106+
SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression(
107+
SyntaxKind.SimpleAssignmentExpression,
108+
left: SyntaxFactory.MemberAccessExpression(
109+
SyntaxKind.SimpleMemberAccessExpression,
110+
expression: SyntaxFactory.ThisExpression(),
111+
name: SyntaxFactory.IdentifierName("node")),
112+
right: SyntaxFactory.IdentifierName("node"))))));
113+
114+
// public SyntaxNode SyntaxNode => this.node;
115+
members = members.Add(SyntaxFactory.PropertyDeclaration(
116+
attributeLists: default,
117+
modifiers: SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword)),
118+
type: SyntaxFactory.IdentifierName(concreteBase),
119+
explicitInterfaceSpecifier: null,
120+
identifier: SyntaxFactory.Identifier("SyntaxNode"),
121+
accessorList: null,
122+
expressionBody: SyntaxFactory.ArrowExpressionClause(SyntaxFactory.MemberAccessExpression(
123+
SyntaxKind.SimpleMemberAccessExpression,
124+
expression: SyntaxFactory.ThisExpression(),
125+
name: SyntaxFactory.IdentifierName("node"))),
126+
initializer: null,
127+
semicolonToken: SyntaxFactory.Token(SyntaxKind.SemicolonToken)));
128+
129+
var wrapperStruct = SyntaxFactory.StructDeclaration(
130+
attributeLists: default,
131+
modifiers: SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.InternalKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword), SyntaxFactory.Token(SyntaxKind.PartialKeyword)),
132+
identifier: SyntaxFactory.Identifier(nodeData.WrapperName),
133+
typeParameterList: null,
134+
baseList: SyntaxFactory.BaseList(SyntaxFactory.SingletonSeparatedList<BaseTypeSyntax>(
135+
SyntaxFactory.SimpleBaseType(SyntaxFactory.GenericName(
136+
identifier: SyntaxFactory.Identifier("ISyntaxWrapper"),
137+
typeArgumentList: SyntaxFactory.TypeArgumentList(SyntaxFactory.SingletonSeparatedList<TypeSyntax>(SyntaxFactory.IdentifierName(concreteBase))))))),
138+
constraintClauses: default,
139+
members: members);
140+
var wrapperNamespace = SyntaxFactory.NamespaceDeclaration(
141+
name: SyntaxFactory.ParseName("StyleCop.Analyzers.Lightup"),
142+
externs: default,
143+
usings: SyntaxFactory.List<UsingDirectiveSyntax>()
144+
.Add(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("System")))
145+
.Add(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("System.Collections.Immutable")))
146+
.Add(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("Microsoft.CodeAnalysis")))
147+
.Add(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("Microsoft.CodeAnalysis.CSharp")))
148+
.Add(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("Microsoft.CodeAnalysis.CSharp.Syntax"))),
149+
members: SyntaxFactory.SingletonList<MemberDeclarationSyntax>(wrapperStruct));
150+
151+
wrapperNamespace = wrapperNamespace
152+
.NormalizeWhitespace()
153+
.WithLeadingTrivia(
154+
SyntaxFactory.Comment("// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved."),
155+
SyntaxFactory.CarriageReturnLineFeed,
156+
SyntaxFactory.Comment("// Licensed under the MIT License. See LICENSE in the project root for license information."),
157+
SyntaxFactory.CarriageReturnLineFeed,
158+
SyntaxFactory.CarriageReturnLineFeed)
159+
.WithTrailingTrivia(
160+
SyntaxFactory.CarriageReturnLineFeed);
161+
162+
context.AddSource(nodeData.WrapperName + ".g.cs", SourceText.From(wrapperNamespace.ToFullString(), Encoding.UTF8));
163+
}
164+
165+
private sealed class SyntaxData
166+
{
167+
private readonly Dictionary<string, NodeData> nameToNode;
168+
169+
public SyntaxData(in GeneratorExecutionContext context, XDocument document)
170+
{
171+
var nodesBuilder = ImmutableArray.CreateBuilder<NodeData>();
172+
foreach (var element in document.XPathSelectElement("/Tree[@Root='SyntaxNode']").XPathSelectElements("PredefinedNode|AbstractNode|Node"))
173+
{
174+
nodesBuilder.Add(new NodeData(in context, element));
175+
}
176+
177+
this.Nodes = nodesBuilder.ToImmutable();
178+
this.nameToNode = this.Nodes.ToDictionary(node => node.Name);
179+
}
180+
181+
public ImmutableArray<NodeData> Nodes { get; }
182+
183+
public NodeData TryGetConcreteBase(NodeData node)
184+
{
185+
for (var current = this.TryGetNode(node.BaseName); current is not null; current = this.TryGetNode(current.BaseName))
186+
{
187+
if (current.WrapperName is null)
188+
{
189+
// This is not a wrapper
190+
return current;
191+
}
192+
}
193+
194+
return null;
195+
}
196+
197+
private NodeData TryGetNode(string name)
198+
{
199+
this.nameToNode.TryGetValue(name, out var node);
200+
return node;
201+
}
202+
}
203+
204+
private sealed class NodeData
205+
{
206+
public NodeData(in GeneratorExecutionContext context, XElement element)
207+
{
208+
this.Kind = element.Name.LocalName switch
209+
{
210+
"PredefinedNode" => NodeKind.Predefined,
211+
"AbstractNode" => NodeKind.Abstract,
212+
"Node" => NodeKind.Concrete,
213+
_ => throw new NotSupportedException($"Unknown element name '{element.Name}'"),
214+
};
215+
216+
this.Name = element.Attribute("Name").Value;
217+
218+
var existingType = context.Compilation.GetTypeByMetadataName($"Microsoft.CodeAnalysis.CSharp.Syntax.{this.Name}")
219+
?? context.Compilation.GetTypeByMetadataName($"Microsoft.CodeAnalysis.CSharp.{this.Name}")
220+
?? context.Compilation.GetTypeByMetadataName($"Microsoft.CodeAnalysis.{this.Name}");
221+
if (existingType?.DeclaredAccessibility == Accessibility.Public)
222+
{
223+
this.WrapperName = null;
224+
}
225+
else
226+
{
227+
this.WrapperName = this.Name + "Wrapper";
228+
}
229+
230+
this.BaseName = element.Attribute("Base").Value;
231+
this.Fields = element.XPathSelectElements("Field").Select(field => new FieldData(field)).ToImmutableArray();
232+
}
233+
234+
public NodeKind Kind { get; }
235+
236+
public string Name { get; }
237+
238+
public string WrapperName { get; }
239+
240+
public string BaseName { get; }
241+
242+
public ImmutableArray<FieldData> Fields { get; }
243+
}
244+
245+
private sealed class FieldData
246+
{
247+
public FieldData(XElement element)
248+
{
249+
this.Name = element.Attribute("Name").Value;
250+
this.Type = element.Attribute("Type").Value;
251+
this.IsOverride = element.Attribute("Override")?.Value == "true";
252+
}
253+
254+
public string Name { get; }
255+
256+
public string Type { get; }
257+
258+
public bool IsOverride { get; }
259+
}
260+
}
261+
}

StyleCop.Analyzers/StyleCop.Analyzers.ruleset

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
<Rule Id="SA1305" Action="Warning" />
8181
<Rule Id="SA1412" Action="Warning" />
8282
<Rule Id="SA1600" Action="None" />
83+
<Rule Id="SA1601" Action="None" />
8384
<Rule Id="SA1609" Action="Warning" />
8485
</Rules>
8586
<Rules AnalyzerId="DocumentationAnalyzers" RuleNamespace="DocumentationAnalyzers">
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
namespace StyleCop.Analyzers.Lightup
5+
{
6+
using System;
7+
using System.Collections.Immutable;
8+
using Microsoft.CodeAnalysis;
9+
using Microsoft.CodeAnalysis.CSharp;
10+
using Microsoft.CodeAnalysis.CSharp.Syntax;
11+
12+
internal readonly partial struct BaseObjectCreationExpressionSyntaxWrapper : ISyntaxWrapper<ExpressionSyntax>
13+
{
14+
internal const string WrappedTypeName = "Microsoft.CodeAnalysis.CSharp.Syntax.BaseObjectCreationExpressionSyntax";
15+
private static readonly Type WrappedType;
16+
private readonly ExpressionSyntax node;
17+
private BaseObjectCreationExpressionSyntaxWrapper(ExpressionSyntax node)
18+
{
19+
this.node = node;
20+
}
21+
22+
public ExpressionSyntax SyntaxNode => this.node;
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
namespace StyleCop.Analyzers.Lightup
5+
{
6+
using System;
7+
using System.Collections.Immutable;
8+
using Microsoft.CodeAnalysis;
9+
using Microsoft.CodeAnalysis.CSharp;
10+
using Microsoft.CodeAnalysis.CSharp.Syntax;
11+
12+
internal readonly partial struct BaseParameterSyntaxWrapper : ISyntaxWrapper<CSharpSyntaxNode>
13+
{
14+
internal const string WrappedTypeName = "Microsoft.CodeAnalysis.CSharp.Syntax.BaseParameterSyntax";
15+
private static readonly Type WrappedType;
16+
private readonly CSharpSyntaxNode node;
17+
private BaseParameterSyntaxWrapper(CSharpSyntaxNode node)
18+
{
19+
this.node = node;
20+
}
21+
22+
public CSharpSyntaxNode SyntaxNode => this.node;
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
namespace StyleCop.Analyzers.Lightup
5+
{
6+
using System;
7+
using System.Collections.Immutable;
8+
using Microsoft.CodeAnalysis;
9+
using Microsoft.CodeAnalysis.CSharp;
10+
using Microsoft.CodeAnalysis.CSharp.Syntax;
11+
12+
internal readonly partial struct BinaryPatternSyntaxWrapper : ISyntaxWrapper<CSharpSyntaxNode>
13+
{
14+
internal const string WrappedTypeName = "Microsoft.CodeAnalysis.CSharp.Syntax.BinaryPatternSyntax";
15+
private static readonly Type WrappedType;
16+
private readonly CSharpSyntaxNode node;
17+
private BinaryPatternSyntaxWrapper(CSharpSyntaxNode node)
18+
{
19+
this.node = node;
20+
}
21+
22+
public CSharpSyntaxNode SyntaxNode => this.node;
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
namespace StyleCop.Analyzers.Lightup
5+
{
6+
using System;
7+
using System.Collections.Immutable;
8+
using Microsoft.CodeAnalysis;
9+
using Microsoft.CodeAnalysis.CSharp;
10+
using Microsoft.CodeAnalysis.CSharp.Syntax;
11+
12+
internal readonly partial struct CasePatternSwitchLabelSyntaxWrapper : ISyntaxWrapper<SwitchLabelSyntax>
13+
{
14+
internal const string WrappedTypeName = "Microsoft.CodeAnalysis.CSharp.Syntax.CasePatternSwitchLabelSyntax";
15+
private static readonly Type WrappedType;
16+
private readonly SwitchLabelSyntax node;
17+
private CasePatternSwitchLabelSyntaxWrapper(SwitchLabelSyntax node)
18+
{
19+
this.node = node;
20+
}
21+
22+
public SwitchLabelSyntax SyntaxNode => this.node;
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
namespace StyleCop.Analyzers.Lightup
5+
{
6+
using System;
7+
using System.Collections.Immutable;
8+
using Microsoft.CodeAnalysis;
9+
using Microsoft.CodeAnalysis.CSharp;
10+
using Microsoft.CodeAnalysis.CSharp.Syntax;
11+
12+
internal readonly partial struct CommonForEachStatementSyntaxWrapper : ISyntaxWrapper<StatementSyntax>
13+
{
14+
internal const string WrappedTypeName = "Microsoft.CodeAnalysis.CSharp.Syntax.CommonForEachStatementSyntax";
15+
private static readonly Type WrappedType;
16+
private readonly StatementSyntax node;
17+
private CommonForEachStatementSyntaxWrapper(StatementSyntax node)
18+
{
19+
this.node = node;
20+
}
21+
22+
public StatementSyntax SyntaxNode => this.node;
23+
}
24+
}

0 commit comments

Comments
 (0)