Skip to content

Commit 3f93134

Browse files
committed
Split LocationTransformer code into multiple files
1 parent 6336a6b commit 3f93134

File tree

5 files changed

+264
-239
lines changed

5 files changed

+264
-239
lines changed
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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 Microsoft.CodeAnalysis;
5+
using Microsoft.CodeAnalysis.CSharp;
6+
using Microsoft.CodeAnalysis.CSharp.Syntax;
7+
8+
namespace Silk.NET.SilkTouch.Mods.LocationTransformation;
9+
10+
/// <summary>
11+
/// Renames the identifiers for all locations transformed.
12+
/// </summary>
13+
/// <param name="newNamesBySymbol">The new names for each symbol as a dictionary.</param>
14+
/// <param name="includeDeclarations">Should declaration references be renamed?</param>
15+
/// <param name="includeCandidateLocations">Should candidate references or implicit references be renamed?</param>
16+
public class IdentifierRenamingTransformer(IEnumerable<(ISymbol Symbol, string NewName)> newNamesBySymbol, bool includeDeclarations = true, bool includeCandidateLocations = false) : LocationTransformer
17+
{
18+
private LocationTransformerContext _context;
19+
private Dictionary<ISymbol, string> _newNameLookup = newNamesBySymbol.Select(t => new KeyValuePair<ISymbol, string>(t.Symbol, t.NewName)).ToDictionary(SymbolEqualityComparer.Default);
20+
21+
/// <inheritdoc />
22+
public override SyntaxNode? GetNodeToModify(SyntaxNode current, LocationTransformerContext context)
23+
{
24+
_context = context;
25+
26+
if (!includeDeclarations && context.IsDeclaration)
27+
{
28+
return null;
29+
}
30+
31+
if (!includeCandidateLocations && context.IsCandidateLocation)
32+
{
33+
return null;
34+
}
35+
36+
return current;
37+
}
38+
39+
private SyntaxToken GetNewName() => SyntaxFactory.Identifier(_newNameLookup[_context.Symbol]);
40+
41+
/// <inheritdoc />
42+
public override SyntaxNode? VisitIdentifierName(IdentifierNameSyntax node)
43+
=> SyntaxFactory.IdentifierName(GetNewName());
44+
45+
// ----- Types -----
46+
47+
/// <inheritdoc />
48+
public override SyntaxNode? VisitClassDeclaration(ClassDeclarationSyntax node)
49+
=> node.WithIdentifier(GetNewName());
50+
51+
/// <inheritdoc />
52+
public override SyntaxNode? VisitStructDeclaration(StructDeclarationSyntax node)
53+
=> node.WithIdentifier(GetNewName());
54+
55+
/// <inheritdoc />
56+
public override SyntaxNode? VisitInterfaceDeclaration(InterfaceDeclarationSyntax node)
57+
=> node.WithIdentifier(GetNewName());
58+
59+
/// <inheritdoc />
60+
public override SyntaxNode? VisitRecordDeclaration(RecordDeclarationSyntax node)
61+
=> node.WithIdentifier(GetNewName());
62+
63+
/// <inheritdoc />
64+
public override SyntaxNode? VisitDelegateDeclaration(DelegateDeclarationSyntax node)
65+
=> node.WithIdentifier(GetNewName());
66+
67+
/// <inheritdoc />
68+
public override SyntaxNode? VisitEnumDeclaration(EnumDeclarationSyntax node)
69+
=> node.WithIdentifier(GetNewName());
70+
71+
// ----- Members -----
72+
73+
/// <inheritdoc />
74+
public override SyntaxNode? VisitEnumMemberDeclaration(EnumMemberDeclarationSyntax node)
75+
=> node.WithIdentifier(GetNewName());
76+
77+
/// <inheritdoc />
78+
public override SyntaxNode? VisitEventDeclaration(EventDeclarationSyntax node)
79+
=> node.WithIdentifier(GetNewName());
80+
81+
/// <inheritdoc />
82+
public override SyntaxNode? VisitMethodDeclaration(MethodDeclarationSyntax node)
83+
=> node.WithIdentifier(GetNewName());
84+
85+
/// <inheritdoc />
86+
public override SyntaxNode? VisitPropertyDeclaration(PropertyDeclarationSyntax node)
87+
=> node.WithIdentifier(GetNewName());
88+
89+
/// <inheritdoc />
90+
public override SyntaxNode? VisitConstructorDeclaration(ConstructorDeclarationSyntax node)
91+
=> node.WithIdentifier(GetNewName());
92+
93+
/// <inheritdoc />
94+
public override SyntaxNode? VisitDestructorDeclaration(DestructorDeclarationSyntax node)
95+
=> node.WithIdentifier(GetNewName());
96+
97+
// ----- Other -----
98+
99+
/// <inheritdoc />
100+
public override SyntaxNode? VisitVariableDeclarator(VariableDeclaratorSyntax node)
101+
=> node.WithIdentifier(GetNewName());
102+
103+
/// <inheritdoc />
104+
public override SyntaxNode? VisitTypeParameter(TypeParameterSyntax node)
105+
=> node.WithIdentifier(GetNewName());
106+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
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.FindSymbols;
8+
using Microsoft.Extensions.Logging;
9+
10+
namespace Silk.NET.SilkTouch.Mods.LocationTransformation;
11+
12+
/// <summary>
13+
/// Utilities for transforming <see cref="Location"/>s.
14+
/// </summary>
15+
public static class LocationTransformationUtils
16+
{
17+
/// <summary>
18+
/// Finds all references to the specified symbols and applies the specified transformations to them.
19+
/// Transformations will be done in order.
20+
/// </summary>
21+
public static async Task ModifyAllReferencesAsync(
22+
IModContext ctx,
23+
ILogger logger,
24+
IEnumerable<ISymbol> symbols,
25+
IEnumerable<LocationTransformer> transformers,
26+
CancellationToken ct = default)
27+
{
28+
// Convert to lists
29+
// The parameters being IEnumerables is for convenience
30+
var symbolList = symbols.ToList();
31+
var transformersList = transformers.ToList();
32+
33+
var project = ctx.SourceProject;
34+
if (project == null)
35+
{
36+
return;
37+
}
38+
39+
var compilation = await project.GetCompilationAsync(ct);
40+
if (compilation == null)
41+
{
42+
return;
43+
}
44+
45+
var locations = new ConcurrentBag<(Location Location, LocationTransformerContext Context)>();
46+
47+
// Add the locations of the declaration identifiers for each symbol
48+
foreach (var symbol in symbolList)
49+
{
50+
foreach (var declaringSyntaxReference in symbol.DeclaringSyntaxReferences)
51+
{
52+
locations.Add((declaringSyntaxReference.GetSyntax().GetLocation(),
53+
new LocationTransformerContext(symbol, true, false)));
54+
}
55+
}
56+
57+
// Find all locations where the symbols are referenced
58+
// TODO this needs parallelisation config & be sensitive to the environment (future src generator form factor?)
59+
var documents = project.Documents.ToImmutableHashSet();
60+
await Parallel.ForEachAsync(
61+
symbolList,
62+
ct,
63+
async (symbol, _) => {
64+
var references = await SymbolFinder.FindReferencesAsync(symbol, project.Solution, documents, ct);
65+
foreach (var reference in references)
66+
{
67+
foreach (var location in reference.Locations)
68+
{
69+
locations.Add((location.Location,
70+
new LocationTransformerContext(symbol, false,
71+
location.IsCandidateLocation || location.IsImplicit)));
72+
}
73+
}
74+
}
75+
);
76+
77+
// Group the locations by source tree. This will be used to prevent accidentally overwriting changes.
78+
var locationsBySourcetree = locations.GroupBy(l => l.Location.SourceTree);
79+
foreach (var group in locationsBySourcetree)
80+
{
81+
var syntaxTree = group.Key;
82+
if (syntaxTree == null)
83+
{
84+
continue;
85+
}
86+
87+
var document = project.GetDocument(syntaxTree);
88+
if (document == null)
89+
{
90+
continue;
91+
}
92+
93+
var syntaxRoot = await syntaxTree.GetRootAsync(ct);
94+
95+
// Modify each location
96+
// We order the locations so that we modify starting from the end of the file
97+
// This way we prevent changes from being accidentally overwriting changes
98+
foreach (var (location, context) in group.OrderByDescending(l => l.Location.SourceSpan.Start))
99+
{
100+
foreach (var transformer in transformersList)
101+
{
102+
var syntaxNode = syntaxRoot.FindNode(location.SourceSpan);
103+
var nodeToModify = transformer.GetNodeToModify(syntaxNode, context);
104+
if (nodeToModify == null)
105+
{
106+
continue;
107+
}
108+
109+
var newNode = transformer.Visit(nodeToModify);
110+
syntaxRoot = syntaxRoot.ReplaceNode(nodeToModify, newNode);
111+
}
112+
}
113+
114+
// Commit the changes to the project
115+
var newDocument = document.WithSyntaxRoot(syntaxRoot);
116+
project = newDocument.Project;
117+
}
118+
119+
ctx.SourceProject = project;
120+
}
121+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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 Microsoft.CodeAnalysis;
5+
using Microsoft.CodeAnalysis.CSharp;
6+
7+
namespace Silk.NET.SilkTouch.Mods.LocationTransformation;
8+
9+
/// <summary>
10+
/// Base class for location transformers used by <see cref="LocationTransformationUtils.ModifyAllReferencesAsync"/>.
11+
/// </summary>
12+
public abstract class LocationTransformer : CSharpSyntaxRewriter
13+
{
14+
/// <summary>
15+
/// Given a node, this method should return the given node, another node, or null.
16+
/// Returning null will lead to no node being modified.
17+
/// Returning the another node will lead to the other node being modified instead of the original node.
18+
/// </summary>
19+
/// <param name="current">The current node.</param>
20+
/// <param name="context">Additional information about the syntax node being processed.</param>
21+
/// <returns>The given node, another node, or null.</returns>
22+
public abstract SyntaxNode? GetNodeToModify(SyntaxNode current, LocationTransformerContext context);
23+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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 Microsoft.CodeAnalysis;
5+
6+
namespace Silk.NET.SilkTouch.Mods.LocationTransformation;
7+
8+
/// <summary>
9+
/// Additional information about the syntax node being processed.
10+
/// </summary>
11+
/// <param name="Symbol">The symbol used to find the node.</param>
12+
/// <param name="IsDeclaration">Does the syntax node represent the declaration of the symbol?</param>
13+
/// <param name="IsCandidateLocation">Does the syntax node represent a candidate location?</param>
14+
public record struct LocationTransformerContext(ISymbol Symbol, bool IsDeclaration, bool IsCandidateLocation);

0 commit comments

Comments
 (0)