Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

67 method with a default enum parameter wich type is byte does not compile #70

23 changes: 21 additions & 2 deletions AutomaticInterface/AutomaticInterface/Builder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,25 @@ private static string InheritDoc(ISymbol source) =>
$"/// <inheritdoc cref=\"{source.ToDisplayString().Replace('<', '{').Replace('>', '}')}\" />"; // we use inherit doc because that should be able to fetch documentation from base classes.

private static readonly SymbolDisplayFormat FullyQualifiedDisplayFormat =
new(
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
memberOptions: SymbolDisplayMemberOptions.IncludeParameters
| SymbolDisplayMemberOptions.IncludeContainingType,
Copy link
Contributor

Choose a reason for hiding this comment

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

I can't believe this is a member option! 🤦‍♀️ I didn't even consider that a member option could control default parameter rendering!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I found that also quite surprising. I played around until it worked ;).
Not ideal, but nothing of that is documented...

parameterOptions: SymbolDisplayParameterOptions.IncludeType
| SymbolDisplayParameterOptions.IncludeParamsRefOut
| SymbolDisplayParameterOptions.IncludeDefaultValue
| SymbolDisplayParameterOptions.IncludeName,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Included,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes
| SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier
| SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers
);

/// <summary>
/// We do need to be able to group shadowing and new methods/events into a single entry, hence this is missing SymbolDisplayMemberOptions.IncludeContainingType
/// </summary>
private static readonly SymbolDisplayFormat FullyQualifiedDisplayFormatForGrouping =
new(
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
memberOptions: SymbolDisplayMemberOptions.IncludeParameters,
Expand Down Expand Up @@ -91,7 +110,7 @@ private static void AddMethodsToInterface(List<ISymbol> members, InterfaceBuilde
.Where(x => x.MethodKind == MethodKind.Ordinary)
.Where(x => x.ContainingType.Name != nameof(Object))
.Where(x => !HasIgnoreAttribute(x))
.GroupBy(x => x.ToDisplayString(FullyQualifiedDisplayFormat))
.GroupBy(x => x.ToDisplayString(FullyQualifiedDisplayFormatForGrouping))
.Select(g => g.First())
.ToList()
.ForEach(method => AddMethod(codeGenerator, method));
Expand Down Expand Up @@ -182,7 +201,7 @@ private static void AddEventsToInterface(List<ISymbol> members, InterfaceBuilder
members
.Where(x => x.Kind == SymbolKind.Event)
.OfType<IEventSymbol>()
.GroupBy(x => x.ToDisplayString(FullyQualifiedDisplayFormat))
.GroupBy(x => x.ToDisplayString(FullyQualifiedDisplayFormatForGrouping))
.Select(g => g.First())
.ToList()
.ForEach(evt =>
Expand Down
2 changes: 2 additions & 0 deletions AutomaticInterface/AutomaticInterface/InterfaceBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,9 @@ public string Build()

cb.Indent();
foreach (var method in methodInfos)
{
BuildMethod(cb, method);
}

cb.Dedent();

Expand Down
132 changes: 132 additions & 0 deletions AutomaticInterface/Tests/GeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2025,4 +2025,136 @@ public partial interface IDemoClass
""";
GenerateCode(code).Should().Be(expected);
}

[Fact]
public void WorksWithByteEnums()
{
const string code = """

using AutomaticInterface;
using System;

namespace AutomaticInterfaceExample;

public enum EnumWithByteType : byte { A = 1, B = 2, C = 3 };

[GenerateAutomaticInterface]
public class DemoClass
{
public void MethodWithDefaultParameter(EnumWithByteType a = EnumWithByteType.B) { }
}

""";

const string expected = """
//--------------------------------------------------------------------------------------------------
// <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>
//--------------------------------------------------------------------------------------------------

namespace AutomaticInterfaceExample
{
[global::System.CodeDom.Compiler.GeneratedCode("AutomaticInterface", "")]
public partial interface IDemoClass
{
/// <inheritdoc cref="AutomaticInterfaceExample.DemoClass.MethodWithDefaultParameter(AutomaticInterfaceExample.EnumWithByteType)" />
void MethodWithDefaultParameter(global::AutomaticInterfaceExample.EnumWithByteType a = global::AutomaticInterfaceExample.EnumWithByteType.B);

}
}

""";
GenerateCode(code).Should().Be(expected);
}

[Fact]
public void WorksWithByteEnumsAsReturnType()
{
const string code = """

using AutomaticInterface;
using System;

namespace AutomaticInterfaceExample;

public enum EnumWithByteType : byte { A = 1, B = 2, C = 3 };

[GenerateAutomaticInterface]
public class DemoClass
{
public EnumWithByteType MethodWithDefaultParameter(EnumWithByteType a = EnumWithByteType.B) { return a; }
}

""";

const string expected = """
//--------------------------------------------------------------------------------------------------
// <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>
//--------------------------------------------------------------------------------------------------

namespace AutomaticInterfaceExample
{
[global::System.CodeDom.Compiler.GeneratedCode("AutomaticInterface", "")]
public partial interface IDemoClass
{
/// <inheritdoc cref="AutomaticInterfaceExample.DemoClass.MethodWithDefaultParameter(AutomaticInterfaceExample.EnumWithByteType)" />
global::AutomaticInterfaceExample.EnumWithByteType MethodWithDefaultParameter(global::AutomaticInterfaceExample.EnumWithByteType a = global::AutomaticInterfaceExample.EnumWithByteType.B);

}
}

""";
GenerateCode(code).Should().Be(expected);
}

[Fact]
public void WorksWithByteEnumsProperties()
{
const string code = """

using AutomaticInterface;
using System;

namespace AutomaticInterfaceExample;

public enum EnumWithByteType : byte { A = 1, B = 2, C = 3 };

[GenerateAutomaticInterface]
public class DemoClass
{
public EnumWithByteType SomeProperty { get; set; }
}

""";

const string expected = """
//--------------------------------------------------------------------------------------------------
// <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>
//--------------------------------------------------------------------------------------------------

namespace AutomaticInterfaceExample
{
[global::System.CodeDom.Compiler.GeneratedCode("AutomaticInterface", "")]
public partial interface IDemoClass
{
/// <inheritdoc cref="AutomaticInterfaceExample.DemoClass.SomeProperty" />
global::AutomaticInterfaceExample.EnumWithByteType SomeProperty { get; set; }

}
}

""";
GenerateCode(code).Should().Be(expected);
}
}
Loading