├── .github
└── workflows
│ ├── build-debug.yml
│ ├── build-release.yml
│ └── stale.yml
├── .gitignore
├── Directory.Build.props
├── Icon.png
├── LICENSE.md
├── PrivateProxy.sln
├── README.md
├── opensource.snk
├── sandbox
├── ClassLibrary
│ ├── Class1.cs
│ └── ClassLibrary.csproj
└── ConsoleAppNet8
│ ├── BlogSample.cs
│ ├── ConsoleAppNet8.csproj
│ └── Program.cs
├── src
└── PrivateProxy.Generator
│ ├── DiagnosticDescriptors.cs
│ ├── EmitHelper.cs
│ ├── MetaMember.cs
│ ├── PrivateProxy.Generator.csproj
│ ├── PrivateProxyGenerator.cs
│ └── Properties
│ └── launchSettings.json
└── tests
└── PrivateProxy.Tests
├── ChangeKindTest.cs
├── ClassGenerateTest.cs
├── DiagnosticsTest.cs
├── GenerateInternalTest.cs
├── GlobalUsings.cs
├── OtherTypesTest.cs
├── PrivateProxy.Tests.csproj
├── RefStructGenerateTest.cs
├── StructGenerateTest.cs
└── Utils
└── CsharpGeneratorRunner.cs
/.github/workflows/build-debug.yml:
--------------------------------------------------------------------------------
1 | name: Build-Debug
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | branches:
7 | - "main"
8 | pull_request:
9 | branches:
10 | - "main"
11 |
12 | jobs:
13 | build-dotnet:
14 | runs-on: ubuntu-latest
15 | timeout-minutes: 10
16 | steps:
17 | - uses: actions/checkout@v3
18 | - uses: Cysharp/Actions/.github/actions/setup-dotnet@main
19 | with:
20 | dotnet-version: |
21 | 8.0.x
22 | - run: dotnet build -c Debug
23 | - run: dotnet test -c Debug --no-build
--------------------------------------------------------------------------------
/.github/workflows/build-release.yml:
--------------------------------------------------------------------------------
1 | name: Build-Release
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | tag:
7 | description: "tag: git tag you want create. (sample 1.0.0)"
8 | required: true
9 | dry-run:
10 | description: "dry-run: true will never create relase/nuget."
11 | required: true
12 | default: false
13 | type: boolean
14 |
15 | jobs:
16 | build-dotnet:
17 | runs-on: ubuntu-latest
18 | timeout-minutes: 10
19 | steps:
20 | - uses: actions/checkout@v3
21 | - uses: Cysharp/Actions/.github/actions/setup-dotnet@main
22 | # pack nuget
23 | - run: dotnet build -c Release -p:Version=${{ inputs.tag }}
24 | - run: dotnet test -c Release --no-build
25 | - run: dotnet pack -c Release --no-build -p:Version=${{ inputs.tag }} -o ./publish
26 | # Store artifacts.
27 | - uses: Cysharp/Actions/.github/actions/upload-artifact@main
28 | with:
29 | name: nuget
30 | path: ./publish/
31 | retention-days: 1
32 |
33 | create-release:
34 | needs: [build-dotnet]
35 | uses: Cysharp/Actions/.github/workflows/create-release.yaml@main
36 | with:
37 | commit-id: ${{ needs.update-packagejson.outputs.sha }}
38 | tag: ${{ inputs.tag }}
39 | dry-run: ${{ inputs.dry-run }}
40 | nuget-push: true
41 | release-upload: false
42 | secrets: inherit
43 |
--------------------------------------------------------------------------------
/.github/workflows/stale.yml:
--------------------------------------------------------------------------------
1 | name: "Close stale issues"
2 |
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | - cron: "0 0 * * *"
7 |
8 | jobs:
9 | stale:
10 | uses: Cysharp/Actions/.github/workflows/stale-issue.yaml@main
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
2 | [Bb]in/
3 | [Oo]bj/
4 |
5 | # mstest test results
6 | TestResults
7 |
8 | ## Ignore Visual Studio temporary files, build results, and
9 | ## files generated by popular Visual Studio add-ons.
10 |
11 | # User-specific files
12 | *.suo
13 | *.user
14 | *.sln.docstates
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Rr]elease/
19 | *_i.c
20 | *_p.c
21 | *.ilk
22 | *.obj
23 | *.pch
24 | *.pdb
25 | *.pgc
26 | *.pgd
27 | *.rsp
28 | *.sbr
29 | *.tlb
30 | *.tli
31 | *.tlh
32 | *.tmp
33 | *.log
34 | *.vspscc
35 | *.vssscc
36 | .builds
37 |
38 | # Visual C++ cache files
39 | ipch/
40 | *.aps
41 | *.ncb
42 | *.opensdf
43 | *.sdf
44 |
45 | # Visual Studio profiler
46 | *.psess
47 | *.vsp
48 | *.vspx
49 |
50 | # Guidance Automation Toolkit
51 | *.gpState
52 |
53 | # ReSharper is a .NET coding add-in
54 | _ReSharper*
55 |
56 | # NCrunch
57 | *.ncrunch*
58 | .*crunch*.local.xml
59 |
60 | # Installshield output folder
61 | [Ee]xpress
62 |
63 | # DocProject is a documentation generator add-in
64 | DocProject/buildhelp/
65 | DocProject/Help/*.HxT
66 | DocProject/Help/*.HxC
67 | DocProject/Help/*.hhc
68 | DocProject/Help/*.hhk
69 | DocProject/Help/*.hhp
70 | DocProject/Help/Html2
71 | DocProject/Help/html
72 |
73 | # Click-Once directory
74 | publish
75 |
76 | # Publish Web Output
77 | *.Publish.xml
78 |
79 | # NuGet Packages Directory
80 | # packages # upm pacakge will use Packages
81 |
82 | # Windows Azure Build Output
83 | csx
84 | *.build.csdef
85 |
86 | # Windows Store app package directory
87 | AppPackages/
88 |
89 | # Others
90 | [Bb]in
91 | [Oo]bj
92 | sql
93 | TestResults
94 | [Tt]est[Rr]esult*
95 | *.Cache
96 | ClientBin
97 | [Ss]tyle[Cc]op.*
98 | ~$*
99 | *.dbmdl
100 | Generated_Code #added for RIA/Silverlight projects
101 |
102 | # Backup & report files from converting an old project file to a newer
103 | # Visual Studio version. Backup files are not needed, because we have git ;-)
104 | _UpgradeReport_Files/
105 | Backup*/
106 | UpgradeLog*.XML
107 | .vs/config/applicationhost.config
108 | .vs/restore.dg
109 |
110 | nuget/tools/*
111 | nuget/*.nupkg
112 | nuget/*.unitypackage
113 | .vs/
114 |
115 | # Jetbrains Rider
116 | .idea/
117 |
118 | __packages/
119 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | latest
4 | enable
5 | true
6 | $(NoWarn);CS1591
7 | true
8 | $(MSBuildThisFileDirectory)opensource.snk
9 |
10 |
11 | $(Version)
12 | Cysharp
13 | Cysharp
14 | © Cysharp, Inc.
15 | https://github.com/Cysharp/PrivateProxy
16 | $(PackageProjectUrl)
17 | git
18 | MIT
19 | Icon.png
20 | $(MSBuildThisFileDirectory)opensource.snk
21 |
22 |
--------------------------------------------------------------------------------
/Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cysharp/PrivateProxy/8ddcd17466b423f77799dc3897438c40676037f6/Icon.png
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Cysharp, Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/PrivateProxy.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.8.34112.27
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F2E8F22F-A91D-457A-932A-E3647B964A94}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{FB22D44B-8937-4C9F-92A6-5A0A4190D1A2}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sandbox", "sandbox", "{7753D4E4-4967-4ED2-97CC-5B1A71B53BCD}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PrivateProxy.Generator", "src\PrivateProxy.Generator\PrivateProxy.Generator.csproj", "{57EEAB5C-EDFE-4186-8101-8B969A57CB6F}"
13 | EndProject
14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleAppNet8", "sandbox\ConsoleAppNet8\ConsoleAppNet8.csproj", "{F1349485-DCA7-4F9B-B434-DC6950988E7A}"
15 | EndProject
16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PrivateProxy.Tests", "tests\PrivateProxy.Tests\PrivateProxy.Tests.csproj", "{06BE6B13-4795-4E98-B1F4-BE3D10270C6C}"
17 | EndProject
18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassLibrary", "sandbox\ClassLibrary\ClassLibrary.csproj", "{23947225-D1A8-4097-B758-731E1D375CB0}"
19 | EndProject
20 | Global
21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
22 | Debug|Any CPU = Debug|Any CPU
23 | Release|Any CPU = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
26 | {57EEAB5C-EDFE-4186-8101-8B969A57CB6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {57EEAB5C-EDFE-4186-8101-8B969A57CB6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {57EEAB5C-EDFE-4186-8101-8B969A57CB6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {57EEAB5C-EDFE-4186-8101-8B969A57CB6F}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {F1349485-DCA7-4F9B-B434-DC6950988E7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {F1349485-DCA7-4F9B-B434-DC6950988E7A}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {F1349485-DCA7-4F9B-B434-DC6950988E7A}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {F1349485-DCA7-4F9B-B434-DC6950988E7A}.Release|Any CPU.Build.0 = Release|Any CPU
34 | {06BE6B13-4795-4E98-B1F4-BE3D10270C6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {06BE6B13-4795-4E98-B1F4-BE3D10270C6C}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {06BE6B13-4795-4E98-B1F4-BE3D10270C6C}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {06BE6B13-4795-4E98-B1F4-BE3D10270C6C}.Release|Any CPU.Build.0 = Release|Any CPU
38 | {23947225-D1A8-4097-B758-731E1D375CB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39 | {23947225-D1A8-4097-B758-731E1D375CB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
40 | {23947225-D1A8-4097-B758-731E1D375CB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
41 | {23947225-D1A8-4097-B758-731E1D375CB0}.Release|Any CPU.Build.0 = Release|Any CPU
42 | EndGlobalSection
43 | GlobalSection(SolutionProperties) = preSolution
44 | HideSolutionNode = FALSE
45 | EndGlobalSection
46 | GlobalSection(NestedProjects) = preSolution
47 | {57EEAB5C-EDFE-4186-8101-8B969A57CB6F} = {F2E8F22F-A91D-457A-932A-E3647B964A94}
48 | {F1349485-DCA7-4F9B-B434-DC6950988E7A} = {7753D4E4-4967-4ED2-97CC-5B1A71B53BCD}
49 | {06BE6B13-4795-4E98-B1F4-BE3D10270C6C} = {FB22D44B-8937-4C9F-92A6-5A0A4190D1A2}
50 | {23947225-D1A8-4097-B758-731E1D375CB0} = {7753D4E4-4967-4ED2-97CC-5B1A71B53BCD}
51 | EndGlobalSection
52 | GlobalSection(ExtensibilityGlobals) = postSolution
53 | SolutionGuid = {800D301B-C4F2-454D-87B9-BA3A404A9937}
54 | EndGlobalSection
55 | EndGlobal
56 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | > [!Important]
2 | > The current Roslyn/Source Generator has limitations in accessing private members of referenced projects. Moreover, accessing private members can lead to situations where it works fine in the IDE but fails during the build process. For more details, please refer to the Roslyn issue, [MetadataImportOptions.All doesn't work in source generators?](https://github.com/dotnet/roslyn/issues/72369). To avoid confusion, I will archive this until the relationship between Source Generators and private members is improved.
3 |
4 | # PrivateProxy
5 | [](https://github.com/Cysharp/PrivateProxy/actions) [](https://github.com/Cysharp/PrivateProxy/releases)
6 | [](https://nuget.org/packages/PrivateProxy)
7 |
8 | Source Generator and .NET 8 [UnsafeAccessor](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.unsafeaccessorattribute) based high-performance strongly-typed private accessor for unit testing and runtime.
9 |
10 | `[GeneratePrivateProxy(typeof(TargetType))]` generates accessor proxy.
11 |
12 | ```csharp
13 | using PrivateProxy;
14 |
15 | public class Sample
16 | {
17 | int _field1;
18 | int PrivateAdd(int x, int y) => x + y;
19 | }
20 |
21 | [GeneratePrivateProxy(typeof(Sample))]
22 | public partial struct SampleProxy;
23 | ```
24 |
25 | ```csharp
26 | // Source Generator generate this type
27 | partial struct SampleProxy(Sample target)
28 | {
29 | [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_field1")]
30 | static extern ref int ___field1__(Sample target);
31 |
32 | [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "PrivateAdd")]
33 | static extern int __PrivateAdd__(Sample target, int x, int y);
34 |
35 | public ref int _field1 => ref ___field1__(target);
36 | public int PrivateAdd(int x, int y) => __PrivateAdd__(target, x, y);
37 | }
38 |
39 | public static class SamplePrivateProxyExtensions
40 | {
41 | public static SampleProxy AsPrivateProxy(this Sample target)
42 | {
43 | return new SampleProxy(target);
44 | }
45 | }
46 | ```
47 |
48 | ```csharp
49 | // You can access like this.
50 | var sample = new Sample();
51 | sample.AsPrivateProxy()._field1 = 10;
52 | ```
53 |
54 | Generated code is fully typed, you can access private filed via IntelliSense and when private field was changed, can check compiler error.
55 |
56 | 
57 |
58 | * No performance penalty, it can be used not only for unit testing but also for runtime
59 | * No runtime dependency(all codes are included in source generator)
60 | * Private accessors are strongly-typed
61 | * Supports both instance, static and fields, properties, methods
62 | * Supports `ref`, `out`, `in`, and `ref readonly` method parameters
63 | * Supports `readonly` field and property
64 | * Supports `ref` return
65 | * Supports mutable struct
66 | * Supports instance constructor
67 |
68 | For example, this is the mutable struct and static, ref return, and constructor sample.
69 |
70 | ```csharp
71 | using PrivateProxy;
72 |
73 | public struct MutableStructSample
74 | {
75 | int _counter;
76 | void Increment() => _counter++;
77 |
78 | // static and ref sample
79 | static ref int GetInstanceCounter(ref MutableStructSample sample) => ref sample._counter;
80 |
81 | // constructor sample
82 | MutalbeStructSample(int x, int y) { /* ... */ }
83 | }
84 |
85 | // use ref partial struct
86 | [GeneratePrivateProxy(typeof(MutableStructSample))]
87 | public ref partial struct MutableStructSampleProxy;
88 | ```
89 |
90 | ```csharp
91 | var sample = new MutableStructSample();
92 | var proxy = sample.AsPrivateProxy();
93 | proxy.Increment();
94 | proxy.Increment();
95 | proxy.Increment();
96 |
97 | // call private static method.
98 | ref var counter = ref MutableStructSampleProxy.GetInstanceCounter(ref sample);
99 |
100 | Console.WriteLine(counter); // 3
101 | counter = 9999;
102 | Console.WriteLine(proxy._counter); // 9999
103 |
104 | // call private constructor and create instance.
105 | var sample = MutableStructSampleProxy.CreateMutableStructFromConstructor(111, 222);
106 | ```
107 |
108 | Installation
109 | ---
110 | This library is distributed via NuGet, minimum requirement is .NET 8 and C# 12.
111 |
112 | PM> Install-Package [PrivateProxy](https://www.nuget.org/packages/PrivateProxy)
113 |
114 | Package provides only analyzer and generated code does not dependent any other libraries.
115 |
116 | Supported Members
117 | ---
118 | `GeneratePrivateProxy` target type and member
119 |
120 | ```csharp
121 | public class/* struct */ SupportTarget
122 | {
123 | // field
124 | private int field;
125 | private readonly int readOnlyField;
126 |
127 | // property
128 | private int Property { get; set; }
129 | private int GetOnlyProperty { get; }
130 | public int GetOnlyPrivateProperty { private get; set; }
131 | public int SetOnlyPrivateProperty { get; private set; }
132 | private int SetOnlyProperty { set => field = value; }
133 | private ref int RefGetOnlyProperty => ref field;
134 | private ref readonly int RefReadOnlyGetOnlyProperty => ref field;
135 |
136 | // method
137 | private void VoidMethod() { }
138 | private int ReturnMethod() => field;
139 | private int ParameterMethod(int x, int y) => x + y;
140 | private void RefOutInMethod(in int x, ref int y, out int z, ref readonly int xyz) { z = field; }
141 | private ref int RefReturnMethod() => ref field;
142 | private ref readonly int RefReadOnlyReturnMethod() => ref field;
143 |
144 | // static
145 | static int staticField;
146 | static readonly int staticReadOnlyField;
147 | static int StaticProperty { get; set; }
148 | static int StaticGetOnlyProperty { get; }
149 | public static int StaticGetOnlyPrivateProperty { private get; set; }
150 | public static int StaticSetOnlyPrivateProperty { get; private set; }
151 | private static int StaticSetOnlyProperty { set => staticField = value; }
152 | private static ref int StaticRefGetOnlyProperty => ref staticField;
153 | private static ref readonly int StaticRefReadOnlyGetOnlyProperty => ref staticField;
154 | private static void StaticVoidMethod() { }
155 | static int StaticReturnMethod() => staticField;
156 | static int StaticParameterMethod(int x, int y) => x + y;
157 | static void StaticRefOutInMethod(in int x, ref int y, out int z, ref readonly int xyz) { z = staticField; }
158 | static ref int StaticRefReturnMethod() => ref staticField;
159 | static ref readonly int StaticRefReadOnlyReturnMethod() => ref staticField;
160 | static ref int StaticRefReturnMethodParameter() => ref staticField;
161 |
162 | // constructor
163 | SupportTarget() { }
164 | SupportTarget(int x, int y) { }
165 | }
166 | ```
167 |
168 | Proxy type can be `class` => `class` or `struct`, `struct` => `ref struct`.
169 |
170 | ```csharp
171 | using PrivateProxy;
172 |
173 | public class Sample;
174 |
175 | // class proxy type both supports class and struct(recommend is struct)
176 | [GeneratePrivateProxy(typeof(Sample))]
177 | public partial class SampleProxy1;
178 |
179 | [GeneratePrivateProxy(typeof(Sample))]
180 | public partial struct SampleProxy2;
181 |
182 | public struct SamplleStruct;
183 |
184 | // struct only supports ref struct(when use standard struct, analyzer shows error)
185 | [GeneratePrivateProxy(typeof(SamplleStruct))]
186 | public ref partial struct SamplleStructProxy;
187 | ```
188 |
189 | GeneratePrivateProxyAttribute has two constructor, when use `PrivateProxyGenerateKinds` parameter, can configure generate member kind.
190 |
191 | ```csharp
192 | public GeneratePrivateProxyAttribute(Type target) // use PrivateProxyGenerateKinds.All
193 | public GeneratePrivateProxyAttribute(Type target, PrivateProxyGenerateKinds generateKinds)
194 |
195 | [Flags]
196 | internal enum PrivateProxyGenerateKinds
197 | {
198 | All = 0, // Field | Method | Property | Instance | Static | Constructor
199 | Field = 1,
200 | Method = 2,
201 | Property = 4,
202 | Instance = 8,
203 | Static = 16,
204 | Constructor = 32,
205 | }
206 | ```
207 |
208 | Limitation
209 | ---
210 | Currently, the following features are not supported
211 |
212 | * Generics type
213 | * see: [dotnet/runtime#89439 Implement unbound Generic support for UnsafeAccessorAttribute](https://github.com/dotnet/runtime/issues/89439)
214 | * Static class, Non public return/parameter type(ignore generate)
215 | * see: [dotnet/runtime#90081 UnsafeAccessorTypeAttribute for static or private type access](https://github.com/dotnet/runtime/issues/90081)
216 | * ref struct
217 | * ref field can not pass to ref method parameter
218 | * Types from external dll(for example `String`)
219 | * Probably, by enabling MetadataImportOptions.All in the Source Generator, it should be possible to read it. However, I haven't been able to find a way to do that. I need help.
220 |
221 | License
222 | ---
223 | This library is licensed under the MIT License.
224 |
--------------------------------------------------------------------------------
/opensource.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cysharp/PrivateProxy/8ddcd17466b423f77799dc3897438c40676037f6/opensource.snk
--------------------------------------------------------------------------------
/sandbox/ClassLibrary/Class1.cs:
--------------------------------------------------------------------------------
1 | #pragma warning disable CS8604
2 | #pragma warning disable CS8321
3 | #pragma warning disable CS0414
4 |
5 | using System.Runtime.CompilerServices;
6 | using System.Runtime.InteropServices.Marshalling;
7 |
8 | namespace ClassLibrary
9 | {
10 | public class MyClass
11 | {
12 | private int _privateField;
13 | private NestedPrivate _nesterPrivate = new NestedPrivate();
14 | private NestedPublic _nesterPublic = new NestedPublic();
15 | private InternalEnum _internalEnum = default;
16 |
17 | MyClass()
18 | {
19 | }
20 |
21 | private void PrivateMethod()
22 | {
23 | _privateField++;
24 | }
25 |
26 | class NestedPrivate
27 | {
28 |
29 | }
30 |
31 | public class NestedPublic
32 | {
33 |
34 | }
35 | }
36 |
37 | internal enum InternalEnum
38 | {
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/sandbox/ClassLibrary/ClassLibrary.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 | false
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/sandbox/ConsoleAppNet8/BlogSample.cs:
--------------------------------------------------------------------------------
1 | #pragma warning disable CS8604
2 | #pragma warning disable CS8321
3 | #pragma warning disable CS0414
4 | #pragma warning disable CS0169
5 | #pragma warning disable CS0649
6 |
7 | using PrivateProxy;
8 | using System;
9 | using System.Collections.Generic;
10 | using System.Linq;
11 | using System.Runtime.CompilerServices;
12 | using System.Security.Principal;
13 | using System.Text;
14 | using System.Threading.Tasks;
15 |
16 | public class Sample
17 | {
18 | int _field1;
19 | int _field2;
20 | public int PrivateProperty { get; private set; }
21 | int PrivateAdd(int x, int y) => x + y;
22 |
23 | Sample()
24 | {
25 | }
26 |
27 | Sample(int x, int y)
28 | {
29 | _field1 = x;
30 | _field2 = y;
31 | }
32 | }
33 |
34 | [GeneratePrivateProxy(typeof(Sample))]
35 | public partial struct SampleProxy;
36 |
37 |
38 | public struct MutableStructSample
39 | {
40 | int _counter;
41 | void Increment() => _counter++;
42 |
43 | // static and ref sample
44 | static ref int GetInstanceCounter(ref MutableStructSample sample) => ref sample._counter;
45 | }
46 |
47 | // use ref partial struct
48 | [GeneratePrivateProxy(typeof(MutableStructSample))]
49 | public ref partial struct MutableStructSampleProxy;
50 |
51 |
52 | public static class Calle
53 | {
54 | public static void Foo()
55 | {
56 | var sample = new MutableStructSample();
57 | var proxy = sample.AsPrivateProxy();
58 | proxy.Increment();
59 | proxy.Increment();
60 | proxy.Increment();
61 |
62 | // call private static method.
63 | ref var counter = ref MutableStructSampleProxy.GetInstanceCounter(ref sample);
64 |
65 | Console.WriteLine(counter); // 3
66 | counter = 9999;
67 | Console.WriteLine(proxy._counter); // 9999
68 | }
69 | }
--------------------------------------------------------------------------------
/sandbox/ConsoleAppNet8/ConsoleAppNet8.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net8.0
6 | disable
7 | enable
8 | false
9 |
10 |
11 |
12 |
13 | Analyzer
14 | false
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/sandbox/ConsoleAppNet8/Program.cs:
--------------------------------------------------------------------------------
1 | #pragma warning disable CS8604
2 | #pragma warning disable CS8321
3 | #pragma warning disable CS0414
4 | #pragma warning disable CS0169
5 | #pragma warning disable CS0649
6 |
7 | // using PrivateProxy;
8 | using PrivateProxy;
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Data;
12 | using System.Runtime.CompilerServices;
13 | using System.Runtime.InteropServices;
14 |
15 |
16 | Calle.Foo();
17 |
18 |
19 | static void Bar(ref int x, out int y, in int z)
20 | {
21 | Foo(ref x, out y, in z);
22 | }
23 |
24 |
25 | static void Foo(ref int x, out int y, in int z)
26 | {
27 | y = 10;
28 | }
29 |
30 |
31 |
32 |
33 | [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "field")]
34 | static extern ref int UnsafeAccessorTest(ref global::StructTest target);
35 |
36 | [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "field")]
37 | static extern ref int UnsafeAccessorTest2(global::MyClass target);
38 |
39 | [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "TakoyakiX")]
40 | static extern int UnsafeAccessorTest3(in global::MyClass target, int x, int y);
41 |
42 | public partial struct Hoge
43 | {
44 | static Hoge ____default__ = default(Hoge);
45 |
46 | public void Tako() { }
47 |
48 | public int MyProperty { get; set; }
49 | static int y;
50 | public ref readonly int MyProperty2 { get => ref y; }
51 |
52 | public int Foo
53 | {
54 | set
55 | {
56 | }
57 | }
58 |
59 |
60 | [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "TakoyakiX")]
61 | static extern int UnsafeAccessorTest3(ref global::Hoge target, int x, int y);
62 |
63 | public static int CallTakoyakiX(int x, int y) => UnsafeAccessorTest3(ref ____default__, x, y);
64 |
65 | static int TakoyakiX(int x, int y)
66 | {
67 | return x + y;
68 | }
69 | }
70 |
71 |
72 | [GeneratePrivateProxy(typeof(ClassLibrary.MyClass))]
73 | public partial struct ClassLibraryMyClassProxy;
74 |
75 |
76 | public class MyClass
77 | {
78 | int field;
79 |
80 | public int MyProperty { get; set; }
81 |
82 | public void Show() => Console.WriteLine(field);
83 |
84 |
85 | public int Show2 => field;
86 |
87 |
88 | public ref readonly int RetField() => ref field;
89 |
90 |
91 | static int TakoyakiX(int x, int y)
92 | {
93 | return x + y;
94 | }
95 | }
96 |
97 |
98 | public static class ExtTest
99 | {
100 | public static MyClassProxy AsProxy2(this MyClass mc)
101 | {
102 | return new MyClassProxy(mc);
103 | }
104 | }
105 |
106 |
107 | //public ref struct MyClass
108 | //{
109 |
110 | //}
111 |
112 | ref partial struct Tako
113 | {
114 | }
115 |
116 | public struct MyClassProxy
117 | {
118 | MyClass target;
119 |
120 | public MyClassProxy(MyClass target)
121 | {
122 | this.target = target;
123 | }
124 | }
125 |
126 |
127 | public struct TestRef
128 | {
129 | }
130 |
131 | public ref struct TestRefRef
132 | {
133 | ref TestRef target;
134 |
135 | public TestRefRef(ref TestRef target)
136 | {
137 | this.target = ref target;
138 | }
139 |
140 |
141 |
142 | [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "field")]
143 | static extern ref int __field__(ref global::TestRef target);
144 |
145 | public ref int field => ref __field__(ref target);
146 |
147 | public int field2
148 | {
149 | get => __field__(ref target);
150 | set => __field__(ref target) = value;
151 | }
152 |
153 | }
154 |
155 |
156 |
157 | public struct StructTest
158 | {
159 | //public StructTest(ref int xxx)
160 | //{
161 | //this.refReadOnly = ref xxx;
162 | //}
163 |
164 | // Roslyn IFieldSymbol behaviour
165 | public int field; // RefKind=None
166 | public readonly int readOnlyField; // RefKind=None, IsReadOnly=true | can't assign
167 | //public ref int refField; // RefKind=Ref
168 | //public ref readonly int refReadOnly; // RefKind=In | can't assign
169 | //public readonly ref int readonlyRef; // RefKind=Ref, IsReadOnly=true
170 | //public readonly ref readonly int readOnlyRefReadOnly; // RefKind=In, IsReadOnly=true | can't assign
171 |
172 |
173 |
174 |
175 | // Roslyn IPropertySymbol behaviour
176 | public int Prop0 => 1;
177 | public readonly int Prop1 => 1;
178 | //public ref int Prop2 => ref refField; // ReturnsBy
179 | //public ref readonly int Prop3 => ref refField;
180 | //public readonly ref int Prop4 => ref refField;
181 | //public readonly ref readonly int Prop5 => ref refField;
182 |
183 | // Roslyn IMethodSYmbol behaviour
184 | public int Method0() => 1;
185 | public readonly int Method1() => 1;
186 | //public ref int Method2() => ref refField;
187 | //public ref readonly int Method3() => ref refField;
188 | //public readonly ref int Method4() => ref refField;
189 | //public readonly ref readonly int Method5() => ref refField;
190 |
191 | public void Show()
192 | {
193 | Console.WriteLine(field);
194 | }
195 |
196 | static void HogeMogeDayo()
197 | {
198 | }
199 | }
200 |
201 | //public ref partial struct RefStructTestProxy2
202 | //{
203 | // RefStructTest target;
204 |
205 | // public RefStructTestProxy2(ref RefStructTest target)
206 | // {
207 | // this.target = target;
208 | // }
209 |
210 | // static global::RefStructTest ____static_instance => default;
211 |
212 | // public RefStructTestProxy2(ref RefStructTest test)
213 | // {
214 | // // this.target = MemoryMarshal.GetReference(test;
215 | // }
216 |
217 |
218 | // [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "HogeMogeDayo")]
219 | // static extern void __HogeMogeDayo__(in global::RefStructTest target);
220 |
221 | // public static void HogeMogeDayo()
222 | // {
223 | // __HogeMogeDayo__(____static_instance);
224 | // }
225 |
226 |
227 | //}
228 |
229 |
230 |
231 |
232 | [GeneratePrivateProxy(typeof(StructTest))]
233 | public ref partial struct StructTestProxy;
234 |
235 |
236 |
237 | partial struct PrivateClass2Proxy2
238 | {
239 | global::PrivateClass2 target;
240 |
241 | public PrivateClass2Proxy2(global::PrivateClass2 target)
242 | {
243 | this.target = target;
244 | }
245 |
246 | [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_privateField")]
247 | static extern ref int ___privateField__(global::PrivateClass2 target);
248 | public ref int _privateField => ref ___privateField__(target);
249 |
250 | [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_PrivateMyProperty")]
251 | static extern int __get_PrivateMyProperty__(global::PrivateClass2 target);
252 |
253 | [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_PrivateMyProperty")]
254 | static extern void __set_PrivateMyProperty__(global::PrivateClass2 target, int value);
255 |
256 | public int PrivateMyProperty
257 | {
258 | get => __get_PrivateMyProperty__(target);
259 | set => __set_PrivateMyProperty__(target, value);
260 | }
261 |
262 |
263 |
264 | public void Tako() => Console.WriteLine("hoge");
265 | }
266 |
267 |
268 |
269 |
270 |
271 | public class PrivateClass
272 | {
273 | private int _privateField;
274 | private static int _privateStaticField;
275 |
276 | private int PrivateMethod() => 10;
277 | private static int PrivateStaticMethod() => 99;
278 |
279 |
280 | public int PublicField;
281 | public readonly int ReadOnlyPublicField;
282 | }
283 |
284 |
285 | public class PrivateClass2
286 | {
287 | private int _privateField;
288 |
289 | private int PrivateMyProperty { get; set; }
290 |
291 | public void ShowPrivateField()
292 | {
293 |
294 |
295 | Console.WriteLine(_privateField);
296 | }
297 |
298 | void Show2()
299 | {
300 | Console.WriteLine("2");
301 | }
302 |
303 |
304 |
305 | void ShowWithP(int x, int y)
306 | {
307 | Console.WriteLine(x + y);
308 | }
309 |
310 | int FooBarBaz(ref int x, out int y, in int z) => y = x + z;
311 | }
312 |
313 | [GeneratePrivateProxy(typeof(PrivateClass2))]
314 | partial struct PrivateClass2Proxy;
315 |
316 | public class TakoyakIX
317 | {
318 | public int get_MyProperty() => 10;
319 | // public int MyProperty { get; set; }
320 | }
321 |
322 |
323 | public struct NonRefStructTest
324 | {
325 | int field;
326 | }
327 |
328 | public ref partial struct NonRefStructTestProxy333
329 | {
330 | global::NonRefStructTest target;
331 |
332 | public NonRefStructTestProxy333(ref global::NonRefStructTest target)
333 | {
334 | this.target = target;
335 | }
336 |
337 | //[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "field")]
338 | //static extern ref int __field__(ref global::NonRefStructTest target);
339 |
340 | //public ref int field
341 | //{
342 | // get
343 | // {
344 | // return ref __field__(ref this.target);
345 | // }
346 | //}
347 |
348 | [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_Prop0")]
349 | static extern int __get_Prop0__(ref global::NonRefStructTest target);
350 |
351 | public int Prop0
352 | {
353 | get => __get_Prop0__(ref this.target);
354 | }
355 |
356 |
357 | }
--------------------------------------------------------------------------------
/src/PrivateProxy.Generator/DiagnosticDescriptors.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace PrivateProxy.Generator;
4 |
5 | public static class DiagnosticDescriptors
6 | {
7 | const string Category = "PrivateProxy";
8 |
9 | public static readonly DiagnosticDescriptor MustBePartial = new(
10 | id: "PP001",
11 | title: "PrivateProxy type must be partial",
12 | messageFormat: "The PrivateProxy type '{0}' must be partial",
13 | category: Category,
14 | defaultSeverity: DiagnosticSeverity.Error,
15 | isEnabledByDefault: true);
16 |
17 | public static readonly DiagnosticDescriptor NotAllowReadOnly = new(
18 | id: "PP002",
19 | title: "PrivateProxy does not allow readonly struct",
20 | messageFormat: "The PrivateProxy struct '{0}' does not allow readonly struct",
21 | category: Category,
22 | defaultSeverity: DiagnosticSeverity.Error,
23 | isEnabledByDefault: true);
24 |
25 | public static readonly DiagnosticDescriptor ClassNotAllowRefStruct = new(
26 | id: "PP003",
27 | title: "PrivateProxy class does not allow ref struct",
28 | messageFormat: "The PrivateProxy class(reference-type) '{0}' does not allow ref struct",
29 | category: Category,
30 | defaultSeverity: DiagnosticSeverity.Error,
31 | isEnabledByDefault: true);
32 |
33 | public static readonly DiagnosticDescriptor StructNotAllowClass = new(
34 | id: "PP004",
35 | title: "PrivateProxy struct does not allow class",
36 | messageFormat: "The PrivateProxy struct '{0}' does not allow class, only allows ref struct",
37 | category: Category,
38 | defaultSeverity: DiagnosticSeverity.Error,
39 | isEnabledByDefault: true);
40 |
41 | public static readonly DiagnosticDescriptor StructNotAllowStruct = new(
42 | id: "PP005",
43 | title: "PrivateProxy struct does not allow struct",
44 | messageFormat: "The PrivateProxy struct '{0}' does not allow struct, only allows ref struct",
45 | category: Category,
46 | defaultSeverity: DiagnosticSeverity.Error,
47 | isEnabledByDefault: true);
48 |
49 | public static readonly DiagnosticDescriptor RefStructNotSupported = new(
50 | id: "PP006",
51 | title: "PrivateProxy does not support ref struct",
52 | messageFormat: "The PrivateProxy does not support ref struct",
53 | category: Category,
54 | defaultSeverity: DiagnosticSeverity.Error,
55 | isEnabledByDefault: true);
56 |
57 | public static readonly DiagnosticDescriptor GenericsNotSupported = new(
58 | id: "PP007",
59 | title: "PrivateProxy does not support generics type",
60 | messageFormat: "The PrivateProxy does not support generics type",
61 | category: Category,
62 | defaultSeverity: DiagnosticSeverity.Error,
63 | isEnabledByDefault: true);
64 |
65 | public static readonly DiagnosticDescriptor StaticNotSupported = new(
66 | id: "PP008",
67 | title: "PrivateProxy does not support static type",
68 | messageFormat: "The PrivateProxy does not support static type",
69 | category: Category,
70 | defaultSeverity: DiagnosticSeverity.Error,
71 | isEnabledByDefault: true);
72 | }
--------------------------------------------------------------------------------
/src/PrivateProxy.Generator/EmitHelper.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using System.Collections.Immutable;
3 |
4 | namespace PrivateProxy.Generator;
5 |
6 | internal static class EmitHelper
7 | {
8 | public static string ToCode(this Accessibility accessibility)
9 | {
10 | switch (accessibility)
11 | {
12 | case Accessibility.NotApplicable:
13 | return "";
14 | case Accessibility.Private:
15 | return "private";
16 | case Accessibility.ProtectedAndInternal:
17 | return "private protected";
18 | case Accessibility.Protected:
19 | return "protected";
20 | case Accessibility.Internal:
21 | return "internal";
22 | case Accessibility.ProtectedOrInternal:
23 | return "protected internal";
24 | case Accessibility.Public:
25 | return "public";
26 | default:
27 | return "";
28 | }
29 | }
30 |
31 | public static string ToParameterPrefix(this RefKind kind)
32 | {
33 | switch (kind)
34 | {
35 | case RefKind.Out: return "out ";
36 | case RefKind.Ref: return "ref ";
37 | case RefKind.In: return "in ";
38 | // case RefKind.RefReadOnlyParameter: return "ref readonly ";
39 | case (RefKind)4: return "ref readonly ";
40 | case RefKind.None: return "";
41 | default: return "";
42 | }
43 | }
44 |
45 | public static string ToUseParameterPrefix(this RefKind kind)
46 | {
47 | switch (kind)
48 | {
49 | case RefKind.Out: return "out ";
50 | case RefKind.Ref: return "ref ";
51 | case RefKind.In: return "in ";
52 | case (RefKind)4: return "in "; // ref readonly
53 | case RefKind.None: return "";
54 | default: return "";
55 | }
56 | }
57 |
58 | public static string ToFullyQualifiedFormatString(this ISymbol symbol)
59 | {
60 | return symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
61 | }
62 |
63 | public static string ForEachLine(string indent, IEnumerable values, Func lineSelector)
64 | {
65 | return string.Join(Environment.NewLine, values.Select(x => indent + lineSelector(x)));
66 | }
67 |
68 | public static string ForLine(string indent, int begin, int end, Func lineSelector)
69 | {
70 | return string.Join(Environment.NewLine, Enumerable.Range(begin, end - begin).Select(x => indent + lineSelector(x)));
71 | }
72 |
73 | public static string If(bool condition, string code)
74 | {
75 | return condition ? code : "";
76 | }
77 |
78 | public static string If(bool condition, string ifCode, string elseCode)
79 | {
80 | return condition ? ifCode : elseCode;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/PrivateProxy.Generator/MetaMember.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using System.Collections.Immutable;
3 |
4 | namespace PrivateProxy.Generator;
5 |
6 | public enum MemberKind
7 | {
8 | Field, Property, Method, Constructor
9 | }
10 |
11 | public class MetaMember
12 | {
13 | public ITypeSymbol MemberType { get; }
14 | public string MemberTypeFullName { get; }
15 | public string Name { get; }
16 | public bool IsPublic { get; }
17 | public bool IsStatic { get; }
18 | public MemberKind MemberKind { get; }
19 |
20 | public bool HasGetMethod { get; } // only for property
21 | public bool HasSetMethod { get; } // only for property
22 |
23 | public bool IsRequireReadOnly { get; }
24 | public bool IsRefReturn { get; }
25 |
26 | public ImmutableArray MethodParameters { get; } // only for method
27 |
28 | public MetaMember(ISymbol symbol)
29 | {
30 | this.Name = symbol.Name;
31 | this.IsStatic = symbol.IsStatic;
32 |
33 | if (symbol is IFieldSymbol f)
34 | {
35 | // PrivateProxy only handles assignable(use readonly keyword)
36 |
37 | // int field; // RefKind=None
38 | // readonly int readOnlyField; // RefKind=None, IsReadOnly=true | can't assign
39 | // ref int refField; // RefKind=Ref
40 | // ref readonly int refReadOnly; // RefKind=In | can't assign
41 | // readonly ref int readonlyRef; // RefKind=Ref, IsReadOnly=true
42 | // readonly ref readonly int readOnlyRefReadOnly; // RefKind=In, IsReadOnly=true | can't assign
43 |
44 | this.MemberType = f.Type;
45 | this.MemberKind = MemberKind.Field;
46 | this.IsRequireReadOnly = (f.RefKind, f.IsReadOnly) switch
47 | {
48 | (RefKind.None, true) => true,
49 | (RefKind.In, _) => true,
50 | _ => false
51 | };
52 | this.IsRefReturn = f.RefKind != RefKind.None;
53 | }
54 | else if (symbol is IPropertySymbol p)
55 | {
56 | // int Prop0 => 1;
57 | // readonly int Prop1 => 1;
58 | // ref int Prop2 => ref refField; // ReturnsByRef:true
59 | // ref readonly int Prop3 => ref refField; // ReturnsByRefReadOnly:true | can't assign
60 | // readonly ref int Prop4 => ref refField; // ReturnsByRef:true
61 | // readonly ref readonly int Prop5 => ref refField; // ReturnsByRefReadOnly:true | can't assign
62 |
63 | this.MemberType = p.Type;
64 | this.MemberKind = MemberKind.Property;
65 | this.HasGetMethod = p.GetMethod != null;
66 | this.HasSetMethod = p.SetMethod != null;
67 |
68 | (this.IsRefReturn, this.IsRequireReadOnly) = (p.ReturnsByRef, p.ReturnsByRefReadonly) switch
69 | {
70 | (true, true) => (true, true), // but maybe don't come here.
71 | (true, false) => (true, false),
72 | (false, true) => (true, true),
73 | (false, false) => (false, false),
74 | };
75 | }
76 | else if (symbol is IMethodSymbol m)
77 | {
78 | // same as property
79 | // int Method0() => 1;
80 | // readonly int Method1() => 1;
81 | // ref int Method2() => ref refField;
82 | // ref readonly int Method3() => ref refField;
83 | // readonly ref int Method4() => ref refField;
84 | // readonly ref readonly int Method5() => ref refField;
85 |
86 | this.MemberType = m.ReturnType;
87 | this.MethodParameters = m.Parameters;
88 | this.MemberKind = m.Name == ".ctor" ? MemberKind.Constructor : MemberKind.Method;
89 |
90 | (this.IsRefReturn, this.IsRequireReadOnly) = (m.ReturnsByRef, m.ReturnsByRefReadonly) switch
91 | {
92 | (true, true) => (true, true), // but maybe don't come here.
93 | (true, false) => (true, false),
94 | (false, true) => (true, true),
95 | (false, false) => (false, false),
96 | };
97 | }
98 | else
99 | {
100 | throw new InvalidOperationException("Symbol type is invalid. " + symbol.GetType().FullName);
101 | }
102 | this.MemberTypeFullName = this.MemberType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/PrivateProxy.Generator/PrivateProxy.Generator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | 12
6 | enable
7 | cs
8 | enable
9 |
10 | PrivateProxy
11 |
12 | $(TargetsForTfmSpecificContentInPackage);PackBuildOutputs
13 | false
14 | true
15 | false
16 | true
17 | true
18 | codegenerator
19 | PrivateProxy SourceGenerator.
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | all
29 | runtime; build; native; contentfiles; analyzers; buildtransitive
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/src/PrivateProxy.Generator/PrivateProxyGenerator.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Immutable;
2 | using Microsoft.CodeAnalysis;
3 | using Microsoft.CodeAnalysis.CSharp;
4 | using Microsoft.CodeAnalysis.CSharp.Syntax;
5 | using System.Text;
6 | using static PrivateProxy.Generator.EmitHelper;
7 |
8 | namespace PrivateProxy.Generator;
9 |
10 | [Generator(LanguageNames.CSharp)]
11 | public partial class PrivateProxyGenerator : IIncrementalGenerator
12 | {
13 | public void Initialize(IncrementalGeneratorInitializationContext context)
14 | {
15 | context.RegisterPostInitializationOutput(EmitAttributes);
16 |
17 | var source = context.SyntaxProvider.ForAttributeWithMetadataName(
18 | "PrivateProxy.GeneratePrivateProxyAttribute",
19 | static (node, token) => node is StructDeclarationSyntax or ClassDeclarationSyntax,
20 | static (context, token) => context);
21 |
22 | // NOTE: currently does not provide private metadata from external dll.
23 | // context.CompilationProvider.Select((x, token) => x.WithOptions(x.Options.WithMetadataImportOptions(MetadataImportOptions.All));
24 |
25 | context.RegisterSourceOutput(source, Emit);
26 | }
27 |
28 | static void EmitAttributes(IncrementalGeneratorPostInitializationContext context)
29 | {
30 | context.AddSource("GeneratePrivateProxyAttribute.cs", """
31 | using System;
32 | using System.Collections.Generic;
33 |
34 | namespace PrivateProxy
35 | {
36 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
37 | internal sealed class GeneratePrivateProxyAttribute : Attribute
38 | {
39 | public Type Target { get; }
40 | public PrivateProxyGenerateKinds GenerateKinds { get; }
41 |
42 | public GeneratePrivateProxyAttribute(Type target)
43 | {
44 | this.Target = target;
45 | this.GenerateKinds = PrivateProxyGenerateKinds.All;
46 | }
47 |
48 | public GeneratePrivateProxyAttribute(Type target, PrivateProxyGenerateKinds generateKinds)
49 | {
50 | this.Target = target;
51 | this.GenerateKinds = generateKinds;
52 | }
53 | }
54 |
55 | [Flags]
56 | internal enum PrivateProxyGenerateKinds
57 | {
58 | All = 0, // Field | Method | Property | Instance | Static | Constructor
59 | Field = 1,
60 | Method = 2,
61 | Property = 4,
62 | Instance = 8,
63 | Static = 16,
64 | Constructor = 32,
65 | }
66 | }
67 | """);
68 | }
69 |
70 | [Flags]
71 | public enum PrivateProxyGenerateKinds
72 | {
73 | All = 0, // Field | Method | Property | Instance | Static | Constructor
74 | Field = 1,
75 | Method = 2,
76 | Property = 4,
77 | Instance = 8,
78 | Static = 16,
79 | Constructor = 32,
80 | }
81 |
82 | static void Emit(SourceProductionContext context, GeneratorAttributeSyntaxContext source)
83 | {
84 | var attr = source.Attributes[0]; // allowMultiple:false
85 | GetAttributeParameters(attr, out var targetType, out var kind);
86 |
87 | var members = GetMembers(targetType, kind);
88 |
89 | if (!Verify(context, (TypeDeclarationSyntax)source.TargetNode, (INamedTypeSymbol)source.TargetSymbol, targetType))
90 | {
91 | return;
92 | }
93 |
94 | if (members.Length == 0)
95 | {
96 | return;
97 | }
98 |
99 | //// Generate Code
100 | var code = EmitCode((ITypeSymbol)source.TargetSymbol, targetType, members);
101 | AddSource(context, source.TargetSymbol, code);
102 | }
103 |
104 | static void GetAttributeParameters(AttributeData attr, out INamedTypeSymbol targetType, out PrivateProxyGenerateKinds kind)
105 | {
106 | // Extract attribute parameter
107 | // public GeneratePrivateProxyAttribute(Type target)
108 | // public GeneratePrivateProxyAttribute(Type target, PrivateProxyGenerateKinds generateKinds)
109 |
110 | targetType = (INamedTypeSymbol)attr.ConstructorArguments[0].Value!;
111 |
112 | if (attr.ConstructorArguments.Length == 1)
113 | {
114 | kind = PrivateProxyGenerateKinds.All;
115 | }
116 | else
117 | {
118 | kind = (PrivateProxyGenerateKinds)attr.ConstructorArguments[1].Value!;
119 | }
120 | }
121 |
122 | static MetaMember[] GetMembers(INamedTypeSymbol targetType, PrivateProxyGenerateKinds kind)
123 | {
124 | var members = targetType.GetMembers();
125 |
126 | var list = new List(members.Length);
127 |
128 | kind = (kind == PrivateProxyGenerateKinds.All) ? PrivateProxyGenerateKinds.Field | PrivateProxyGenerateKinds.Method | PrivateProxyGenerateKinds.Property | PrivateProxyGenerateKinds.Instance | PrivateProxyGenerateKinds.Static | PrivateProxyGenerateKinds.Constructor : kind;
129 |
130 | var generateField = kind.HasFlag(PrivateProxyGenerateKinds.Field);
131 | var generateProperty = kind.HasFlag(PrivateProxyGenerateKinds.Property);
132 | var generateMethod = kind.HasFlag(PrivateProxyGenerateKinds.Method);
133 | var generateInstance = kind.HasFlag(PrivateProxyGenerateKinds.Instance);
134 | var generateStatic = kind.HasFlag(PrivateProxyGenerateKinds.Static);
135 | var generateConstructor = kind.HasFlag(PrivateProxyGenerateKinds.Constructor);
136 |
137 | // If only set Static or Instance, generate all member kind
138 | if (!generateField && !generateProperty && !generateMethod && !generateConstructor)
139 | {
140 | generateField = generateProperty = generateMethod = true;
141 | }
142 | // If only set member kind, generate both static and instance
143 | if (!generateStatic && !generateInstance)
144 | {
145 | generateStatic = generateInstance = true;
146 | }
147 |
148 | foreach (var item in members)
149 | {
150 | if (!item.CanBeReferencedByName && item.Name != ".ctor") continue;
151 |
152 | if (item.IsStatic && !generateStatic) continue;
153 | if (!item.IsStatic && !generateInstance) continue;
154 |
155 | // add field/property/method
156 | if (generateField && item is IFieldSymbol f)
157 | {
158 | // public member don't generate
159 | if (f.DeclaredAccessibility == Accessibility.Public) continue;
160 |
161 | // return type can not access, don't generate
162 | if (!CanExpose(f.Type)) continue;
163 |
164 | list.Add(new(item));
165 | }
166 | else if (generateProperty && item is IPropertySymbol p)
167 | {
168 | if (p.DeclaredAccessibility == Accessibility.Public)
169 | {
170 | var getPublic = true;
171 | var setPublic = true;
172 | if (p.GetMethod != null)
173 | {
174 | getPublic = p.GetMethod.DeclaredAccessibility == Accessibility.Public;
175 | }
176 | if (p.SetMethod != null)
177 | {
178 | setPublic = p.SetMethod.DeclaredAccessibility == Accessibility.Public;
179 | }
180 |
181 | if (getPublic && setPublic) continue;
182 | }
183 |
184 | if (!CanExpose(p.Type)) continue;
185 |
186 | list.Add(new(item));
187 | }
188 | else if ((generateMethod || generateConstructor) && item is IMethodSymbol m)
189 | {
190 | if (m.DeclaredAccessibility == Accessibility.Public) continue;
191 |
192 | // both return type and parameter type must be accessible
193 | if (!CanExpose(m.ReturnType)) goto Next;
194 | foreach (var parameter in m.Parameters)
195 | {
196 | if (!CanExpose(parameter.Type)) goto Next;
197 | }
198 |
199 | if (m.Name == ".ctor")
200 | {
201 | if (!generateConstructor) continue;
202 | }
203 | else
204 | {
205 | if (!generateMethod) continue;
206 | }
207 |
208 | list.Add(new(item));
209 | Next:
210 | ;
211 | }
212 | }
213 | return list.ToArray();
214 | }
215 |
216 | static bool Verify(SourceProductionContext context, TypeDeclarationSyntax typeSyntax, INamedTypeSymbol proxyType, INamedTypeSymbol targetType)
217 | {
218 | // Type Rule
219 | // ProxyClass: class -> allows class or struct
220 | // : struct -> allows ref struct
221 |
222 | var hasError = false;
223 |
224 | // require partial
225 | if (!typeSyntax.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword)))
226 | {
227 | context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.MustBePartial, typeSyntax.Identifier.GetLocation(), proxyType.Name));
228 | hasError = true;
229 | }
230 |
231 | // not allow readonly struct
232 | if (proxyType.IsValueType && proxyType.IsReadOnly)
233 | {
234 | context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.NotAllowReadOnly, typeSyntax.Identifier.GetLocation(), proxyType.Name));
235 | hasError = true;
236 | }
237 |
238 | // class, not allow ref struct
239 | if (targetType.IsReferenceType && proxyType.IsRefLikeType)
240 | {
241 | context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.ClassNotAllowRefStruct, typeSyntax.Identifier.GetLocation(), proxyType.Name));
242 | hasError = true;
243 | }
244 |
245 | // struct, not allow class or struct(only allows ref struct)
246 | if (targetType.IsValueType)
247 | {
248 | if (proxyType.IsReferenceType)
249 | {
250 | context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.StructNotAllowClass, typeSyntax.Identifier.GetLocation(), proxyType.Name));
251 | hasError = true;
252 | }
253 | else if (proxyType.IsValueType && !proxyType.IsRefLikeType)
254 | {
255 | context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.StructNotAllowStruct, typeSyntax.Identifier.GetLocation(), proxyType.Name));
256 | hasError = true;
257 | }
258 | }
259 |
260 | // target type not allow `ref struct`
261 | if (targetType.IsValueType && targetType.IsRefLikeType)
262 | {
263 | context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.RefStructNotSupported, typeSyntax.Identifier.GetLocation()));
264 | hasError = true;
265 | }
266 |
267 | // generics is not supported
268 | if (targetType.IsGenericType)
269 | {
270 | context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.GenericsNotSupported, typeSyntax.Identifier.GetLocation()));
271 | hasError = true;
272 | }
273 |
274 | // static class is not supported
275 | if (targetType.IsStatic)
276 | {
277 | context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.StaticNotSupported, typeSyntax.Identifier.GetLocation()));
278 | hasError = true;
279 | }
280 |
281 | return !hasError;
282 | }
283 |
284 | static string EmitCode(ITypeSymbol proxyType, INamedTypeSymbol targetType, MetaMember[] members)
285 | {
286 | var targetTypeFullName = targetType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
287 | var code = new StringBuilder();
288 |
289 | var accessibility = proxyType.DeclaredAccessibility.ToCode();
290 | var structOrClass = proxyType.IsReferenceType ? "class" : "struct";
291 | var refStruct = proxyType.IsRefLikeType ? "ref " : "";
292 |
293 | var hasStatic = members.Any(x => x.IsStatic);
294 |
295 | code.AppendLine($$"""
296 | {{refStruct}}partial {{structOrClass}} {{proxyType.Name}}
297 | {
298 | {{If(hasStatic, $$"""
299 | static {{targetTypeFullName}} ____static_instance = default!;
300 | """)}}
301 | {{refStruct}}{{targetTypeFullName}} target;
302 |
303 | public {{proxyType.Name}}({{refStruct}}{{targetTypeFullName}} target)
304 | {
305 | this.target = {{refStruct}}target;
306 | }
307 |
308 | """);
309 |
310 | foreach (var item in members)
311 | {
312 | var readonlyCode = item.IsRequireReadOnly ? "readonly " : "";
313 | var refReturn = item.IsRefReturn ? "ref " : "";
314 |
315 | var staticCode = item.IsStatic ? "Static" : "";
316 | var staticCode2 = item.IsStatic ? "static " : "";
317 | var targetInstance = item.IsStatic ? $"{refStruct}____static_instance" : $"{refStruct}this.target";
318 | switch (item.MemberKind)
319 | {
320 | case MemberKind.Field:
321 | code.AppendLine($$"""
322 | [UnsafeAccessor(UnsafeAccessorKind.{{staticCode}}Field, Name = "{{item.Name}}")]
323 | static extern ref {{readonlyCode}}{{item.MemberTypeFullName}} __{{item.Name}}__({{refStruct}}{{targetTypeFullName}} target);
324 |
325 | public {{staticCode2}}ref {{readonlyCode}}{{item.MemberTypeFullName}} {{item.Name}} => ref __{{item.Name}}__({{targetInstance}});
326 | """);
327 | break;
328 | case MemberKind.Property:
329 |
330 | if (item.HasGetMethod)
331 | {
332 | code.AppendLine($$"""
333 | [UnsafeAccessor(UnsafeAccessorKind.{{staticCode}}Method, Name = "get_{{item.Name}}")]
334 | static extern {{refReturn}}{{item.MemberTypeFullName}} __get_{{item.Name}}__({{refStruct}}{{targetTypeFullName}} target);
335 |
336 | """);
337 | }
338 |
339 | if (item.HasSetMethod)
340 | {
341 | code.AppendLine($$"""
342 | [UnsafeAccessor(UnsafeAccessorKind.{{staticCode}}Method, Name = "set_{{item.Name}}")]
343 | static extern void __set_{{item.Name}}__({{refStruct}}{{targetTypeFullName}} target, {{item.MemberTypeFullName}} value);
344 |
345 | """);
346 | }
347 |
348 | code.AppendLine($$"""
349 | public {{staticCode2}}{{refReturn}}{{readonlyCode}}{{item.MemberTypeFullName}} {{item.Name}}
350 | {
351 | """);
352 | if (item.HasGetMethod)
353 | {
354 | code.AppendLine($" get => {refReturn}__get_{item.Name}__({targetInstance});");
355 | }
356 | if (item.HasSetMethod)
357 | {
358 | code.AppendLine($" set => __set_{item.Name}__({targetInstance}, value);");
359 | }
360 |
361 | code.AppendLine(" }"); // close property
362 | break;
363 | case MemberKind.Constructor:
364 | case MemberKind.Method:
365 | var parameters = string.Join(", ", item.MethodParameters.Select(x => $"{x.RefKind.ToParameterPrefix()}{x.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} {x.Name}"));
366 | var parametersWithComma = (parameters != "") ? ", " + parameters : "";
367 | var useParameters = string.Join(", ", item.MethodParameters.Select(x => $"{x.RefKind.ToUseParameterPrefix()}{x.Name}"));
368 | if (useParameters != "") useParameters = ", " + useParameters;
369 |
370 | if (item.MemberKind == MemberKind.Constructor)
371 | {
372 | code.AppendLine($$"""
373 | [UnsafeAccessor(UnsafeAccessorKind.Constructor)]
374 | public static extern {{targetTypeFullName}} Create{{targetType.Name}}FromConstructor({{parameters}});
375 |
376 | """);
377 | }
378 | else
379 | {
380 | code.AppendLine($$"""
381 | [UnsafeAccessor(UnsafeAccessorKind.{{staticCode}}Method, Name = "{{item.Name}}")]
382 | static extern {{refReturn}}{{item.MemberTypeFullName}} __{{item.Name}}__({{refStruct}}{{targetTypeFullName}} target{{parametersWithComma}});
383 |
384 | public {{staticCode2}}{{refReturn}}{{readonlyCode}}{{item.MemberTypeFullName}} {{item.Name}}({{parameters}}) => {{refReturn}}__{{item.Name}}__({{targetInstance}}{{useParameters}});
385 |
386 | """);
387 | }
388 | break;
389 | default:
390 | break;
391 | }
392 | }
393 |
394 | code.AppendLine("}"); // close Proxy partial
395 |
396 | code.AppendLine($$"""
397 |
398 | {{accessibility}} static class {{targetType.Name}}PrivateProxyExtensions
399 | {
400 | public static {{proxyType.ToFullyQualifiedFormatString()}} AsPrivateProxy(this {{refStruct}}{{targetTypeFullName}} target)
401 | {
402 | return new {{proxyType.ToFullyQualifiedFormatString()}}({{refStruct}}target);
403 | }
404 | }
405 | """);
406 |
407 | return code.ToString();
408 | }
409 |
410 | static void AddSource(SourceProductionContext context, ISymbol targetSymbol, string code, string fileExtension = ".g.cs")
411 | {
412 | var fullType = targetSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)
413 | .Replace("global::", "")
414 | .Replace("<", "_")
415 | .Replace(">", "_");
416 |
417 | var sb = new StringBuilder();
418 |
419 | sb.AppendLine("""
420 | //
421 | #nullable enable
422 | #pragma warning disable CS0108
423 | #pragma warning disable CS0162
424 | #pragma warning disable CS0164
425 | #pragma warning disable CS0219
426 | #pragma warning disable CS8600
427 | #pragma warning disable CS8601
428 | #pragma warning disable CS8602
429 | #pragma warning disable CS8604
430 | #pragma warning disable CS8619
431 | #pragma warning disable CS8620
432 | #pragma warning disable CS8631
433 | #pragma warning disable CS8765
434 | #pragma warning disable CS9074
435 | #pragma warning disable CA1050
436 |
437 | using System;
438 | using System.Runtime.CompilerServices;
439 | using System.Runtime.InteropServices;
440 | """);
441 |
442 | var ns = targetSymbol.ContainingNamespace;
443 | if (!ns.IsGlobalNamespace)
444 | {
445 | sb.AppendLine($"namespace {ns} {{");
446 | }
447 | sb.AppendLine();
448 |
449 | sb.AppendLine(code);
450 |
451 | if (!ns.IsGlobalNamespace)
452 | {
453 | sb.AppendLine($"}}");
454 | }
455 |
456 | var sourceCode = sb.ToString();
457 | context.AddSource($"{fullType}{fileExtension}", sourceCode);
458 | }
459 |
460 | static bool CanExpose(ITypeSymbol typeSymbol)
461 | {
462 | if (typeSymbol is IPointerTypeSymbol) return false;
463 |
464 | if (typeSymbol is IArrayTypeSymbol arrayType)
465 | {
466 | var elementType = arrayType.ElementType;
467 | if (!CanExpose(elementType)) return false;
468 |
469 | while (elementType is IArrayTypeSymbol nestedArrayType)
470 | {
471 | if (!CanExpose(elementType)) return false;
472 | elementType = nestedArrayType.ElementType;
473 | }
474 | }
475 |
476 | if (typeSymbol is INamedTypeSymbol { IsGenericType: true } genericType)
477 | {
478 | foreach (var t in genericType.TypeArguments)
479 | {
480 | if (!CanExpose(t)) return false;
481 | }
482 | }
483 | return typeSymbol.DeclaredAccessibility == Accessibility.Public ||
484 | (typeSymbol.DeclaredAccessibility == Accessibility.NotApplicable && typeSymbol is IArrayTypeSymbol);
485 | }
486 | }
487 |
--------------------------------------------------------------------------------
/src/PrivateProxy.Generator/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "Profile 1": {
4 | "commandName": "DebugRoslynComponent",
5 | "targetProject": "../../sandbox/ConsoleAppNet8/ConsoleAppNet8.csproj"
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/tests/PrivateProxy.Tests/ChangeKindTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Text.RegularExpressions;
7 | using System.Threading.Tasks;
8 |
9 | namespace PrivateProxy.Tests
10 | {
11 | public class KindTarget
12 | {
13 | private int field;
14 | private int Property { get; set; }
15 | int Method() => 3;
16 |
17 | static private int staticField;
18 | static private int StaticProperty { get; set; }
19 | static private int StaticMethod() => 3;
20 | }
21 |
22 | [GeneratePrivateProxy(typeof(KindTarget), PrivateProxyGenerateKinds.Property | PrivateProxyGenerateKinds.Static)]
23 | public partial struct KindTargetProxy;
24 |
25 |
26 | public class ChangeKindTest
27 | {
28 | string GenerateCode(PrivateProxyGenerateKinds generateKinds)
29 | {
30 | var kind = string.Join(" | ", generateKinds.ToString().Split(',').Select(x => "PrivateProxyGenerateKinds." + x.Trim()));
31 |
32 | var codes = CSharpGeneratorRunner.RunGeneratorCode($$"""
33 | using PrivateProxy;
34 |
35 | public class KindTarget
36 | {
37 | private int field;
38 | private int Property { get; set; }
39 | int Method() => 3;
40 |
41 | static private int staticField;
42 | static private int StaticProperty { get; set; }
43 | static private int StaticMethod() => 3;
44 |
45 | private KindTarget(int x) { }
46 | }
47 |
48 | [GeneratePrivateProxy(typeof(KindTarget), {{kind}})]
49 | public partial struct KindTargetProxy { }
50 | """);
51 |
52 | var code = codes.First(x => x.FilePath.Contains("g.cs")).Code;
53 | return code;
54 | }
55 |
56 | [Fact]
57 | public void All()
58 | {
59 | var all = GenerateCode(PrivateProxyGenerateKinds.All);
60 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(9);
61 | }
62 |
63 | [Fact]
64 | public void StaticOnly()
65 | {
66 | var all = GenerateCode(PrivateProxyGenerateKinds.Static);
67 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(4);
68 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(4);
69 | }
70 |
71 | [Fact]
72 | public void InstanceOnly()
73 | {
74 | var all = GenerateCode(PrivateProxyGenerateKinds.Instance);
75 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(4);
76 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(4);
77 | }
78 |
79 | [Fact]
80 | public void MethodOnly()
81 | {
82 | var all = GenerateCode(PrivateProxyGenerateKinds.Method);
83 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(2);
84 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(1);
85 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(1);
86 |
87 | all = GenerateCode(PrivateProxyGenerateKinds.Static | PrivateProxyGenerateKinds.Method);
88 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(1);
89 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(1);
90 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(0);
91 |
92 | all = GenerateCode(PrivateProxyGenerateKinds.Instance | PrivateProxyGenerateKinds.Method);
93 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(1);
94 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(0);
95 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(1);
96 | }
97 |
98 | [Fact]
99 | public void PropertyOnly()
100 | {
101 | var all = GenerateCode(PrivateProxyGenerateKinds.Property);
102 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(4);
103 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(2);
104 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(2);
105 |
106 | all = GenerateCode(PrivateProxyGenerateKinds.Static | PrivateProxyGenerateKinds.Property);
107 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(2);
108 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(2);
109 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(0);
110 |
111 | all = GenerateCode(PrivateProxyGenerateKinds.Instance | PrivateProxyGenerateKinds.Property);
112 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(2);
113 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(0);
114 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(2);
115 | }
116 |
117 | [Fact]
118 | public void FieldOnly()
119 | {
120 | var all = GenerateCode(PrivateProxyGenerateKinds.Field);
121 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(2);
122 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(1);
123 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(1);
124 |
125 | all = GenerateCode(PrivateProxyGenerateKinds.Static | PrivateProxyGenerateKinds.Field);
126 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(1);
127 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(1);
128 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(0);
129 |
130 | all = GenerateCode(PrivateProxyGenerateKinds.Instance | PrivateProxyGenerateKinds.Field);
131 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(1);
132 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(0);
133 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(1);
134 | }
135 |
136 | [Fact]
137 | public void ConstructorOnly()
138 | {
139 | var all = GenerateCode(PrivateProxyGenerateKinds.Constructor);
140 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(1);
141 | }
142 |
143 | [Fact]
144 | public void Mix()
145 | {
146 | var all = GenerateCode(PrivateProxyGenerateKinds.Field | PrivateProxyGenerateKinds.Method);
147 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(4);
148 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(2);
149 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(2);
150 |
151 | all = GenerateCode(PrivateProxyGenerateKinds.Static| PrivateProxyGenerateKinds.Instance | PrivateProxyGenerateKinds.Field | PrivateProxyGenerateKinds.Method);
152 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(4);
153 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(2);
154 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(2);
155 |
156 | all = GenerateCode(PrivateProxyGenerateKinds.Static | PrivateProxyGenerateKinds.Field | PrivateProxyGenerateKinds.Method);
157 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(2);
158 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(2);
159 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(0);
160 |
161 | all = GenerateCode(PrivateProxyGenerateKinds.Instance | PrivateProxyGenerateKinds.Field | PrivateProxyGenerateKinds.Method);
162 | Regex.Count(all, @"\[UnsafeAccessor.+\]").Should().Be(2);
163 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.Static.+\]").Should().Be(0);
164 | Regex.Count(all, @"\[UnsafeAccessor\(UnsafeAccessorKind.(?!Static).+\]").Should().Be(2);
165 | }
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/tests/PrivateProxy.Tests/ClassGenerateTest.cs:
--------------------------------------------------------------------------------
1 | #pragma warning disable CS0649
2 | #pragma warning disable CS0169
3 | #pragma warning disable CS0414
4 |
5 | using Microsoft.CodeAnalysis;
6 | using System.Runtime.CompilerServices;
7 |
8 | namespace PrivateProxy.Tests;
9 |
10 | public class PrivateClassTarget
11 | {
12 | public PrivateClassTarget(int readOnlyField)
13 | {
14 | this.readOnlyField = readOnlyField;
15 | this.GetOnlyProperty = readOnlyField;
16 | this._refReadOnlyGetOnlyPropertyField = readOnlyField;
17 | }
18 |
19 | // public(no generate)
20 | public int publicField;
21 | public int PublicProperty { get; set; }
22 | public void PublicMethod() { }
23 |
24 | // field
25 | private int field;
26 | private readonly int readOnlyField;
27 | // public required int requiredField;
28 |
29 | // property
30 | private int Property { get; set; }
31 | private int GetOnlyProperty { get; }
32 | public int GetOnlyPrivateProperty { private get; set; }
33 | public int SetOnlyPrivateProperty { get; private set; }
34 | public required int RequiredProperty { get; set; }
35 | public int InitProperty { get; init; }
36 | public required int RequiredInitProperty { get; init; }
37 |
38 | int _setOnlyPropertyField;
39 | private int SetOnlyProperty { set => _setOnlyPropertyField = value; }
40 |
41 | int _refGetOnlyPropertyField;
42 | private ref int RefGetOnlyProperty => ref _refGetOnlyPropertyField;
43 |
44 | int _refReadOnlyGetOnlyPropertyField;
45 | private ref readonly int RefReadOnlyGetOnlyProperty => ref _refReadOnlyGetOnlyPropertyField;
46 |
47 | // method
48 | public int voidMethodCalledCount;
49 |
50 | private void VoidMethod()
51 | {
52 | voidMethodCalledCount++;
53 | }
54 |
55 | private int ReturnMethod() => 10;
56 |
57 | private int ParameterMethod1(int x) => x;
58 |
59 | private int ParameterMethod2(int x, int y) => x + y;
60 |
61 | private void VoidParameter(int x, int y)
62 | {
63 | voidMethodCalledCount = x + y;
64 | }
65 |
66 | private void RefOutInMethod(in int x, ref int y, out int z, ref readonly int xyz)
67 | {
68 | y = 40;
69 | z = 9999;
70 | voidMethodCalledCount = x + xyz;
71 | }
72 |
73 | int _refReturnMethodField;
74 | private ref int RefReturnMethod() => ref _refReturnMethodField;
75 |
76 | int _refReadOnlyReturnMethodField;
77 | private ref readonly int RefReadOnlyReturnMethod() => ref _refReadOnlyReturnMethodField;
78 |
79 | int _refReturnMethodParameterField;
80 | private ref int RefReturnMethodParameter(ref int x)
81 | {
82 | return ref x;
83 | }
84 |
85 | // checker
86 | public (int field, int readOnlyField) GetFields() => (field, readOnlyField);
87 | public (int property, int getOnlyProperty, int getOnlyPrivate, int setOnlyPrivate, int setOnlyProperty) GetProperties() => (Property, GetOnlyProperty, GetOnlyPrivateProperty, SetOnlyPrivateProperty, _setOnlyPropertyField);
88 |
89 | public (int refGetOnlyPropertyField, int _refReadOnlyGetOnlyPropertyField) GetRefProperties() => (_refGetOnlyPropertyField, _refReadOnlyGetOnlyPropertyField);
90 |
91 |
92 | // static
93 |
94 | static int staticField;
95 | static readonly int staticReadOnlyField = 4444;
96 |
97 |
98 | static int StaticProperty { get; set; }
99 | static int StaticGetOnlyProperty { get; } = 5000;
100 | public static int StaticGetOnlyPrivateProperty { private get; set; }
101 | public static int StaticSetOnlyPrivateProperty { get; private set; }
102 |
103 | static int _StaticsetOnlyPropertyField;
104 | private static int StaticSetOnlyProperty { set => _StaticsetOnlyPropertyField = value; }
105 |
106 | static int _StaticrefGetOnlyPropertyField;
107 | private static ref int StaticRefGetOnlyProperty => ref _StaticrefGetOnlyPropertyField;
108 |
109 | static int _StaticrefReadOnlyGetOnlyPropertyField;
110 | private static ref readonly int StaticRefReadOnlyGetOnlyProperty => ref _StaticrefReadOnlyGetOnlyPropertyField;
111 |
112 |
113 | public static int staticvoidMethodCalledCount;
114 |
115 | private static void StaticVoidMethod()
116 | {
117 | staticvoidMethodCalledCount++;
118 | }
119 |
120 | static int StaticReturnMethod() => 10;
121 |
122 | static int StaticParameterMethod1(int x) => x;
123 |
124 | static int StaticParameterMethod2(int x, int y) => x + y;
125 |
126 | static void StaticVoidParameter(int x, int y)
127 | {
128 | staticvoidMethodCalledCount = x + y;
129 | }
130 |
131 | static void StaticRefOutInMethod(in int x, ref int y, out int z, ref readonly int xyz)
132 | {
133 | y = 40;
134 | z = 9999;
135 | staticvoidMethodCalledCount = x + xyz;
136 | }
137 |
138 | static int _StaticrefReturnMethodField;
139 | static ref int StaticRefReturnMethod() => ref _StaticrefReturnMethodField;
140 |
141 | static int _StaticrefReadOnlyReturnMethodField;
142 | static ref readonly int StaticRefReadOnlyReturnMethod() => ref _StaticrefReadOnlyReturnMethodField;
143 |
144 | static ref int StaticRefReturnMethodParameter(ref int x)
145 | {
146 | return ref x;
147 | }
148 | }
149 |
150 |
151 | #if true
152 | [GeneratePrivateProxy(typeof(PrivateClassTarget))]
153 | public partial struct PrivateClassTargetStructProxy;
154 |
155 | #else
156 | [GeneratePrivateProxy(typeof(PrivateClassTarget))]
157 | public partial class PrivateClassTargetStructProxy; // ClassProxy but use same name...
158 | #endif
159 |
160 | public class ClassGenerateTest
161 | {
162 | [Fact]
163 | public void Field()
164 | {
165 | var target = new PrivateClassTarget(5000) { RequiredInitProperty = 0, RequiredProperty = 0 };
166 | var proxy = target.AsPrivateProxy();
167 |
168 | proxy.field = 1000;
169 |
170 | var (field, readOnlyField) = target.GetFields();
171 |
172 | field.Should().Be(1000).And.Be(proxy.field);
173 | readOnlyField.Should().Be(5000).And.Be(proxy.readOnlyField);
174 | }
175 |
176 | [Fact]
177 | public void Property()
178 | {
179 | var target = new PrivateClassTarget(5000) { RequiredInitProperty = 0, RequiredProperty = 0 };
180 | var proxy = target.AsPrivateProxy();
181 |
182 | proxy.Property = 9999;
183 | proxy.GetOnlyPrivateProperty = 8888;
184 | proxy.SetOnlyPrivateProperty = 7777;
185 |
186 | proxy.SetOnlyProperty = 6666;
187 |
188 | ref var refGet = ref proxy.RefGetOnlyProperty;
189 | ref readonly var refRead = ref proxy.RefReadOnlyGetOnlyProperty;
190 |
191 | refGet = 5555;
192 | // refRead = 100; can't write
193 |
194 | var (prop, getOnly, getOnlyPrivate, setOnlyPrivate, setOnly) = target.GetProperties();
195 |
196 | prop.Should().Be(9999).And.Be(proxy.Property);
197 | getOnlyPrivate.Should().Be(8888).And.Be(proxy.GetOnlyPrivateProperty);
198 | setOnlyPrivate.Should().Be(7777).And.Be(proxy.SetOnlyPrivateProperty);
199 | getOnly.Should().Be(5000).And.Be(proxy.GetOnlyProperty);
200 |
201 | setOnly.Should().Be(6666);
202 |
203 | var (refReadOnly, refReadOnly2) = target.GetRefProperties();
204 |
205 | proxy.RefGetOnlyProperty.Should().Be(5555);
206 |
207 |
208 | refGet.Should().Be(5555).And.Be(refReadOnly);
209 | refReadOnly2.Should().Be(5000).And.Be(refRead);
210 | }
211 |
212 | [Fact]
213 | public void Method()
214 | {
215 | var target = new PrivateClassTarget(5000) { RequiredInitProperty = 0, RequiredProperty = 0 };
216 | var proxy = target.AsPrivateProxy();
217 |
218 | proxy.VoidMethod();
219 | proxy.VoidMethod();
220 | proxy.VoidMethod();
221 |
222 | target.voidMethodCalledCount.Should().Be(3);
223 |
224 |
225 | proxy.ReturnMethod().Should().Be(10);
226 | proxy.ParameterMethod1(9999).Should().Be(9999);
227 |
228 | proxy.ParameterMethod2(10, 20).Should().Be(30);
229 |
230 | proxy.VoidParameter(10, 20);
231 | target.voidMethodCalledCount.Should().Be(30);
232 |
233 | var y = 0;
234 | var zz = 20;
235 | proxy.RefOutInMethod(10, ref y, out var z, in zz);
236 | y.Should().Be(40);
237 | z.Should().Be(9999);
238 | target.voidMethodCalledCount.Should().Be(30); // x + xyz
239 |
240 | proxy.RefReturnMethod() = 1111;
241 | proxy._refReturnMethodField.Should().Be(1111);
242 |
243 | proxy._refReadOnlyReturnMethodField = 99999;
244 | proxy.RefReadOnlyReturnMethod().Should().Be(99999);
245 |
246 | var zzzzz = 999;
247 | ref var xxxxx = ref proxy.RefReturnMethodParameter(ref zzzzz);
248 | xxxxx.Should().Be(999);
249 | xxxxx = 111;
250 | zzzzz.Should().Be(111);
251 | }
252 |
253 | [Fact]
254 | public void StaticField()
255 | {
256 | PrivateClassTargetStructProxy.staticField = 9999;
257 | PrivateClassTargetStructProxy.staticField.Should().Be(9999);
258 | PrivateClassTargetStructProxy.staticReadOnlyField.Should().Be(4444);
259 | }
260 |
261 | [Fact]
262 | public void StaticProperty()
263 | {
264 | PrivateClassTargetStructProxy.StaticProperty = 9999;
265 | PrivateClassTargetStructProxy.StaticGetOnlyPrivateProperty = 8888;
266 | PrivateClassTargetStructProxy.StaticSetOnlyPrivateProperty = 7777;
267 |
268 | PrivateClassTargetStructProxy.StaticSetOnlyProperty = 6666;
269 |
270 | ref var refGet = ref PrivateClassTargetStructProxy.StaticRefGetOnlyProperty;
271 | ref readonly var refRead = ref PrivateClassTargetStructProxy.StaticRefReadOnlyGetOnlyProperty;
272 |
273 | refGet = 5555;
274 |
275 | PrivateClassTargetStructProxy._StaticrefReadOnlyGetOnlyPropertyField = 5000;
276 |
277 | PrivateClassTargetStructProxy.StaticProperty.Should().Be(9999);
278 | PrivateClassTargetStructProxy.StaticGetOnlyPrivateProperty.Should().Be(8888);
279 | PrivateClassTargetStructProxy.StaticSetOnlyPrivateProperty.Should().Be(7777);
280 | PrivateClassTargetStructProxy.StaticGetOnlyProperty.Should().Be(5000);
281 |
282 | PrivateClassTargetStructProxy._StaticsetOnlyPropertyField.Should().Be(6666);
283 |
284 | PrivateClassTargetStructProxy.StaticRefGetOnlyProperty.Should().Be(5555);
285 |
286 | PrivateClassTargetStructProxy._StaticrefGetOnlyPropertyField.Should().Be(5555);
287 | PrivateClassTargetStructProxy._StaticrefReadOnlyGetOnlyPropertyField.Should().Be(5000).And.Be(refRead);
288 | }
289 |
290 | [Fact]
291 | public void StaticMethod()
292 | {
293 | PrivateClassTargetStructProxy.StaticVoidMethod();
294 | PrivateClassTargetStructProxy.StaticVoidMethod();
295 | PrivateClassTargetStructProxy.StaticVoidMethod();
296 |
297 | PrivateClassTarget.staticvoidMethodCalledCount.Should().Be(3);
298 |
299 |
300 | PrivateClassTargetStructProxy.StaticReturnMethod().Should().Be(10);
301 | PrivateClassTargetStructProxy.StaticParameterMethod1(9999).Should().Be(9999);
302 |
303 | PrivateClassTargetStructProxy.StaticParameterMethod2(10, 20).Should().Be(30);
304 |
305 | PrivateClassTargetStructProxy.StaticVoidParameter(10, 20);
306 | PrivateClassTarget.staticvoidMethodCalledCount.Should().Be(30);
307 |
308 | var y = 0;
309 | var zz = 20;
310 | PrivateClassTargetStructProxy.StaticRefOutInMethod(10, ref y, out var z, in zz);
311 | y.Should().Be(40);
312 | z.Should().Be(9999);
313 | PrivateClassTarget.staticvoidMethodCalledCount.Should().Be(30); // x + xyz
314 |
315 | PrivateClassTargetStructProxy.StaticRefReturnMethod() = 1111;
316 | PrivateClassTargetStructProxy._StaticrefReturnMethodField.Should().Be(1111);
317 |
318 | PrivateClassTargetStructProxy._StaticrefReadOnlyReturnMethodField = 99999;
319 | PrivateClassTargetStructProxy.StaticRefReadOnlyReturnMethod().Should().Be(99999);
320 |
321 | var zzzzz = 999;
322 | ref var xxxxx = ref PrivateClassTargetStructProxy.StaticRefReturnMethodParameter(ref zzzzz);
323 | xxxxx.Should().Be(999);
324 | xxxxx = 111;
325 | zzzzz.Should().Be(111);
326 | }
327 | }
--------------------------------------------------------------------------------
/tests/PrivateProxy.Tests/DiagnosticsTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace PrivateProxy.Tests;
8 |
9 | public class DiagnosticsTest
10 | {
11 | void Compile(int id, string code, bool allowMultipleError = false)
12 | {
13 | var diagnostics = CSharpGeneratorRunner.RunGenerator(code);
14 | if (!allowMultipleError)
15 | {
16 | diagnostics.Length.Should().Be(1);
17 | diagnostics[0].Id.Should().Be("PP" + id.ToString("000"));
18 | }
19 | else
20 | {
21 | diagnostics.Select(x => x.Id).Should().Contain("PP" + id.ToString("000"));
22 | }
23 | }
24 |
25 | [Fact]
26 | public void PP001_MustBePartial()
27 | {
28 | Compile(1, """
29 | using PrivateProxy;
30 |
31 | public struct Foo
32 | {
33 | int X;
34 | }
35 |
36 | [GeneratePrivateProxy(typeof(Foo))]
37 | public ref struct FooPrivateProxy { }
38 | """);
39 | }
40 |
41 | [Fact]
42 | public void PP002_NotAllowReadOnly()
43 | {
44 | Compile(2, """
45 | using PrivateProxy;
46 |
47 | public struct Foo
48 | {
49 | int X;
50 | }
51 |
52 | [GeneratePrivateProxy(typeof(Foo))]
53 | public readonly ref partial struct FooPrivateProxy { }
54 | """);
55 | }
56 |
57 | [Fact]
58 | public void PP003_ClassNotAllowRefStruct()
59 | {
60 | Compile(3, """
61 | using PrivateProxy;
62 |
63 | public class Foo
64 | {
65 | int X;
66 | }
67 |
68 | [GeneratePrivateProxy(typeof(Foo))]
69 | public ref partial struct FooPrivateProxy { }
70 | """);
71 | }
72 |
73 | [Fact]
74 | public void PP004_StructNotAllowClass()
75 | {
76 | Compile(4, """
77 | using PrivateProxy;
78 |
79 | public struct Foo
80 | {
81 | int X;
82 | }
83 |
84 | [GeneratePrivateProxy(typeof(Foo))]
85 | public partial class FooPrivateProxy { }
86 | """);
87 | }
88 |
89 | [Fact]
90 | public void PP005_StructNotAllowStruct()
91 | {
92 | Compile(5, """
93 | using PrivateProxy;
94 |
95 | public struct Foo
96 | {
97 | int X;
98 | }
99 |
100 | [GeneratePrivateProxy(typeof(Foo))]
101 | public partial struct FooPrivateProxy { }
102 | """);
103 | }
104 |
105 | [Fact]
106 | public void PP006_RefStructNotSupported()
107 | {
108 | Compile(6, """
109 | using PrivateProxy;
110 |
111 | public ref struct Foo
112 | {
113 | int X;
114 | }
115 |
116 | [GeneratePrivateProxy(typeof(Foo))]
117 | public ref partial struct FooPrivateProxy { }
118 | """);
119 | }
120 |
121 | [Fact]
122 | public void PP007_GenericsNotSupported()
123 | {
124 | Compile(7, """
125 | using PrivateProxy;
126 |
127 | public class Foo
128 | {
129 | T X;
130 | }
131 |
132 | [GeneratePrivateProxy(typeof(Foo))]
133 | public partial class FooPrivateProxy { }
134 | """);
135 |
136 | Compile(7, """
137 | using PrivateProxy;
138 |
139 | public class Foo
140 | {
141 | T X;
142 | }
143 |
144 | [GeneratePrivateProxy(typeof(Foo<>))]
145 | public partial class FooPrivateProxy { }
146 | """);
147 | }
148 |
149 | [Fact]
150 | public void PP008_StaticNotSupported()
151 | {
152 | Compile(8, """
153 | using PrivateProxy;
154 |
155 | public static class Foo
156 | {
157 | static int x;
158 | }
159 |
160 | [GeneratePrivateProxy(typeof(Foo))]
161 | public partial class FooPrivateProxy { }
162 | """);
163 | }
164 | }
--------------------------------------------------------------------------------
/tests/PrivateProxy.Tests/GenerateInternalTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace PrivateProxy.Tests;
8 |
9 |
10 | public class InternalTarget
11 | {
12 | InnerClass innerField = new();
13 |
14 | private void Hoge()
15 | {
16 | }
17 |
18 | private InnerClass GetInnerClass() => new InnerClass();
19 |
20 | private InternalClass GetInternalClass => new();
21 | private InternalClass[] GetInternalArray() => [];
22 | private InternalClass[][] GetInternalNestedArray() => [];
23 | private Dictionary GetInternalGeneric => [];
24 |
25 | class InnerClass;
26 | }
27 |
28 | internal class InternalClass;
29 |
30 | [GeneratePrivateProxy(typeof(InternalTarget))]
31 | public partial struct InnerTargetProxy;
32 |
33 | public class GenerateInternalTest
34 |
35 | {
36 | }
37 |
--------------------------------------------------------------------------------
/tests/PrivateProxy.Tests/GlobalUsings.cs:
--------------------------------------------------------------------------------
1 | global using Xunit;
2 | global using Xunit.Abstractions;
3 | global using FluentAssertions;
--------------------------------------------------------------------------------
/tests/PrivateProxy.Tests/OtherTypesTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace PrivateProxy.Tests;
8 |
9 |
10 |
11 | // TODO: test inheritance
12 | // TODO: check explicit interface member
13 | // TODO: inheritance class uses new?
14 | // TODO: target record, record struct
15 |
16 | public class BaseType
17 | {
18 | int _baseInt;
19 | }
20 |
21 | public class InheritType : BaseType
22 | {
23 | int _inheritInt;
24 | }
25 |
26 | [GeneratePrivateProxy(typeof(InheritType))]
27 | public partial class InheritProxy;
28 |
29 | public interface TestInterface
30 | {
31 | int Foo { get; set; }
32 | }
33 |
34 | public class ExplitImpl : TestInterface
35 | {
36 | int a;
37 | int TestInterface.Foo { get => a; set => a = value; }
38 | }
39 |
40 | [GeneratePrivateProxy(typeof(ExplitImpl))]
41 | public partial class ExplitImplProxy;
42 |
43 | public record TestRecord(int x, int y)
44 | {
45 | int z;
46 | }
47 |
48 | public record struct TestRecordStruct(int x, int y)
49 | {
50 | int z;
51 | }
52 |
53 | [GeneratePrivateProxy(typeof(TestRecord))]
54 | public partial class TestRecordProxy;
55 |
56 | [GeneratePrivateProxy(typeof(TestRecordStruct))]
57 | public ref partial struct TestRecordStructPrxy;
58 |
59 | public class OtherTypesTest
60 | {
61 | public void Foo()
62 | {
63 | // new InheritType().AsPrivateProxy()._base
64 | // new ExplitImpl().AsPrivateProxy().Foo
65 |
66 | //new TestRecord().AsPrivateProxy().z
67 | // new TestRecordStruct(1, 2).AsPrivateProxy().z;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/tests/PrivateProxy.Tests/PrivateProxy.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 | false
8 | true
9 |
10 |
11 |
12 | $(NoWarn);CS1591;CS8604;CS8321;CS0414;CS0169;CS0649
13 |
14 |
15 |
16 | $(NoWarn);CS1591;CS8604;CS8321;CS0414;CS0169;CS0649
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | runtime; build; native; contentfiles; analyzers; buildtransitive
26 | all
27 |
28 |
29 |
30 |
31 |
32 |
33 | Analyzer
34 | false
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/tests/PrivateProxy.Tests/RefStructGenerateTest.cs:
--------------------------------------------------------------------------------
1 | namespace PrivateProxy.Tests;
2 |
3 | public class RefStructGenerateTest
4 | {
5 | // [Fact]
6 | // public void CheckRefStructField()
7 | // {
8 | // var diagnostics = CSharpGeneratorRunner.RunGenerator("""
9 | //using PrivateProxy;
10 |
11 | //public ref struct RefStructTest
12 | //{
13 | // public int field; // RefKind=None
14 | // public readonly int readOnlyField; // RefKind=None, IsReadOnly=true
15 | // public ref int refField; // RefKind=Ref
16 | // public ref readonly int refReadOnly; // RefKind=In
17 | // public readonly ref int readonlyRef; // RefKind=Ref, IsReadOnly=true
18 | // public readonly ref readonly int readOnlyRefReadOnly;
19 | //}
20 |
21 | //[GeneratePrivateProxy(typeof(RefStructTest))]
22 | //public partial struct Proxy { }
23 | //""");
24 |
25 | // diagnostics.Should().BeEmpty();
26 | // }
27 |
28 | // [Fact]
29 | // public void CheckRefStructProperty()
30 | // {
31 | // var diagnostics = CSharpGeneratorRunner.RunGenerator("""
32 | //using PrivateProxy;
33 |
34 | //public ref struct RefStructTest
35 | //{
36 | // public ref int refField;
37 |
38 | // public int Prop0 => 1;
39 | // public readonly int Prop1 => 1;
40 | // public ref int Prop2 => ref refField; // ReturnsByRef:true
41 | // public ref readonly int Prop3 => ref refField; // ReturnsByRefReadOnly:true | can't assign
42 | // public readonly ref int Prop4 => ref refField; // ReturnsByRef:true
43 | // public readonly ref readonly int Prop5 => ref refField;// ReturnsByRefReadOnly:true | can't assign
44 | //}
45 |
46 | //[GeneratePrivateProxy(typeof(RefStructTest))]
47 | //public partial struct Proxy { }
48 | //""");
49 |
50 | // diagnostics.Should().BeEmpty();
51 | // }
52 |
53 | // [Fact]
54 | // public void CheckRefStructMethod()
55 | // {
56 | // var diagnostics = CSharpGeneratorRunner.RunGenerator("""
57 | //using PrivateProxy;
58 |
59 | //public ref struct RefStructTest
60 | //{
61 | // public ref int refField;
62 |
63 | // public int Method0() => 1;
64 | // public readonly int Method1() => 1;
65 | // public ref int Method2() => ref refField;
66 | // public ref readonly int Method3() => ref refField;
67 | // public readonly ref int Method4() => ref refField;
68 | // public readonly ref readonly int Method5() => ref refField;
69 | //}
70 |
71 | //[GeneratePrivateProxy(typeof(RefStructTest))]
72 | //public partial struct Proxy { }
73 | //""");
74 |
75 | // diagnostics.Should().BeEmpty();
76 | // }
77 |
78 | // [Fact]
79 | // public void CheckRefKind()
80 | // {
81 | // var diagnostics = CSharpGeneratorRunner.RunGenerator("""
82 | //using PrivateProxy;
83 |
84 | //public class ClassTest
85 | //{
86 | // private void RefOutInMethod(ref readonly int xyz) { }
87 | //}
88 |
89 | //[GeneratePrivateProxy(typeof(ClassTest))]
90 | //public partial struct Proxy { }
91 | //""");
92 |
93 | // diagnostics.Should().BeEmpty();
94 | // }
95 | }
96 |
--------------------------------------------------------------------------------
/tests/PrivateProxy.Tests/StructGenerateTest.cs:
--------------------------------------------------------------------------------
1 | #pragma warning disable CS0649
2 | #pragma warning disable CS0169
3 | #pragma warning disable CS0414
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 |
11 | namespace PrivateProxy.Tests;
12 |
13 | public struct PrivateStructTarget
14 | {
15 | public PrivateStructTarget(int readOnlyField)
16 | {
17 | this.readOnlyField = readOnlyField;
18 | this.GetOnlyProperty = readOnlyField;
19 | // this._refReadOnlyGetOnlyPropertyField = readOnlyField;
20 | }
21 |
22 | // public(no generate)
23 | public int publicField;
24 | public int PublicProperty { get; set; }
25 | public void PublicMethod() { }
26 |
27 | // field
28 | private int field;
29 | private readonly int readOnlyField;
30 | // public required int requiredField;
31 |
32 | // property
33 | private int Property { get; set; }
34 | private int GetOnlyProperty { get; }
35 | public int GetOnlyPrivateProperty { private get; set; }
36 | public int SetOnlyPrivateProperty { get; private set; }
37 | public required int RequiredProperty { get; set; }
38 | public int InitProperty { get; init; }
39 | public required int RequiredInitProperty { get; init; }
40 |
41 | int _setOnlyPropertyField;
42 | private int SetOnlyProperty { set => _setOnlyPropertyField = value; }
43 |
44 | public static int _refGetOnlyPropertyField;
45 | private ref int RefGetOnlyProperty => ref _refGetOnlyPropertyField;
46 |
47 | public static int _refReadOnlyGetOnlyPropertyField;
48 | private ref readonly int RefReadOnlyGetOnlyProperty => ref _refReadOnlyGetOnlyPropertyField;
49 |
50 | // method
51 | public int voidMethodCalledCount;
52 |
53 | private void VoidMethod()
54 | {
55 | voidMethodCalledCount++;
56 | }
57 |
58 | private int ReturnMethod() => 10;
59 |
60 | private int ParameterMethod1(int x) => x;
61 |
62 | private int ParameterMethod2(int x, int y) => x + y;
63 |
64 | private void VoidParameter(int x, int y)
65 | {
66 | voidMethodCalledCount = x + y;
67 | }
68 |
69 | private void RefOutInMethod(in int x, ref int y, out int z, ref readonly int xyz)
70 | {
71 | y = 40;
72 | z = 9999;
73 | voidMethodCalledCount = x + xyz;
74 | }
75 |
76 | public static int _refReturnMethodField;
77 | ref int RefReturnMethod() => ref _refReturnMethodField;
78 |
79 | public static int _refReadOnlyReturnMethodField;
80 | private ref readonly int RefReadOnlyReturnMethod() => ref _refReadOnlyReturnMethodField;
81 |
82 | int _refReturnMethodParameterField;
83 | private ref int RefReturnMethodParameter(ref int x)
84 | {
85 | return ref x;
86 | }
87 |
88 | // checker
89 | public (int field, int readOnlyField) GetFields() => (field, readOnlyField);
90 | public (int property, int getOnlyProperty, int getOnlyPrivate, int setOnlyPrivate, int setOnlyProperty) GetProperties() => (Property, GetOnlyProperty, GetOnlyPrivateProperty, SetOnlyPrivateProperty, _setOnlyPropertyField);
91 |
92 | public (int refGetOnlyPropertyField, int _refReadOnlyGetOnlyPropertyField) GetRefProperties() => (_refGetOnlyPropertyField, _refReadOnlyGetOnlyPropertyField);
93 |
94 |
95 | // static
96 |
97 | static int staticField;
98 | static readonly int staticReadOnlyField = 4444;
99 |
100 |
101 | static int StaticProperty { get; set; }
102 | static int StaticGetOnlyProperty { get; } = 5000;
103 | public static int StaticGetOnlyPrivateProperty { private get; set; }
104 | public static int StaticSetOnlyPrivateProperty { get; private set; }
105 |
106 | static int _StaticsetOnlyPropertyField;
107 | private static int StaticSetOnlyProperty { set => _StaticsetOnlyPropertyField = value; }
108 |
109 | static int _StaticrefGetOnlyPropertyField;
110 | private static ref int StaticRefGetOnlyProperty => ref _StaticrefGetOnlyPropertyField;
111 |
112 | static int _StaticrefReadOnlyGetOnlyPropertyField;
113 | private static ref readonly int StaticRefReadOnlyGetOnlyProperty => ref _StaticrefReadOnlyGetOnlyPropertyField;
114 |
115 |
116 | public static int staticvoidMethodCalledCount;
117 |
118 | private static void StaticVoidMethod()
119 | {
120 | staticvoidMethodCalledCount += 1;
121 | }
122 |
123 | static int StaticReturnMethod() => 10;
124 |
125 | static int StaticParameterMethod1(int x) => x;
126 |
127 | static int StaticParameterMethod2(int x, int y) => x + y;
128 |
129 | static void StaticVoidParameter(int x, int y)
130 | {
131 | staticvoidMethodCalledCount = x + y;
132 | }
133 |
134 | static void StaticRefOutInMethod(in int x, ref int y, out int z, ref readonly int xyz)
135 | {
136 | y = 40;
137 | z = 9999;
138 | staticvoidMethodCalledCount = x + xyz;
139 | }
140 |
141 | static int _StaticrefReturnMethodField;
142 | static ref int StaticRefReturnMethod() => ref _StaticrefReturnMethodField;
143 |
144 | static int _StaticrefReadOnlyReturnMethodField;
145 | static ref readonly int StaticRefReadOnlyReturnMethod() => ref _StaticrefReadOnlyReturnMethodField;
146 |
147 | static ref int StaticRefReturnMethodParameter(ref int x)
148 | {
149 | return ref x;
150 | }
151 |
152 |
153 | }
154 |
155 | [GeneratePrivateProxy(typeof(PrivateStructTarget))]
156 | public ref partial struct PrivateStructTargetStructProxy;
157 |
158 |
159 |
160 | public class StructGenerateTest
161 | {
162 | [Fact]
163 | public void Field()
164 | {
165 | var target = new PrivateStructTarget(5000) { RequiredInitProperty = 0, RequiredProperty = 0 };
166 |
167 | var proxy = target.AsPrivateProxy();
168 |
169 | proxy.field = 1000;
170 |
171 | var (field, readOnlyField) = target.GetFields();
172 |
173 | field.Should().Be(1000).And.Be(proxy.field);
174 | readOnlyField.Should().Be(5000).And.Be(proxy.readOnlyField);
175 | }
176 |
177 | [Fact]
178 | public void Property()
179 | {
180 | var target = new PrivateStructTarget(5000) { RequiredInitProperty = 0, RequiredProperty = 0 };
181 | PrivateStructTarget._refReadOnlyGetOnlyPropertyField = 5000;
182 | var proxy = target.AsPrivateProxy();
183 |
184 | proxy.Property = 9999;
185 | proxy.GetOnlyPrivateProperty = 8888;
186 | proxy.SetOnlyPrivateProperty = 7777;
187 |
188 | proxy.SetOnlyProperty = 6666;
189 |
190 | ref var refGet = ref proxy.RefGetOnlyProperty;
191 | ref readonly var refRead = ref proxy.RefReadOnlyGetOnlyProperty;
192 |
193 | refGet = 5555;
194 | // refRead = 100; can't write
195 |
196 | var (prop, getOnly, getOnlyPrivate, setOnlyPrivate, setOnly) = target.GetProperties();
197 |
198 | prop.Should().Be(9999).And.Be(proxy.Property);
199 | getOnlyPrivate.Should().Be(8888).And.Be(proxy.GetOnlyPrivateProperty);
200 | setOnlyPrivate.Should().Be(7777).And.Be(proxy.SetOnlyPrivateProperty);
201 | getOnly.Should().Be(5000).And.Be(proxy.GetOnlyProperty);
202 |
203 | setOnly.Should().Be(6666);
204 |
205 | var (refReadOnly, refReadOnly2) = target.GetRefProperties();
206 |
207 | proxy.RefGetOnlyProperty.Should().Be(5555);
208 |
209 |
210 | refGet.Should().Be(5555).And.Be(refReadOnly);
211 | refReadOnly2.Should().Be(5000).And.Be(refRead);
212 | }
213 |
214 | [Fact]
215 | public void Method()
216 | {
217 | var target = new PrivateStructTarget(5000) { RequiredInitProperty = 0, RequiredProperty = 0 };
218 | var proxy = target.AsPrivateProxy();
219 |
220 | proxy.VoidMethod();
221 | proxy.VoidMethod();
222 | proxy.VoidMethod();
223 |
224 | target.voidMethodCalledCount.Should().Be(3);
225 |
226 |
227 | proxy.ReturnMethod().Should().Be(10);
228 | proxy.ParameterMethod1(9999).Should().Be(9999);
229 |
230 | proxy.ParameterMethod2(10, 20).Should().Be(30);
231 |
232 | proxy.VoidParameter(10, 20);
233 | target.voidMethodCalledCount.Should().Be(30);
234 |
235 | var y = 0;
236 | var zz = 20;
237 | proxy.RefOutInMethod(10, ref y, out var z, in zz);
238 | y.Should().Be(40);
239 | z.Should().Be(9999);
240 | target.voidMethodCalledCount.Should().Be(30); // x + xyz
241 |
242 | proxy.RefReturnMethod() = 1111;
243 | PrivateStructTarget._refReturnMethodField.Should().Be(1111);
244 |
245 | PrivateStructTarget._refReadOnlyReturnMethodField = 99999;
246 | proxy.RefReadOnlyReturnMethod().Should().Be(99999);
247 |
248 | var zzzzz = 999;
249 | ref var xxxxx = ref proxy.RefReturnMethodParameter(ref zzzzz);
250 | xxxxx.Should().Be(999);
251 | xxxxx = 111;
252 | zzzzz.Should().Be(111);
253 | }
254 |
255 | [Fact]
256 | public void StaticField()
257 | {
258 | PrivateStructTargetStructProxy.staticField = 9999;
259 | PrivateStructTargetStructProxy.staticField.Should().Be(9999);
260 | PrivateStructTargetStructProxy.staticReadOnlyField.Should().Be(4444);
261 | }
262 |
263 | [Fact]
264 | public void StaticProperty()
265 | {
266 | PrivateStructTargetStructProxy.StaticProperty = 9999;
267 | PrivateStructTargetStructProxy.StaticGetOnlyPrivateProperty = 8888;
268 | PrivateStructTargetStructProxy.StaticSetOnlyPrivateProperty = 7777;
269 |
270 | PrivateStructTargetStructProxy.StaticSetOnlyProperty = 6666;
271 |
272 | ref var refGet = ref PrivateStructTargetStructProxy.StaticRefGetOnlyProperty;
273 | ref readonly var refRead = ref PrivateStructTargetStructProxy.StaticRefReadOnlyGetOnlyProperty;
274 |
275 | refGet = 5555;
276 |
277 | PrivateStructTargetStructProxy._StaticrefReadOnlyGetOnlyPropertyField = 5000;
278 |
279 | PrivateStructTargetStructProxy.StaticProperty.Should().Be(9999);
280 | PrivateStructTargetStructProxy.StaticGetOnlyPrivateProperty.Should().Be(8888);
281 | PrivateStructTargetStructProxy.StaticSetOnlyPrivateProperty.Should().Be(7777);
282 | PrivateStructTargetStructProxy.StaticGetOnlyProperty.Should().Be(5000);
283 |
284 | PrivateStructTargetStructProxy._StaticsetOnlyPropertyField.Should().Be(6666);
285 |
286 | PrivateStructTargetStructProxy.StaticRefGetOnlyProperty.Should().Be(5555);
287 |
288 | PrivateStructTargetStructProxy._StaticrefGetOnlyPropertyField.Should().Be(5555);
289 | PrivateStructTargetStructProxy._StaticrefReadOnlyGetOnlyPropertyField.Should().Be(5000).And.Be(refRead);
290 | }
291 |
292 | [Fact]
293 | public void StaticMethod()
294 | {
295 | PrivateStructTargetStructProxy.StaticVoidMethod();
296 | PrivateStructTargetStructProxy.StaticVoidMethod();
297 | PrivateStructTargetStructProxy.StaticVoidMethod();
298 |
299 | PrivateStructTarget.staticvoidMethodCalledCount.Should().Be(3);
300 |
301 |
302 | PrivateStructTargetStructProxy.StaticReturnMethod().Should().Be(10);
303 | PrivateStructTargetStructProxy.StaticParameterMethod1(9999).Should().Be(9999);
304 |
305 | PrivateStructTargetStructProxy.StaticParameterMethod2(10, 20).Should().Be(30);
306 |
307 | PrivateStructTargetStructProxy.StaticVoidParameter(10, 20);
308 | PrivateStructTarget.staticvoidMethodCalledCount.Should().Be(30);
309 |
310 | var y = 0;
311 | var zz = 20;
312 | PrivateStructTargetStructProxy.StaticRefOutInMethod(10, ref y, out var z, in zz);
313 | y.Should().Be(40);
314 | z.Should().Be(9999);
315 | PrivateStructTarget.staticvoidMethodCalledCount.Should().Be(30); // x + xyz
316 |
317 | PrivateStructTargetStructProxy.StaticRefReturnMethod() = 1111;
318 | PrivateStructTargetStructProxy._StaticrefReturnMethodField.Should().Be(1111);
319 |
320 | PrivateStructTargetStructProxy._StaticrefReadOnlyReturnMethodField = 99999;
321 | PrivateStructTargetStructProxy.StaticRefReadOnlyReturnMethod().Should().Be(99999);
322 |
323 | var zzzzz = 999;
324 | ref var xxxxx = ref PrivateStructTargetStructProxy.StaticRefReturnMethodParameter(ref zzzzz);
325 | xxxxx.Should().Be(999);
326 | xxxxx = 111;
327 | zzzzz.Should().Be(111);
328 | }
329 | }
--------------------------------------------------------------------------------
/tests/PrivateProxy.Tests/Utils/CsharpGeneratorRunner.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using Microsoft.CodeAnalysis.CSharp;
3 | using Microsoft.CodeAnalysis.Diagnostics;
4 | using PrivateProxy.Generator;
5 | using System.Runtime.CompilerServices;
6 |
7 | namespace PrivateProxy.Tests;
8 |
9 | public static class CSharpGeneratorRunner
10 | {
11 | static Compilation baseCompilation = default!;
12 |
13 | [ModuleInitializer]
14 | public static void InitializeCompilation()
15 | {
16 | // running .NET Core system assemblies dir path
17 | var baseAssemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location)!;
18 | var systemAssemblies = Directory.GetFiles(baseAssemblyPath)
19 | .Where(x =>
20 | {
21 | var fileName = Path.GetFileName(x);
22 | if (fileName.EndsWith("Native.dll")) return false;
23 | return fileName.StartsWith("System") || (fileName is "mscorlib.dll" or "netstandard.dll");
24 | });
25 |
26 | var references = systemAssemblies
27 | .Select(x => MetadataReference.CreateFromFile(x))
28 | .ToArray();
29 |
30 | var compilation = CSharpCompilation.Create("generatortest",
31 | references: references,
32 | options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
33 |
34 | baseCompilation = compilation;
35 | }
36 |
37 | public static Diagnostic[] RunGenerator(string source, string[]? preprocessorSymbols = null, AnalyzerConfigOptionsProvider? options = null)
38 | {
39 | if (preprocessorSymbols == null)
40 | {
41 | preprocessorSymbols = new[] { "NET8_0_OR_GREATER" };
42 | }
43 | var parseOptions = new CSharpParseOptions(LanguageVersion.CSharp11, preprocessorSymbols: preprocessorSymbols);
44 |
45 | var driver = CSharpGeneratorDriver.Create(new PrivateProxyGenerator()).WithUpdatedParseOptions(parseOptions);
46 | if (options != null)
47 | {
48 | driver = (Microsoft.CodeAnalysis.CSharp.CSharpGeneratorDriver)driver.WithUpdatedAnalyzerConfigOptions(options);
49 | }
50 |
51 | var compilation = baseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(source, parseOptions));
52 |
53 | driver.RunGeneratorsAndUpdateCompilation(compilation, out var newCompilation, out var diagnostics);
54 |
55 | // combine diagnostics as result.(ignore warning)
56 | var compilationDiagnostics = newCompilation.GetDiagnostics();
57 | return diagnostics.Concat(compilationDiagnostics).Where(x => x.Severity == DiagnosticSeverity.Error).ToArray();
58 | }
59 |
60 | public static (string FilePath, string Code)[] RunGeneratorCode(string source, string[]? preprocessorSymbols = null, AnalyzerConfigOptionsProvider? options = null)
61 | {
62 | if (preprocessorSymbols == null)
63 | {
64 | preprocessorSymbols = new[] { "NET8_0_OR_GREATER" };
65 | }
66 | var parseOptions = new CSharpParseOptions(LanguageVersion.CSharp11, preprocessorSymbols: preprocessorSymbols);
67 |
68 | var driver = CSharpGeneratorDriver.Create(new PrivateProxyGenerator()).WithUpdatedParseOptions(parseOptions);
69 | if (options != null)
70 | {
71 | driver = (Microsoft.CodeAnalysis.CSharp.CSharpGeneratorDriver)driver.WithUpdatedAnalyzerConfigOptions(options);
72 | }
73 |
74 | var compilation = baseCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(source, parseOptions));
75 |
76 | driver.RunGeneratorsAndUpdateCompilation(compilation, out var newCompilation, out var diagnostics);
77 |
78 | return newCompilation.SyntaxTrees.Select(x => (x.FilePath, x.ToString())).ToArray();
79 | }
80 | }
--------------------------------------------------------------------------------