Skip to content

Commit 34cadfd

Browse files
committed
Add LocationTransformer class
1 parent 9dd3b92 commit 34cadfd

3 files changed

Lines changed: 212 additions & 86 deletions

File tree

.silktouch/a8a82533527277ff.stout

0 Bytes
Binary file not shown.
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Collections.Concurrent;
5+
using System.Collections.Immutable;
6+
using Microsoft.CodeAnalysis;
7+
using Microsoft.CodeAnalysis.CSharp;
8+
using Microsoft.CodeAnalysis.CSharp.Syntax;
9+
using Microsoft.CodeAnalysis.FindSymbols;
10+
using Microsoft.Extensions.Logging;
11+
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
12+
13+
namespace Silk.NET.SilkTouch.Mods.LocationTransformation;
14+
15+
/// <summary>
16+
/// Utilities for transforming <see cref="Location"/>s.
17+
/// </summary>
18+
public static class LocationTransformationUtils
19+
{
20+
/// <summary>
21+
/// Finds all references to the specified symbols and applies the specified transformations to them.
22+
/// Transformations will be done in order.
23+
/// </summary>
24+
public static async Task ModifyAllReferencesAsync(
25+
IModContext ctx,
26+
ILogger logger,
27+
IEnumerable<ISymbol> symbols,
28+
IEnumerable<LocationTransformer> transformers,
29+
CancellationToken ct = default)
30+
{
31+
var symbolSet = symbols.ToList();
32+
var transformersSet = transformers.ToList();
33+
34+
var project = ctx.SourceProject;
35+
if (project == null)
36+
{
37+
return;
38+
}
39+
40+
var compilation = await project.GetCompilationAsync(ct);
41+
if (compilation == null)
42+
{
43+
return;
44+
}
45+
46+
// Find all locations where the symbols are referenced
47+
var locations = new List<Location>();
48+
var documents = project.Documents.ToImmutableHashSet();
49+
foreach (var symbol in symbolSet)
50+
{
51+
var references = await SymbolFinder.FindReferencesAsync(symbol, project.Solution, documents, ct);
52+
locations.AddRange(references.SelectMany(r => r.Locations).Select(rl => rl.Location));
53+
}
54+
55+
// var locations = new ConcurrentDictionary<Location, string>();
56+
// // TODO this needs parallelisation config & be sensitive to the environment (future src generator form factor?)
57+
// await Parallel.ForEachAsync(
58+
// toRename,
59+
// ct,
60+
// async (tuple, _) =>
61+
// {
62+
// // First, let's add all of the locations of the declaration identifiers.
63+
// var (symbol, newName) = tuple;
64+
// if (includeDeclarations)
65+
// {
66+
// foreach (var syntaxRef in symbol.DeclaringSyntaxReferences)
67+
// {
68+
// var identifierLocation = IdentifierLocation(
69+
// await syntaxRef.GetSyntaxAsync(ct)
70+
// );
71+
// if (identifierLocation is not null)
72+
// {
73+
// locations.TryAdd(identifierLocation, newName);
74+
// }
75+
// }
76+
// }
77+
//
78+
// // Next, let's find all the references of the symbols.
79+
// var references = await SymbolFinder.FindReferencesAsync(
80+
// symbol,
81+
// ctx.SourceProject?.Solution
82+
// ?? throw new ArgumentException("SourceProject is null"),
83+
// ct
84+
// );
85+
//
86+
// foreach (var referencedSymbol in references)
87+
// {
88+
// foreach (var referencedSymbolLocation in referencedSymbol.Locations)
89+
// {
90+
// if (
91+
// !includeCandidateLocations
92+
// && (
93+
// referencedSymbolLocation.IsCandidateLocation
94+
// || referencedSymbolLocation.IsImplicit
95+
// )
96+
// )
97+
// {
98+
// continue;
99+
// }
100+
//
101+
// locations.TryAdd(referencedSymbolLocation.Location, newName);
102+
// }
103+
// }
104+
// }
105+
// );
106+
107+
// Group the locations by source tree. This will be used to prevent accidentally overwriting changes.
108+
var locationsBySourcetree = locations.GroupBy(l => l.SourceTree);
109+
foreach (var group in locationsBySourcetree)
110+
{
111+
var syntaxTree = group.Key;
112+
if (syntaxTree == null)
113+
{
114+
continue;
115+
}
116+
117+
var document = project.GetDocument(syntaxTree);
118+
if (document == null)
119+
{
120+
continue;
121+
}
122+
123+
var syntaxRoot = await syntaxTree.GetRootAsync(ct);
124+
125+
// Modify each location
126+
// We order the locations so that we modify starting from the end of the file
127+
// This way we prevent changes from being accidentally overwriting changes
128+
foreach (var location in group.OrderByDescending(l => l.SourceSpan.Start))
129+
{
130+
foreach (var transformer in transformersSet)
131+
{
132+
var syntaxNode = syntaxRoot.FindNode(location.SourceSpan);
133+
var nodeToModify = transformer.GetNodeToModify(syntaxNode);
134+
if (nodeToModify == null)
135+
{
136+
continue;
137+
}
138+
139+
var newNode = transformer.Visit(nodeToModify);
140+
syntaxRoot = syntaxRoot.ReplaceNode(nodeToModify, newNode);
141+
}
142+
143+
144+
}
145+
146+
// Commit the changes to the project
147+
var newDocument = document.WithSyntaxRoot(syntaxRoot);
148+
project = newDocument.Project;
149+
}
150+
151+
ctx.SourceProject = project;
152+
}
153+
}
154+
155+
/// <summary>
156+
/// Base class for location transformers used by <see cref="LocationTransformationUtils.ModifyAllReferencesAsync"/>.
157+
/// </summary>
158+
public abstract class LocationTransformer : CSharpSyntaxRewriter
159+
{
160+
/// <summary>
161+
/// Given a node, this method should return a parent node, the given node, or null.
162+
/// Returning null will lead to no node being modified.
163+
/// Returning the parent node will lead to the parent node being modified instead of the original node.
164+
/// </summary>
165+
/// <param name="current">TODO</param>
166+
/// <returns>TODO</returns>
167+
public abstract SyntaxNode? GetNodeToModify(SyntaxNode current);
168+
}
169+
170+
// // TODO: Implement this
171+
// /// <summary>
172+
// /// Renames the identifiers for all locations transformed.
173+
// /// </summary>
174+
// public class IdentifierRenamingTransformer(bool includeDeclarations = true, bool includeCandidateLocations = false) : LocationTransformer
175+
// {
176+
// /// <inheritdoc />
177+
// public override SyntaxNode? GetNodeToModify(SyntaxNode current, ISymbol symbol) => current;
178+
//
179+
// /// <inheritdoc />
180+
// public override SyntaxNode? VisitIdentifierName(IdentifierNameSyntax node)
181+
// {
182+
// return IdentifierName("Equals");
183+
// }
184+
// }
185+
186+
/// <summary>
187+
/// Reduces the pointer dimension by one for all locations transformed.
188+
/// If the location is already a non-pointer, then nothing will be done.
189+
/// </summary>
190+
/// <example>
191+
/// <c>Handle**</c> will be replaced with <c>Handle*</c>. <br/>
192+
/// <c>Handle*</c> will be replaced with <c>Handle</c>.
193+
/// </example>
194+
public class PointerDimensionReductionTransformer : LocationTransformer
195+
{
196+
/// <inheritdoc />
197+
public override SyntaxNode? GetNodeToModify(SyntaxNode current)
198+
{
199+
if (current.Parent is PointerTypeSyntax parent)
200+
{
201+
return parent;
202+
}
203+
204+
return null;
205+
}
206+
207+
/// <inheritdoc />
208+
public override SyntaxNode? VisitPointerType(PointerTypeSyntax node) => node.ElementType;
209+
}

sources/SilkTouch/SilkTouch/Mods/TransformHandles.cs

Lines changed: 3 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using Microsoft.Extensions.Logging;
1717
using Microsoft.Extensions.Options;
1818
using Silk.NET.SilkTouch.Clang;
19+
using Silk.NET.SilkTouch.Mods.LocationTransformation;
1920
using Silk.NET.SilkTouch.Naming;
2021
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
2122

@@ -153,8 +154,8 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct =
153154

154155
// Reduce pointer dimensions
155156
ctx.SourceProject = project;
156-
var pointerDimensionReducer = new PointerDimensionReducer(ctx, ct);
157-
await pointerDimensionReducer.ReducePointerDimensionAsync(handleTypes);
157+
await LocationTransformationUtils.ModifyAllReferencesAsync(ctx, logger, handleTypes,
158+
[new PointerDimensionReductionTransformer()], ct);
158159
project = ctx.SourceProject;
159160

160161
// At the time of writing this comment, this line effectively does nothing
@@ -586,88 +587,4 @@ private static IEnumerable<MemberDeclarationSyntax> GetDSLHandleMembers(string s
586587
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
587588
}
588589
}
589-
590-
private class PointerDimensionReducer(IModContext ctx, CancellationToken ct) : CSharpSyntaxRewriter
591-
{
592-
/// <summary>
593-
/// Reduces the pointer dimension of all references to the specified symbols.
594-
/// </summary>
595-
public async Task ReducePointerDimensionAsync(List<INamedTypeSymbol> symbols)
596-
{
597-
var project = ctx.SourceProject;
598-
if (project == null)
599-
{
600-
return;
601-
}
602-
603-
var compilation = await project.GetCompilationAsync(ct);
604-
if (compilation == null)
605-
{
606-
return;
607-
}
608-
609-
// Find all locations where the symbols are referenced
610-
var locations = new List<Location>();
611-
var documents = project.Documents.ToImmutableHashSet();
612-
foreach (var symbol in symbols)
613-
{
614-
var references = await SymbolFinder.FindReferencesAsync(symbol, project.Solution, documents, ct);
615-
locations.AddRange(references.SelectMany(r => r.Locations).Select(rl => rl.Location));
616-
}
617-
618-
// Group the locations by source tree. This will be used to prevent accidentally overwriting changes.
619-
var locationsBySourcetree = locations.GroupBy(l => l.SourceTree);
620-
foreach (var group in locationsBySourcetree)
621-
{
622-
var syntaxTree = group.Key;
623-
if (syntaxTree == null)
624-
{
625-
continue;
626-
}
627-
628-
var document = project.GetDocument(syntaxTree);
629-
if (document == null)
630-
{
631-
continue;
632-
}
633-
634-
var syntaxRoot = await syntaxTree.GetRootAsync(ct);
635-
636-
// Modify each location
637-
// We order the locations so that we modify starting from the end of the file
638-
// This way we prevent changes from being accidentally overwriting changes
639-
foreach (var location in group.OrderByDescending(l => l.SourceSpan.Start))
640-
{
641-
var syntaxNode = syntaxRoot.FindNode(location.SourceSpan);
642-
643-
var nodeToModify = GetNodeToModify(syntaxNode);
644-
if (nodeToModify == null)
645-
{
646-
continue;
647-
}
648-
649-
var newNode = Visit(nodeToModify);
650-
syntaxRoot = syntaxRoot.ReplaceNode(nodeToModify, newNode);
651-
}
652-
653-
// Commit the changes to the project
654-
var newDocument = document.WithSyntaxRoot(syntaxRoot);
655-
project = newDocument.Project;
656-
}
657-
658-
ctx.SourceProject = project;
659-
}
660-
661-
private SyntaxNode? GetNodeToModify(SyntaxNode current)
662-
{
663-
if (current.Parent is PointerTypeSyntax parent)
664-
{
665-
return parent;
666-
}
667-
668-
return null;
669-
}
670-
671-
public override SyntaxNode? VisitPointerType(PointerTypeSyntax node) => node.ElementType;
672-
}
673590
}

0 commit comments

Comments
 (0)