From 744662eb5431b08f7ca1ae94fe931e76cc476efb Mon Sep 17 00:00:00 2001
From: Simon McKenzie <17579442+simonmckenzie@users.noreply.github.com>
Date: Wed, 4 Dec 2024 09:42:07 +1100
Subject: [PATCH 1/4] Add the new() type constraint to method parameters
This addresses #64, where type parameters weren't having the `new()` constraint applied.
Also updated some tests that were failing due to changes in the `inheritdoc` annotations and re-ran csharpier.
---
.../AutomaticInterface/Builder.cs | 20 ++++++++-----------
.../AutomaticInterface/RoslynExtensions.cs | 6 ++++++
AutomaticInterface/Tests/GeneratorTests.cs | 15 +++++++-------
3 files changed, 22 insertions(+), 19 deletions(-)
diff --git a/AutomaticInterface/AutomaticInterface/Builder.cs b/AutomaticInterface/AutomaticInterface/Builder.cs
index 7db6a00..37832f8 100644
--- a/AutomaticInterface/AutomaticInterface/Builder.cs
+++ b/AutomaticInterface/AutomaticInterface/Builder.cs
@@ -63,12 +63,9 @@ private static string GetNameSpace(ISymbol typeSymbol)
{
var generationAttribute = typeSymbol
.GetAttributes()
- .FirstOrDefault(
- x =>
- x.AttributeClass != null
- && x.AttributeClass
- .Name
- .Contains(AutomaticInterfaceGenerator.DefaultAttributeName)
+ .FirstOrDefault(x =>
+ x.AttributeClass != null
+ && x.AttributeClass.Name.Contains(AutomaticInterfaceGenerator.DefaultAttributeName)
);
if (generationAttribute == null)
@@ -298,12 +295,11 @@ private static PropertySetKind GetSetKind(IMethodSymbol? setMethodSymbol)
private static bool HasIgnoreAttribute(ISymbol x)
{
return x.GetAttributes()
- .Any(
- a =>
- a.AttributeClass != null
- && a.AttributeClass
- .Name
- .Contains(AutomaticInterfaceGenerator.IgnoreAutomaticInterfaceAttributeName)
+ .Any(a =>
+ a.AttributeClass != null
+ && a.AttributeClass.Name.Contains(
+ AutomaticInterfaceGenerator.IgnoreAutomaticInterfaceAttributeName
+ )
);
}
diff --git a/AutomaticInterface/AutomaticInterface/RoslynExtensions.cs b/AutomaticInterface/AutomaticInterface/RoslynExtensions.cs
index 0174b84..b31827a 100644
--- a/AutomaticInterface/AutomaticInterface/RoslynExtensions.cs
+++ b/AutomaticInterface/AutomaticInterface/RoslynExtensions.cs
@@ -58,6 +58,12 @@ SymbolDisplayFormat typeDisplayFormat
isFirstConstraint = false;
}
+ if (typeParameterSymbol.HasConstructorConstraint)
+ {
+ constraints += "new()";
+ isFirstConstraint = false;
+ }
+
foreach (var constraintType in typeParameterSymbol.ConstraintTypes)
{
// if not first constraint prepend with comma
diff --git a/AutomaticInterface/Tests/GeneratorTests.cs b/AutomaticInterface/Tests/GeneratorTests.cs
index d2c65d8..129f389 100644
--- a/AutomaticInterface/Tests/GeneratorTests.cs
+++ b/AutomaticInterface/Tests/GeneratorTests.cs
@@ -1053,11 +1053,12 @@ namespace AutomaticInterfaceExample;
public class DemoClass
{
///
- public string CMethod(string x, string y)
+ public string CMethod(string x, string y)
where T : class
where T1 : struct
where T3 : DemoClass
where T4 : IDemoClass
+ where T5 : new()
{
return "Ok";
}
@@ -1080,8 +1081,8 @@ namespace AutomaticInterfaceExample
[global::System.CodeDom.Compiler.GeneratedCode("AutomaticInterface", "")]
public partial interface IDemoClass
{
- ///
- string CMethod(string x, string y) where T : class where T1 : struct where T3 : global::AutomaticInterfaceExample.DemoClass where T4 : IDemoClass;
+ ///
+ string CMethod(string x, string y) where T : class where T1 : struct where T3 : global::AutomaticInterfaceExample.DemoClass where T4 : IDemoClass where T5 : new();
}
}
@@ -1820,10 +1821,10 @@ namespace AutomaticInterfaceExample
[global::System.CodeDom.Compiler.GeneratedCode("AutomaticInterface", "")]
public partial interface IDemoClass
{
- ///
+ ///
void AMethod(Func> getValue);
- ///
+ ///
void AMethod(Func> getValue);
}
@@ -1875,10 +1876,10 @@ namespace AutomaticInterfaceExample
[global::System.CodeDom.Compiler.GeneratedCode("AutomaticInterface", "")]
public partial interface IDemoClass
{
- ///
+ ///
void AMethod(Func> getValue);
- ///
+ ///
void AMethod(Func> getValue);
}
From 147cd5e0060eabba565f4179131f60ab3fec4fd8 Mon Sep 17 00:00:00 2001
From: Simon McKenzie <17579442+simonmckenzie@users.noreply.github.com>
Date: Wed, 4 Dec 2024 11:23:42 +1100
Subject: [PATCH 2/4] Add support for `params` parameters
Small fix - the existing code wasn't emitting the "params" keyword
---
.../AutomaticInterface/Builder.cs | 20 +++++----
AutomaticInterface/Tests/GeneratorTests.cs | 43 +++++++++++++++++++
2 files changed, 54 insertions(+), 9 deletions(-)
diff --git a/AutomaticInterface/AutomaticInterface/Builder.cs b/AutomaticInterface/AutomaticInterface/Builder.cs
index 37832f8..6d24694 100644
--- a/AutomaticInterface/AutomaticInterface/Builder.cs
+++ b/AutomaticInterface/AutomaticInterface/Builder.cs
@@ -102,7 +102,7 @@ private static void AddMethod(InterfaceBuilder codeGenerator, IMethodSymbol meth
ActivateNullableIfNeeded(codeGenerator, method);
var paramResult = new HashSet();
- method.Parameters.Select(GetMethodSignature).ToList().ForEach(x => paramResult.Add(x));
+ method.Parameters.Select(GetParameterSignature).ToList().ForEach(x => paramResult.Add(x));
var typedArgs = method
.TypeParameters.Select(arg =>
@@ -194,16 +194,18 @@ private static void AddEventsToInterface(List members, InterfaceBuilder
});
}
- private static string GetMethodSignature(IParameterSymbol x)
+ private static string GetParameterSignature(IParameterSymbol x)
{
- var name = GetMethodName(x);
- var refKindText = GetRefKind(x);
- var optionalValue = GetMethodOptionalValue(x);
+ var name = GetParameterName(x);
+ var refKindText = GetParameterRefKind(x);
+ var optionalValue = GetParameterOptionalValue(x);
- return $"{refKindText}{x.Type.ToDisplayString(FullyQualifiedDisplayFormat)} {name}{optionalValue}";
+ var paramsPrefix = x.IsParams ? "params " : "";
+
+ return $"{paramsPrefix}{refKindText}{x.Type.ToDisplayString(FullyQualifiedDisplayFormat)} {name}{optionalValue}";
}
- private static string GetMethodOptionalValue(IParameterSymbol x)
+ private static string GetParameterOptionalValue(IParameterSymbol x)
{
if (!x.HasExplicitDefaultValue)
{
@@ -222,7 +224,7 @@ null when x.Type.IsValueType
};
}
- private static string GetMethodName(IParameterSymbol x)
+ private static string GetParameterName(IParameterSymbol x)
{
var syntaxReference = x.DeclaringSyntaxReferences.FirstOrDefault();
@@ -231,7 +233,7 @@ private static string GetMethodName(IParameterSymbol x)
: x.Name;
}
- private static string GetRefKind(IParameterSymbol x)
+ private static string GetParameterRefKind(IParameterSymbol x)
{
return x.RefKind switch
{
diff --git a/AutomaticInterface/Tests/GeneratorTests.cs b/AutomaticInterface/Tests/GeneratorTests.cs
index 129f389..1ee64b7 100644
--- a/AutomaticInterface/Tests/GeneratorTests.cs
+++ b/AutomaticInterface/Tests/GeneratorTests.cs
@@ -97,6 +97,49 @@ public partial interface IDemoClass
GenerateCode(code).Should().Be(expected);
}
+ [Fact]
+ public void WorksWithParamsParameters()
+ {
+ const string code = """
+
+ using AutomaticInterface;
+
+ namespace AutomaticInterfaceExample;
+
+ [GenerateAutomaticInterface]
+ public class DemoClass
+ {
+ public void AMethod(params int[] numbers)
+ {
+ }
+ }
+
+
+ """;
+ const string expected = """
+ //--------------------------------------------------------------------------------------------------
+ //
+ // This code was generated by a tool.
+ //
+ // Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
+ //
+ //--------------------------------------------------------------------------------------------------
+
+ namespace AutomaticInterfaceExample
+ {
+ [global::System.CodeDom.Compiler.GeneratedCode("AutomaticInterface", "")]
+ public partial interface IDemoClass
+ {
+ ///
+ void AMethod(params int[] numbers);
+
+ }
+ }
+
+ """;
+ GenerateCode(code).Should().Be(expected);
+ }
+
[Fact]
public void WorksWithOptionalStructParameters()
{
From b978bae42226bad7559b5c80aa0afa449d0b192c Mon Sep 17 00:00:00 2001
From: Simon McKenzie <17579442+simonmckenzie@users.noreply.github.com>
Date: Wed, 4 Dec 2024 09:59:00 +1100
Subject: [PATCH 3/4] Increment the package version; update changelog
---
.../AutomaticInterface/AutomaticInterface.csproj | 2 +-
README.md | 6 +++++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/AutomaticInterface/AutomaticInterface/AutomaticInterface.csproj b/AutomaticInterface/AutomaticInterface/AutomaticInterface.csproj
index f114ed4..e03d7d7 100644
--- a/AutomaticInterface/AutomaticInterface/AutomaticInterface.csproj
+++ b/AutomaticInterface/AutomaticInterface/AutomaticInterface.csproj
@@ -25,7 +25,7 @@
MIT
True
latest-Recommended
- 5.1.0
+ 5.1.1
README.md
true
1701;1702;NU5128
diff --git a/README.md b/README.md
index de12120..8dd6a58 100644
--- a/README.md
+++ b/README.md
@@ -185,6 +185,10 @@ Should be simply a build and run Tests
## Changelog
+### 5.1.1
+
+- Emit `new()` type constraints on generic type parameters; emit `params` keyword for method parameters. Thanks, @simonmckenzie!
+
### 5.1.0
- Improves inheritdoc so that developer documentation is properly referenced on the autogenerated interfaces. Thanks, CFlorell!
@@ -195,7 +199,7 @@ Should be simply a build and run Tests
### 5.0.2
-- Fully qualify type references; remove usings . Thanks, @simonmckenzie!
+- Fully qualify type references; remove usings. Thanks, @simonmckenzie!
### 5.0.1
From c305b5e6c561b95ef99f37f0997a40887897282b Mon Sep 17 00:00:00 2001
From: Simon McKenzie <17579442+simonmckenzie@users.noreply.github.com>
Date: Wed, 4 Dec 2024 11:51:23 +1100
Subject: [PATCH 4/4] Replace custom "GetParameterSignature" code with call to
`.ToDisplayString(FullyQualifiedDisplayFormat)`
This removes a fair bit of code and ensures we're formatting our parameters consistently.
---
.../AutomaticInterface/Builder.cs | 62 +++----------------
1 file changed, 8 insertions(+), 54 deletions(-)
diff --git a/AutomaticInterface/AutomaticInterface/Builder.cs b/AutomaticInterface/AutomaticInterface/Builder.cs
index 6d24694..9b167ca 100644
--- a/AutomaticInterface/AutomaticInterface/Builder.cs
+++ b/AutomaticInterface/AutomaticInterface/Builder.cs
@@ -17,11 +17,14 @@ private static string InheritDoc(ISymbol source) =>
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
memberOptions: SymbolDisplayMemberOptions.IncludeParameters,
parameterOptions: SymbolDisplayParameterOptions.IncludeType
- | SymbolDisplayParameterOptions.IncludeParamsRefOut,
+ | SymbolDisplayParameterOptions.IncludeParamsRefOut
+ | SymbolDisplayParameterOptions.IncludeDefaultValue
+ | SymbolDisplayParameterOptions.IncludeName,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Included,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes
| SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier
+ | SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers
);
public static string BuildInterfaceFor(ITypeSymbol typeSymbol)
@@ -102,7 +105,10 @@ private static void AddMethod(InterfaceBuilder codeGenerator, IMethodSymbol meth
ActivateNullableIfNeeded(codeGenerator, method);
var paramResult = new HashSet();
- method.Parameters.Select(GetParameterSignature).ToList().ForEach(x => paramResult.Add(x));
+ method
+ .Parameters.Select(x => x.ToDisplayString(FullyQualifiedDisplayFormat))
+ .ToList()
+ .ForEach(x => paramResult.Add(x));
var typedArgs = method
.TypeParameters.Select(arg =>
@@ -194,58 +200,6 @@ private static void AddEventsToInterface(List members, InterfaceBuilder
});
}
- private static string GetParameterSignature(IParameterSymbol x)
- {
- var name = GetParameterName(x);
- var refKindText = GetParameterRefKind(x);
- var optionalValue = GetParameterOptionalValue(x);
-
- var paramsPrefix = x.IsParams ? "params " : "";
-
- return $"{paramsPrefix}{refKindText}{x.Type.ToDisplayString(FullyQualifiedDisplayFormat)} {name}{optionalValue}";
- }
-
- private static string GetParameterOptionalValue(IParameterSymbol x)
- {
- if (!x.HasExplicitDefaultValue)
- {
- return string.Empty;
- }
-
- return x.ExplicitDefaultValue switch
- {
- string => $" = \"{x.ExplicitDefaultValue}\"",
- bool value => $" = {(value ? "true" : "false")}",
- // struct
- null when x.Type.IsValueType
- => $" = default({x.Type.ToDisplayString(FullyQualifiedDisplayFormat)})",
- null => " = null",
- _ => $" = {x.ExplicitDefaultValue}",
- };
- }
-
- private static string GetParameterName(IParameterSymbol x)
- {
- var syntaxReference = x.DeclaringSyntaxReferences.FirstOrDefault();
-
- return syntaxReference != null
- ? ((ParameterSyntax)syntaxReference.GetSyntax()).Identifier.Text
- : x.Name;
- }
-
- private static string GetParameterRefKind(IParameterSymbol x)
- {
- return x.RefKind switch
- {
- RefKind.Ref => "ref ",
- RefKind.Out => "out ",
- RefKind.In => "in ",
- // Not sure why RefReadOnly and In both has Enum index 3.
- // RefKind.RefReadOnly => "ref readonly ",
- _ => string.Empty,
- };
- }
-
private static void AddPropertiesToInterface(
List members,
InterfaceBuilder interfaceGenerator