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/AutomaticInterface/AutomaticInterface/Builder.cs b/AutomaticInterface/AutomaticInterface/Builder.cs index 7db6a00..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) @@ -63,12 +66,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) @@ -105,7 +105,10 @@ 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(x => x.ToDisplayString(FullyQualifiedDisplayFormat)) + .ToList() + .ForEach(x => paramResult.Add(x)); var typedArgs = method .TypeParameters.Select(arg => @@ -197,56 +200,6 @@ private static void AddEventsToInterface(List members, InterfaceBuilder }); } - private static string GetMethodSignature(IParameterSymbol x) - { - var name = GetMethodName(x); - var refKindText = GetRefKind(x); - var optionalValue = GetMethodOptionalValue(x); - - return $"{refKindText}{x.Type.ToDisplayString(FullyQualifiedDisplayFormat)} {name}{optionalValue}"; - } - - private static string GetMethodOptionalValue(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 GetMethodName(IParameterSymbol x) - { - var syntaxReference = x.DeclaringSyntaxReferences.FirstOrDefault(); - - return syntaxReference != null - ? ((ParameterSyntax)syntaxReference.GetSyntax()).Identifier.Text - : x.Name; - } - - private static string GetRefKind(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 @@ -298,12 +251,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..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() { @@ -1053,11 +1096,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 +1124,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 +1864,10 @@ namespace AutomaticInterfaceExample [global::System.CodeDom.Compiler.GeneratedCode("AutomaticInterface", "")] public partial interface IDemoClass { - /// + /// void AMethod(Func> getValue); - /// + /// void AMethod(Func> getValue); } @@ -1875,10 +1919,10 @@ namespace AutomaticInterfaceExample [global::System.CodeDom.Compiler.GeneratedCode("AutomaticInterface", "")] public partial interface IDemoClass { - /// + /// void AMethod(Func> getValue); - /// + /// void AMethod(Func> getValue); } 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