Skip to content

Commit

Permalink
support for method invocation ref & out modifiers (ex. TryParse("one"…
Browse files Browse the repository at this point in the history
…, out int one))

support for tuple deconstruction (including nested kinds) var (x,y) = (1,2) & (var x, var y) = (1,2) & (var x, (var a, var b)) = (1,(2,3))
implicit array initializers new []{1,2,3}
entirely new lst model for dealing with using statements (similar to java's try-with-resources)
new Rewrite.Analyzers project that takes uses native .net code generation for all enums such asCsSpace, CsRightPadded, CsContainer, CsLeftPadded . code is contributed at compile time rather then design time, so no autogenerated files need to be checked in nor generator rerun explicitly - it's called automatically as part of build. It also generates a ToString() overload for all LST types to produce output from printer
major improvement to c# project parser, nearly tripling its parsing speed by adding some parallel processing
when using local printer, tests can now run in parallel vastly speeding up their execution (entire test suite now executes in under 5 seconds)
  • Loading branch information
macsux committed Nov 1, 2024
1 parent c725458 commit a3ca4d5
Show file tree
Hide file tree
Showing 105 changed files with 3,687 additions and 826 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.

**/.NCrunch*/**
**/fixtures/**

**/GeneratedCertificates/**
Expand Down
7 changes: 7 additions & 0 deletions Rewrite/Rewrite.Sources.sln
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rewrite.Test.Engine.Remote"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rewrite.Server", "..\..\..\moderneinc\rewrite-remote\Rewrite.Remote\src\Rewrite.Server\Rewrite.Server.csproj", "{E0721BA1-2772-4486-BDF3-FC9301723BB9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rewrite.Analyzers", "src\Rewrite.Analyzers\Rewrite.Analyzers.csproj", "{9818146E-C516-4D3C-80CE-A84741E7772B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -115,6 +117,10 @@ Global
{E0721BA1-2772-4486-BDF3-FC9301723BB9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E0721BA1-2772-4486-BDF3-FC9301723BB9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E0721BA1-2772-4486-BDF3-FC9301723BB9}.Release|Any CPU.Build.0 = Release|Any CPU
{9818146E-C516-4D3C-80CE-A84741E7772B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9818146E-C516-4D3C-80CE-A84741E7772B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9818146E-C516-4D3C-80CE-A84741E7772B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9818146E-C516-4D3C-80CE-A84741E7772B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{25EEACCB-9BAE-4361-A980-9E9CDA37602E} = {7B268266-B9CD-4D85-ABC5-3B2C4611FD74}
Expand All @@ -133,5 +139,6 @@ Global
{5C70ACE0-BE37-4D67-A67F-EE69767994D2} = {C7A4A8F9-0343-4A3E-AB77-101C458C23B5}
{C04C7CA4-69C3-444F-A654-6C722A0FC3BE} = {C7A4A8F9-0343-4A3E-AB77-101C458C23B5}
{E0721BA1-2772-4486-BDF3-FC9301723BB9} = {45B6292D-DC7E-46A7-A86E-7455BBEAED70}
{9818146E-C516-4D3C-80CE-A84741E7772B} = {C7A4A8F9-0343-4A3E-AB77-101C458C23B5}
EndGlobalSection
EndGlobal
8 changes: 8 additions & 0 deletions Rewrite/Rewrite.Sources.v3.ncrunchsolution
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<SolutionConfiguration>
<Settings>
<AllowParallelTestExecution>False</AllowParallelTestExecution>
<EnableRDI>True</EnableRDI>
<RdiConfigured>True</RdiConfigured>
<SolutionConfigured>True</SolutionConfigured>
</Settings>
</SolutionConfiguration>
7 changes: 7 additions & 0 deletions Rewrite/Rewrite.sln
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_solution", "_solution", "{
Directory.Build.props.user = Directory.Build.props.user
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rewrite.Analyzers", "src\Rewrite.Analyzers\Rewrite.Analyzers.csproj", "{8623AABC-D198-4D56-B26B-14A503B226E1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -91,6 +93,10 @@ Global
{6EC419CB-E60B-4F06-A710-B02EA6B9AAAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6EC419CB-E60B-4F06-A710-B02EA6B9AAAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6EC419CB-E60B-4F06-A710-B02EA6B9AAAC}.Release|Any CPU.Build.0 = Release|Any CPU
{8623AABC-D198-4D56-B26B-14A503B226E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8623AABC-D198-4D56-B26B-14A503B226E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8623AABC-D198-4D56-B26B-14A503B226E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8623AABC-D198-4D56-B26B-14A503B226E1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{25EEACCB-9BAE-4361-A980-9E9CDA37602E} = {7B268266-B9CD-4D85-ABC5-3B2C4611FD74}
Expand All @@ -105,5 +111,6 @@ Global
{D3F48947-C702-4D1A-B6EE-96B89D13953D} = {C7A4A8F9-0343-4A3E-AB77-101C458C23B5}
{D38BB305-3276-4AE1-87B1-C19C7A9F3840} = {C7A4A8F9-0343-4A3E-AB77-101C458C23B5}
{F7F4F27F-2CAE-422B-B6CE-25A88C3603A1} = {C7A4A8F9-0343-4A3E-AB77-101C458C23B5}
{8623AABC-D198-4D56-B26B-14A503B226E1} = {C7A4A8F9-0343-4A3E-AB77-101C458C23B5}
EndGlobalSection
EndGlobal
21 changes: 21 additions & 0 deletions Rewrite/src/Rewrite.Analyzers/DebugAnalyzer.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rewrite.Analyzers", "Rewrite.Analyzers.csproj", "{A9BDECE3-D39F-48AE-A94E-B546887F5EC2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rewrite.CSharp", "..\Rewrite.CSharp\Rewrite.CSharp.csproj", "{E18B9AF4-A15B-4A68-AD48-6F4795DAFA38}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A9BDECE3-D39F-48AE-A94E-B546887F5EC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A9BDECE3-D39F-48AE-A94E-B546887F5EC2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A9BDECE3-D39F-48AE-A94E-B546887F5EC2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A9BDECE3-D39F-48AE-A94E-B546887F5EC2}.Release|Any CPU.Build.0 = Release|Any CPU
{E18B9AF4-A15B-4A68-AD48-6F4795DAFA38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E18B9AF4-A15B-4A68-AD48-6F4795DAFA38}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E18B9AF4-A15B-4A68-AD48-6F4795DAFA38}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
150 changes: 150 additions & 0 deletions Rewrite/src/Rewrite.Analyzers/EnumGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;

namespace Rewrite.Analyzers;

[Generator]
public class EnumGenerator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForSyntaxNotifications(() => new SyntaxScanner());
}

public void Execute(GeneratorExecutionContext context)
{
var scanner = (SyntaxScanner)context.SyntaxReceiver!;

scanner.CsRightPaddedEnumValues.UnionWith(scanner.CsContainerEnumValues);
scanner.CsSpaceEnumValues.UnionWith(scanner.CsContainerEnumValues);
scanner.CsSpaceEnumValues.UnionWith(scanner.CsRightPaddedEnumValues);
scanner.CsSpaceEnumValues.UnionWith(scanner.CsLeftPaddedEnumValues);
// scanner.CsLeftPaddedEnumValues.UnionWith(scanner.CsContainerEnumValues);
var csContainerSrc = $$"""
namespace Rewrite.RewriteCSharp.Tree;
public interface CsContainer
{
public record Location(CsSpace.Location BeforeLocation, CsRightPadded.Location ElementLocation)
{
{{ scanner.CsContainerEnumValues.Render(enumValue => $$"""
public static readonly Location {{enumValue}} = new(CsSpace.Location.{{enumValue}}, CsRightPadded.Location.{{enumValue}});
""", "\n").Ident(2) }}
}
}
""";
var csSpaceSrc = $$"""
namespace Rewrite.RewriteCSharp.Tree;
public interface CsSpace
{
public record Location
{
{{ scanner.CsSpaceEnumValues.Render(enumValue => $$"""
public static readonly Location {{enumValue}} = new();
""", "\n").Ident(2) }}
}
}
""";

var csRightPaddedSrc = $$"""
namespace Rewrite.RewriteCSharp.Tree;
public interface CsRightPadded
{
public record Location(CsSpace.Location AfterLocation)
{
{{ scanner.CsRightPaddedEnumValues.Render(enumValue => $$"""
public static readonly Location {{enumValue}} = new (CsSpace.Location.{{enumValue}});
""", "\n").Ident(2) }}
}
}
""";

var csLeftPaddedSrc = $$"""
namespace Rewrite.RewriteCSharp.Tree;
public interface CsLeftPadded
{
public record Location(CsSpace.Location BeforeLocation)
{
{{ scanner.CsLeftPaddedEnumValues.Render(enumValue => $$"""
public static readonly Location {{enumValue}} = new (CsSpace.Location.{{enumValue}});
""", "\n").Ident(2) }}
}
}
""";
if(scanner.CsContainerEnumValues.Count > 0)
context.AddSource("CsContainer.gs", SourceText.From(csContainerSrc, Encoding.UTF8));
if(scanner.CsRightPaddedEnumValues.Count > 0)
context.AddSource("CsRightPadded.gs", SourceText.From(csRightPaddedSrc, Encoding.UTF8));
if(scanner.CsLeftPaddedEnumValues.Count > 0)
context.AddSource("CsLeftPadded.gs", SourceText.From(csLeftPaddedSrc, Encoding.UTF8));
if(scanner.CsSpaceEnumValues.Count > 0)
context.AddSource("CsSpace.gs", SourceText.From(csSpaceSrc, Encoding.UTF8));
}
public class SyntaxScanner : ISyntaxReceiver
{
public HashSet<string> CsContainerEnumValues { get; } = new();
public HashSet<string> CsRightPaddedEnumValues { get; } = new();
public HashSet<string> CsLeftPaddedEnumValues { get; } = new();
public HashSet<string> CsSpaceEnumValues { get; } = new();
public string? AssemblyFileVersion { get; set; }

public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
if (syntaxNode is MemberAccessExpressionSyntax
{
Expression: MemberAccessExpressionSyntax
{
Expression: IdentifierNameSyntax { Identifier.Text: "CsContainer" },
Name: IdentifierNameSyntax { Identifier.Text: "Location" },
}
} csContainerLocation)
{

CsContainerEnumValues.Add(csContainerLocation.Name.Identifier.Text);
}

if (syntaxNode is MemberAccessExpressionSyntax
{
Expression: MemberAccessExpressionSyntax
{
Expression: IdentifierNameSyntax { Identifier.Text: "CsRightPadded" },
Name: IdentifierNameSyntax { Identifier.Text: "Location" },
}
} csRightPaddedLocation)
{

CsRightPaddedEnumValues.Add(csRightPaddedLocation.Name.Identifier.Text);
}
if (syntaxNode is MemberAccessExpressionSyntax
{
Expression: MemberAccessExpressionSyntax
{
Expression: IdentifierNameSyntax { Identifier.Text: "CsLeftPadded" },
Name: IdentifierNameSyntax { Identifier.Text: "Location" },
}
} csLeftPaddedLocation)
{

CsLeftPaddedEnumValues.Add(csLeftPaddedLocation.Name.Identifier.Text);
}
if (syntaxNode is MemberAccessExpressionSyntax
{
Expression: MemberAccessExpressionSyntax
{
Expression: IdentifierNameSyntax { Identifier.Text: "CsSpace" },
Name: IdentifierNameSyntax { Identifier.Text: "Location" },
}
} csSpaceLocation)
{

CsSpaceEnumValues.Add(csSpaceLocation.Name.Identifier.Text);
}

}
}
}
36 changes: 36 additions & 0 deletions Rewrite/src/Rewrite.Analyzers/Extensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Microsoft.CodeAnalysis;

namespace Rewrite.Analyzers;

public static class Extensions
{
public static bool InheritsFrom(this INamedTypeSymbol symbol, string type)
{
var current = symbol.BaseType;
while (current != null)
{
if (current.Name == type)
return true;
current = current.BaseType;
}
return false;
}
public static string Ident(this object source, int identLevels)
{
var lines = source.ToString().TrimStart(' ').Split('\n');
var ident = new string(' ', identLevels * 4);
return string.Join("\n", lines.Select((x, i) => $"""{ (i > 0 ? ident : "") }{x}"""));
}

public static string Render<T>(this IEnumerable<T> source, Func<T, string> template, string separator = "", string openToken = "", string closeToken = "", bool renderEmpty = true)
{
if (!renderEmpty && source.Count() == 0)
return "";
return $"{openToken}{string.Join(separator, source.Select(template))}{closeToken}";
}

public static string Render<T>(this IEnumerable<T> source, Func<T, int, string> template, string separator = "")
{
return string.Join(separator, source.Select(template));
}
}
67 changes: 67 additions & 0 deletions Rewrite/src/Rewrite.Analyzers/LstToString.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;

namespace Rewrite.Analyzers;

[Generator]
public class LstToGenerator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForSyntaxNotifications(() => new SyntaxScanner());
}

public void Execute(GeneratorExecutionContext context)
{
var scanner = (SyntaxScanner)context.SyntaxReceiver!;
string GetSrc(List<ClassDeclarationSyntax> classes, string parent)
{
var ns = parent == "J" ? "RewriteJava" : "RewriteCSharp";
return $$"""
using Rewrite.Core;
using Rewrite.RewriteJava.Tree;
namespace Rewrite.{{ns}}.Tree
{
public partial interface {{parent}}
{
{{ classes.Render(c => $$"""
partial class {{c.Identifier}}{{c.TypeParameterList?.Parameters.Render(p => p.Identifier.Text, ",", "<", ">", renderEmpty: false)}}
{
public override string? ToString() => Core.Tree.ToString(this) ?? base.ToString();
}
""", "\n").Ident(2) }}
}
}
""";
}
if(scanner.CsClasses.Count > 0)
context.AddSource("CsLstToString.gs", SourceText.From(GetSrc(scanner.CsClasses.Values.ToList(), "Cs"), Encoding.UTF8));
if(scanner.JClasses.Count > 0)
context.AddSource("JLstToString.gs", SourceText.From(GetSrc(scanner.JClasses.Values.ToList(), "J"), Encoding.UTF8));

}
public class SyntaxScanner : ISyntaxReceiver
{
public Dictionary<string, ClassDeclarationSyntax> CsClasses { get; } = new();
public Dictionary<string, ClassDeclarationSyntax> JClasses { get; } = new();
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
if (syntaxNode is ClassDeclarationSyntax
{
Parent: InterfaceDeclarationSyntax
{
Identifier.Text: "Cs" or "J" ,
} parent
} classDeclaration)
{
if (parent.Identifier.Text == "J")
JClasses[classDeclaration.Identifier.Text] = classDeclaration;
else
CsClasses[classDeclaration.Identifier.Text] = classDeclaration;
}
}
}
}
9 changes: 9 additions & 0 deletions Rewrite/src/Rewrite.Analyzers/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"Generators": {
"commandName": "DebugRoslynComponent",
"targetProject": "../Rewrite.CSharp/Rewrite.CSharp.csproj"
}
}
}
29 changes: 29 additions & 0 deletions Rewrite/src/Rewrite.Analyzers/Rewrite.Analyzers.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<IncludeBuildOutput>false</IncludeBuildOutput> <!-- disable convention based nuget creation - we're controlling it fully manually -->
<NoWarn>NU5128</NoWarn>
<GetTargetPathDependsOn>$(GetTargetPathDependsOn);GetDependencyTargetPaths</GetTargetPathDependsOn>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" >
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" PrivateAssets="all"/>
<PackageReference Include="YamlDotNet" Version="16.0.0" GeneratePathProperty="true" PrivateAssets="all" />

</ItemGroup>
<Target Name="GetDependencyTargetPaths">
<ItemGroup>
<!-- <TargetPathWithTargetPlatformMoniker Include="$(PKGYamlDotNet)\lib\netstandard2.0\YamlDotNet.dll" IncludeRuntimeDependency="false" />-->
</ItemGroup>
</Target>
<ItemGroup>
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<!-- <None Include="$(PKGYamlDotNet)\lib\netstandard2.0\YamlDotNet.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />-->
</ItemGroup>

</Project>
Loading

0 comments on commit a3ca4d5

Please sign in to comment.