Skip to content

Commit 5a7dce6

Browse files
authored
Update Reactive command for performance (#98)
1 parent 5bee4ed commit 5a7dce6

11 files changed

+475
-507
lines changed

src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperiesWithAttributes#TestVM.Properties.g.verified.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ namespace TestNs
1010
/// <summary>
1111
/// Partial class for the TestVM which contains ReactiveUI Reactive property initialization.
1212
/// </summary>
13-
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.ReactiveGenerator", "1.1.0.0")]
1413
public partial class TestVM
1514
{
15+
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.ReactiveGenerator", "1.1.0.0")]
1616
/// <inheritdoc cref="_test3"/>
1717
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
1818
[System.Text.Json.Serialization.JsonInclude]

src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactiveProperties#TestVM.Properties.g.verified.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ namespace TestNs
1010
/// <summary>
1111
/// Partial class for the TestVM which contains ReactiveUI Reactive property initialization.
1212
/// </summary>
13-
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.ReactiveGenerator", "1.1.0.0")]
1413
public partial class TestVM
1514
{
15+
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.ReactiveGenerator", "1.1.0.0")]
1616
/// <inheritdoc cref="_test1"/>
1717
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
1818
public int Test1

src/ReactiveUI.SourceGenerator.Tests/REACTIVE/ReactiveGeneratorTests.FromReactivePropertiesWithAccess#TestVM.Properties.g.verified.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ namespace TestNs
1010
/// <summary>
1111
/// Partial class for the TestVM which contains ReactiveUI Reactive property initialization.
1212
/// </summary>
13-
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.ReactiveGenerator", "1.1.0.0")]
1413
public partial class TestVM
1514
{
15+
[global::System.CodeDom.Compiler.GeneratedCode("ReactiveUI.SourceGenerators.ReactiveGenerator", "1.1.0.0")]
1616
/// <inheritdoc cref="_test2"/>
1717
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
1818
public int Test2

src/ReactiveUI.SourceGenerator.Tests/TestHelper.cs

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,20 @@ public sealed class TestHelper<T>(ITestOutputHelper testOutput) : IDisposable
4747
new("ReactiveUI", VersionRange.AllStableFloating, LibraryDependencyTarget.Package);
4848
#pragma warning restore CS0618 // Type or member is obsolete
4949

50+
private static readonly string mscorlibPath = Path.Combine(
51+
System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory(),
52+
"mscorlib.dll");
53+
5054
private static readonly MetadataReference[] References =
5155
[
5256
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
5357
MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location),
5458
MetadataReference.CreateFromFile(typeof(T).Assembly.Location),
5559
MetadataReference.CreateFromFile(typeof(TestHelper<T>).Assembly.Location),
5660

61+
// Create mscorlib Reference
62+
MetadataReference.CreateFromFile(mscorlibPath)
63+
5764
// Wpf references
5865
////MetadataReference.CreateFromFile(Assembly.Load("PresentationCore").Location),
5966
////MetadataReference.CreateFromFile(Assembly.Load("PresentationFramework").Location),
@@ -127,19 +134,22 @@ public void TestFail(
127134
/// Tests a generator expecting it to pass successfully.
128135
/// </summary>
129136
/// <param name="source">The source code to test.</param>
137+
/// <param name="withPreDiagnosics">if set to <c>true</c> [with pre diagnosics].</param>
130138
/// <returns>
131139
/// The driver.
132140
/// </returns>
141+
/// <exception cref="InvalidOperationException">Must have valid compiler instance.</exception>
133142
/// <exception cref="ArgumentNullException">callerType.</exception>
134143
public GeneratorDriver TestPass(
135-
string source)
144+
string source,
145+
bool withPreDiagnosics = false)
136146
{
137147
if (_eventCompiler is null)
138148
{
139149
throw new InvalidOperationException("Must have valid compiler instance.");
140150
}
141151

142-
return RunGeneratorAndCheck(source);
152+
return RunGeneratorAndCheck(source, withPreDiagnosics);
143153
}
144154

145155
/// <inheritdoc/>
@@ -149,13 +159,15 @@ public GeneratorDriver TestPass(
149159
/// Runs the specified source generator and validates the generated code.
150160
/// </summary>
151161
/// <param name="code">The code to be parsed and processed by the generator.</param>
162+
/// <param name="withPreDiagnosics">if set to <c>true</c> [with pre diagnosics].</param>
152163
/// <param name="rerunCompilation">Indicates whether to rerun the compilation after running the generator.</param>
153-
/// <returns>The generator driver used to run the generator.</returns>
154-
/// <exception cref="InvalidOperationException">
155-
/// Thrown if the compiler instance is not valid or if the compilation fails.
156-
/// </exception>
164+
/// <returns>
165+
/// The generator driver used to run the generator.
166+
/// </returns>
167+
/// <exception cref="InvalidOperationException">Thrown if the compiler instance is not valid or if the compilation fails.</exception>
157168
public GeneratorDriver RunGeneratorAndCheck(
158169
string code,
170+
bool withPreDiagnosics = false,
159171
bool rerunCompilation = true)
160172
{
161173
if (_eventCompiler is null)
@@ -180,11 +192,14 @@ public GeneratorDriver RunGeneratorAndCheck(
180192
assemblies,
181193
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, deterministic: true));
182194

183-
// Validate diagnostics before running the generator.
184-
////var prediagnostics = compilation.GetDiagnostics()
185-
//// .Where(d => !d.Id.Contains("CS0518") && d.Severity > DiagnosticSeverity.Warning)
186-
//// .ToList();
187-
////prediagnostics.Should().BeEmpty();
195+
if (withPreDiagnosics)
196+
{
197+
// Validate diagnostics before running the generator.
198+
var prediagnostics = compilation.GetDiagnostics()
199+
.Where(d => d.Severity > DiagnosticSeverity.Warning)
200+
.ToList();
201+
prediagnostics.Should().BeEmpty();
202+
}
188203

189204
var generator = new T();
190205
var driver = CSharpGeneratorDriver.Create(generator).WithUpdatedParseOptions((CSharpParseOptions)syntaxTree.Options);

src/ReactiveUI.SourceGenerator.Tests/UnitTests/ReactiveCMDGeneratorTests.cs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,7 @@ namespace TestNs;
3434
public partial class TestVM : ReactiveObject
3535
{
3636
[ReactiveCommand]
37-
private void Test1()
38-
{
39-
var a = 10;
40-
}
37+
private int Test1() => 10;
4138
}
4239
""";
4340

@@ -68,10 +65,8 @@ namespace TestNs;
6865
public partial class TestVM : ReactiveObject
6966
{
7067
[ReactiveCommand]
71-
private void Test3(string baseString)
72-
{
73-
var a = baseString;
74-
}
68+
[property: JsonInclude]
69+
private int Test3(string baseString) => int.Parse(baseString);
7570
}
7671
""";
7772

src/ReactiveUI.SourceGenerators/Core/Extensions/ITypeSymbolExtensions.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,66 @@ public static string GetFullyQualifiedMetadataName(this ITypeSymbol symbol)
234234
return builder.ToString();
235235
}
236236

237+
public static bool IsTaskReturnType(this ITypeSymbol? typeSymbol)
238+
{
239+
var nameFormat = SymbolDisplayFormat.FullyQualifiedFormat;
240+
do
241+
{
242+
var typeName = typeSymbol?.ToDisplayString(nameFormat);
243+
if (typeName == "global::System.Threading.Tasks.Task")
244+
{
245+
return true;
246+
}
247+
248+
typeSymbol = typeSymbol?.BaseType;
249+
}
250+
while (typeSymbol != null);
251+
252+
return false;
253+
}
254+
255+
public static bool IsObservableReturnType(this ITypeSymbol? typeSymbol)
256+
{
257+
var nameFormat = SymbolDisplayFormat.FullyQualifiedFormat;
258+
do
259+
{
260+
var typeName = typeSymbol?.ToDisplayString(nameFormat);
261+
if (typeName?.Contains("global::System.IObservable") == true)
262+
{
263+
return true;
264+
}
265+
266+
typeSymbol = typeSymbol?.BaseType;
267+
}
268+
while (typeSymbol != null);
269+
270+
return false;
271+
}
272+
273+
public static bool IsObservableBoolType(this ITypeSymbol? typeSymbol)
274+
{
275+
var nameFormat = SymbolDisplayFormat.FullyQualifiedFormat;
276+
do
277+
{
278+
var typeName = typeSymbol?.ToDisplayString(nameFormat);
279+
if (typeName?.Contains("global::System.IObservable<bool>") == true)
280+
{
281+
return true;
282+
}
283+
284+
typeSymbol = typeSymbol?.BaseType;
285+
}
286+
while (typeSymbol != null);
287+
288+
return false;
289+
}
290+
291+
public static ITypeSymbol GetTaskReturnType(this ITypeSymbol typeSymbol, Compilation compilation) => typeSymbol switch
292+
{
293+
INamedTypeSymbol { TypeArguments.Length: 1 } namedTypeSymbol => namedTypeSymbol.TypeArguments[0],
294+
_ => compilation.GetSpecialType(SpecialType.System_Void)
295+
};
296+
237297
/// <summary>
238298
/// Appends the fully qualified metadata name for a given symbol to a target builder.
239299
/// </summary>

src/ReactiveUI.SourceGenerators/Core/Helpers/AttributeInfo.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,4 +131,6 @@ public AttributeSyntax GetSyntax()
131131

132132
return Attribute(IdentifierName(TypeName), AttributeArgumentList(SeparatedList(arguments.Concat(namedArguments))));
133133
}
134+
135+
public override string ToString() => $"[{GetSyntax()}]";
134136
}

src/ReactiveUI.SourceGenerators/Reactive/ReactiveGenerator.Execute.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,9 @@ namespace {{containingNamespace}}
147147
{{AddTabs(1)}}/// <summary>
148148
{{AddTabs(1)}}/// Partial class for the {{containingTypeName}} which contains ReactiveUI Reactive property initialization.
149149
{{AddTabs(1)}}/// </summary>
150-
{{AddTabs(1)}}[global::System.CodeDom.Compiler.GeneratedCode("{{GeneratorName}}", "{{GeneratorVersion}}")]
151150
{{AddTabs(1)}}{{containingClassVisibility}} partial {{containingType}} {{containingTypeName}}
152151
{{AddTabs(1)}}{
152+
{{AddTabs(2)}}[global::System.CodeDom.Compiler.GeneratedCode("{{GeneratorName}}", "{{GeneratorVersion}}")]
153153
{{propertyDeclarations}}
154154
{{AddTabs(1)}}}
155155
}

src/ReactiveUI.SourceGenerators/ReactiveCommand/Models/CommandInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ internal record CommandInfo(
2222
bool IsObservable,
2323
string? CanExecuteObservableName,
2424
CanExecuteTypeInfo? CanExecuteTypeInfo,
25-
EquatableArray<AttributeInfo> ForwardedPropertyAttributes)
25+
EquatableArray<string> ForwardedPropertyAttributes)
2626
{
2727
private const string UnitTypeName = "global::System.Reactive.Unit";
2828

0 commit comments

Comments
 (0)