diff --git a/src/ReactiveUI.SourceGenerator.Tests/REACTIVECMD/ReactiveCMDGeneratorTests.FromReactiveCommandWithNullableTypeAndNullableReturnType#ReactiveUI.SourceGenerators.ReactiveCommandAttribute.g.verified.cs b/src/ReactiveUI.SourceGenerator.Tests/REACTIVECMD/ReactiveCMDGeneratorTests.FromReactiveCommandWithNullableTypeAndNullableReturnType#ReactiveUI.SourceGenerators.ReactiveCommandAttribute.g.verified.cs
new file mode 100644
index 0000000..1462b40
--- /dev/null
+++ b/src/ReactiveUI.SourceGenerator.Tests/REACTIVECMD/ReactiveCMDGeneratorTests.FromReactiveCommandWithNullableTypeAndNullableReturnType#ReactiveUI.SourceGenerators.ReactiveCommandAttribute.g.verified.cs
@@ -0,0 +1,44 @@
+//HintName: ReactiveUI.SourceGenerators.ReactiveCommandAttribute.g.cs
+// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved.
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+//
+#pragma warning disable
+#nullable enable
+namespace ReactiveUI.SourceGenerators;
+
+///
+/// ReativeCommandAttribute.
+///
+///
+[global::System.AttributeUsage(global::System.AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
+internal sealed class ReactiveCommandAttribute : global::System.Attribute
+{
+ ///
+ /// Gets the can execute method or property.
+ ///
+ ///
+ /// The name of the CanExecute Observable of bool.
+ ///
+ public string? CanExecute { get; init; }
+
+ ///
+ /// Gets the output scheduler.
+ ///
+ ///
+ /// The output scheduler.
+ ///
+ public string? OutputScheduler { get; init; }
+
+ ///
+ /// Gets the AccessModifier of the ReactiveCommand property.
+ ///
+ ///
+ /// The AccessModifier of the property.
+ ///
+ public PropertyAccessModifier AccessModifier { get; init; }
+}
+#nullable restore
+#pragma warning restore
\ No newline at end of file
diff --git a/src/ReactiveUI.SourceGenerator.Tests/UnitTests/ReactiveCMDGeneratorTests.cs b/src/ReactiveUI.SourceGenerator.Tests/UnitTests/ReactiveCMDGeneratorTests.cs
index 6ab4cc6..4b7493c 100644
--- a/src/ReactiveUI.SourceGenerator.Tests/UnitTests/ReactiveCMDGeneratorTests.cs
+++ b/src/ReactiveUI.SourceGenerator.Tests/UnitTests/ReactiveCMDGeneratorTests.cs
@@ -238,4 +238,31 @@ public partial class TestVM : ReactiveObject
""";
return TestHelper.TestPass(sourceCode);
}
+
+ ///
+ /// Froms the type of the reactive command with nullable type and nullable return.
+ ///
+ /// A task to monitor the async.
+ [Test]
+ public Task FromReactiveCommandWithNullableTypeAndNullableReturnType()
+ {
+ const string sourceCode = """
+ using System;
+ using ReactiveUI;
+ using ReactiveUI.SourceGenerators;
+ namespace TestNs;
+
+ public class NullableInput
+ {
+ public string? Name { get; set; }
+ }
+
+ public partial class TestVM : ReactiveObject
+ {
+ [ReactiveCommand]
+ private NullableInput? Test1(NullableInput? input) => input;
+ }
+ """;
+ return TestHelper.TestPass(sourceCode);
+ }
}
diff --git a/src/ReactiveUI.SourceGenerators.Execute.Nested3/Class1.cs b/src/ReactiveUI.SourceGenerators.Execute.Nested3/Class1.cs
index 9f7f0e4..3e8179b 100644
--- a/src/ReactiveUI.SourceGenerators.Execute.Nested3/Class1.cs
+++ b/src/ReactiveUI.SourceGenerators.Execute.Nested3/Class1.cs
@@ -6,6 +6,7 @@
using System.Diagnostics.CodeAnalysis;
using ReactiveUI;
using ReactiveUI.SourceGenerators;
+using SGReactiveUI.SourceGenerators.Execute.Nested2;
namespace SGReactiveUI.SourceGenerators.Execute.Nested3;
@@ -17,4 +18,23 @@ public partial class Class1 : ReactiveObject
{
[Reactive]
private string? _property1;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public Class1()
+ {
+ SetPropertyCommand.Execute(new Nested1.Class1 { Property1 = "Initial Value" }).Subscribe();
+ }
+
+ [ReactiveCommand]
+ private SGReactiveUI.SourceGenerators.Execute.Nested2.Class1? SetProperty(Nested1.Class1? class1)
+ {
+ if (class1 == null)
+ {
+ return null;
+ }
+
+ return new() { Property1 = class1.Property1 };
+ }
}
diff --git a/src/ReactiveUI.SourceGenerators.Execute/ReactiveUI.SourceGenerators.Execute.csproj b/src/ReactiveUI.SourceGenerators.Execute/ReactiveUI.SourceGenerators.Execute.csproj
index bd130b1..8c5b259 100644
--- a/src/ReactiveUI.SourceGenerators.Execute/ReactiveUI.SourceGenerators.Execute.csproj
+++ b/src/ReactiveUI.SourceGenerators.Execute/ReactiveUI.SourceGenerators.Execute.csproj
@@ -20,6 +20,7 @@
+
diff --git a/src/ReactiveUI.SourceGenerators.Execute/TestViewModel.cs b/src/ReactiveUI.SourceGenerators.Execute/TestViewModel.cs
index 1e26857..e0f51a7 100644
--- a/src/ReactiveUI.SourceGenerators.Execute/TestViewModel.cs
+++ b/src/ReactiveUI.SourceGenerators.Execute/TestViewModel.cs
@@ -16,6 +16,7 @@
using DynamicData;
using ReactiveUI;
using ReactiveUI.SourceGenerators;
+using SGReactiveUI.SourceGenerators.Execute.Nested3;
namespace SGReactiveUI.SourceGenerators.Test;
@@ -452,4 +453,15 @@ protected virtual void Dispose(bool disposing)
[ReactiveCommand]
private Task GetData(CancellationToken ct) =>
Task.FromResult(Array.Empty());
+
+ [ReactiveCommand]
+ private Execute.Nested2.Class1? SetProperty(Execute.Nested1.Class1? class1)
+ {
+ if (class1 == null)
+ {
+ return null;
+ }
+
+ return new() { Property1 = class1.Property1 };
+ }
}
diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveCommand/ReactiveCommandGenerator.Execute.cs b/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveCommand/ReactiveCommandGenerator.Execute.cs
index dd3718b..6fef1b7 100644
--- a/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveCommand/ReactiveCommandGenerator.Execute.cs
+++ b/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveCommand/ReactiveCommandGenerator.Execute.cs
@@ -3,7 +3,7 @@
// The ReactiveUI and contributors licenses this file to you under the MIT license.
// See the LICENSE file in the project root for full license information.
-using System.Collections.Generic;
+using System;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
@@ -15,7 +15,6 @@
using ReactiveUI.SourceGenerators.Helpers;
using ReactiveUI.SourceGenerators.Input.Models;
using ReactiveUI.SourceGenerators.Models;
-using static ReactiveUI.SourceGenerators.Diagnostics.DiagnosticDescriptors;
namespace ReactiveUI.SourceGenerators;
@@ -120,11 +119,16 @@ public partial class ReactiveCommandGenerator
token.ThrowIfCancellationRequested();
+ var argumentType = methodParameters.ToImmutable().SingleOrDefault()?.Type;
+ var argumentTypeString = argumentType?.GetFullyQualifiedNameWithNullabilityAnnotations();
+
+ token.ThrowIfCancellationRequested();
+
return new(
targetInfo,
symbol.Name,
realReturnType.GetFullyQualifiedNameWithNullabilityAnnotations(),
- methodParameters.ToImmutable().SingleOrDefault()?.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
+ argumentTypeString,
isTask,
isReturnTypeVoid,
isObservable,
@@ -138,7 +142,7 @@ public partial class ReactiveCommandGenerator
private static string GenerateSource(string containingTypeName, string containingNamespace, string containingClassVisibility, string containingType, CommandInfo[] commands)
{
// Get Parent class details from properties.ParentInfo
- var (parentClassDeclarationsString, closingBrackets) = TargetInfo.GenerateParentClassDeclarations(commands.Select(p => p.TargetInfo.ParentInfo).ToArray());
+ var (parentClassDeclarationsString, closingBrackets) = TargetInfo.GenerateParentClassDeclarations([.. commands.Select(p => p.TargetInfo.ParentInfo)]);
var classes = GenerateClassWithCommands(containingTypeName, containingNamespace, containingClassVisibility, containingType, commands);
diff --git a/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveCommand/ReactiveCommandGenerator.cs b/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveCommand/ReactiveCommandGenerator.cs
index 86e5acb..e40aef2 100644
--- a/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveCommand/ReactiveCommandGenerator.cs
+++ b/src/ReactiveUI.SourceGenerators.Roslyn/ReactiveCommand/ReactiveCommandGenerator.cs
@@ -9,7 +9,6 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
-using ReactiveUI.SourceGenerators.Extensions;
using ReactiveUI.SourceGenerators.Helpers;
namespace ReactiveUI.SourceGenerators;