Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 41 additions & 1 deletion AutomaticInterface/AutomaticInterface/Builder.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
Expand Down Expand Up @@ -155,7 +156,7 @@ private static void AddMethod(InterfaceBuilder codeGenerator, IMethodSymbol meth

var paramResult = new HashSet<string>();
method
.Parameters.Select(x => x.ToDisplayString(FullyQualifiedDisplayFormat))
.Parameters.Select(p => GetParameterDisplayString(p, codeGenerator.HasNullable))
.ToList()
.ForEach(x => paramResult.Add(x));

Expand Down Expand Up @@ -226,6 +227,45 @@ private static bool IsNullable(ITypeSymbol typeSymbol)
return false;
}

private static string GetParameterDisplayString(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think that perhaps passing the whole interface builder here is a bit more than necessary? I feel it would be clearer (and less coupled) if the second parameter was a boolean, something like bool forceNullable (a better name than the one I'm suggesting would be welcome!)

EDIT Sorry, I write this hours ago and hadn't noticed I'd accidentally started a review (which I hadn't submitted)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I've changed the parameter to a bool.

IParameterSymbol param,
bool nullableContextEnabled
)
{
var paramParts = param.ToDisplayParts(FullyQualifiedDisplayFormat);
var typeSb = new StringBuilder();
var restSb = new StringBuilder();
var isInsideType = true;
// The part before the first space is the parameter type
foreach (var part in paramParts)
{
if (isInsideType && part.Kind == SymbolDisplayPartKind.Space)
{
isInsideType = false;
}
if (isInsideType)
{
typeSb.Append(part.ToString());
}
else
{
restSb.Append(part.ToString());
}
}
// If this parameter has default value null and we're enabling the nullable context, we need to force the nullable annotation if there isn't one already
if (
param.HasExplicitDefaultValue
&& param.ExplicitDefaultValue is null
&& param.NullableAnnotation != NullableAnnotation.Annotated
&& param.Type.IsReferenceType
&& nullableContextEnabled
)
{
typeSb.Append('?');
}
return typeSb.Append(restSb).ToString();
}

private static void AddEventsToInterface(List<ISymbol> members, InterfaceBuilder codeGenerator)
{
members
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//--------------------------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
// </auto-generated>
//--------------------------------------------------------------------------------------------------

#nullable enable
namespace AutomaticInterfaceExample
{
[global::System.CodeDom.Compiler.GeneratedCode("AutomaticInterface", "")]
public partial interface IDemoClass
{
/// <inheritdoc cref="AutomaticInterfaceExample.DemoClass.TryStartTransaction(int?, int, string)" />
bool TryStartTransaction(int? param, int param2 = 0, string? data = null);

}
}
#nullable restore
23 changes: 23 additions & 0 deletions AutomaticInterface/Tests/Methods/Methods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,29 @@ public bool TryStartTransaction(string data = null)
await Verify(Infrastructure.GenerateCode(code));
}

[Fact]
public async Task WorksWithMixedOptionalNullParameters()
{
const string code = """

using AutomaticInterface;

namespace AutomaticInterfaceExample;

[GenerateAutomaticInterface]
public class DemoClass
{
public bool TryStartTransaction(int? param, int param2 = 0, string data = null)
{
return true;
}
}


""";
await Verify(Infrastructure.GenerateCode(code));
}

[Fact]
public async Task AddsPublicMethodToInterface()
{
Expand Down
Loading