-
Notifications
You must be signed in to change notification settings - Fork 14
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
Emit new() type constraints on generic type parameters; emit params keyword for method parameters #65
base: master
Are you sure you want to change the base?
Emit new() type constraints on generic type parameters; emit params keyword for method parameters #65
Changes from all commits
744662e
147cd5e
b978bae
c305b5e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This ensures that parameters that share names with keywords will be escaped. This replicates the behaviour of the (now deleted) (there's an existing test for this, |
||
); | ||
|
||
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 => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Csharpier reformat - this was failing on master. |
||
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<string>(); | ||
method.Parameters.Select(GetMethodSignature).ToList().ForEach(x => paramResult.Add(x)); | ||
method | ||
.Parameters.Select(x => x.ToDisplayString(FullyQualifiedDisplayFormat)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
.ToList() | ||
.ForEach(x => paramResult.Add(x)); | ||
|
||
var typedArgs = method | ||
.TypeParameters.Select(arg => | ||
|
@@ -197,56 +200,6 @@ private static void AddEventsToInterface(List<ISymbol> 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<ISymbol> 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 | ||
) | ||
); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 = """ | ||
//-------------------------------------------------------------------------------------------------- | ||
// <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.AMethod(params int[])" /> | ||
void AMethod(params int[] numbers); | ||
|
||
} | ||
} | ||
|
||
"""; | ||
GenerateCode(code).Should().Be(expected); | ||
} | ||
|
||
[Fact] | ||
public void WorksWithOptionalStructParameters() | ||
{ | ||
|
@@ -1053,11 +1096,12 @@ namespace AutomaticInterfaceExample; | |
public class DemoClass | ||
{ | ||
/// <inheritdoc /> | ||
public string CMethod<T, T1, T2, T3, T4>(string x, string y) | ||
public string CMethod<T, T1, T2, T3, T4, T5>(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 | ||
{ | ||
/// <inheritdoc cref="AutomaticInterfaceExample.DemoClass.CMethod{T, T1, T2, T3, T4}(string, string)" /> | ||
string CMethod<T, T1, T2, T3, T4>(string x, string y) where T : class where T1 : struct where T3 : global::AutomaticInterfaceExample.DemoClass where T4 : IDemoClass; | ||
/// <inheritdoc cref="AutomaticInterfaceExample.DemoClass.CMethod{T, T1, T2, T3, T4, T5}(string, string)" /> | ||
string CMethod<T, T1, T2, T3, T4, T5>(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 | ||
{ | ||
/// <inheritdoc /> | ||
/// <inheritdoc cref="AutomaticInterfaceExample.DemoClass.AMethod(Func{Task{int}})" /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These tests were failing on master. Updated. |
||
void AMethod(Func<Task<int>> getValue); | ||
|
||
/// <inheritdoc /> | ||
/// <inheritdoc cref="AutomaticInterfaceExample.DemoClass.AMethod(Func{Task{float}})" /> | ||
void AMethod(Func<Task<float>> getValue); | ||
|
||
} | ||
|
@@ -1875,10 +1919,10 @@ namespace AutomaticInterfaceExample | |
[global::System.CodeDom.Compiler.GeneratedCode("AutomaticInterface", "")] | ||
public partial interface IDemoClass | ||
{ | ||
/// <inheritdoc /> | ||
/// <inheritdoc cref="AutomaticInterfaceExample.DemoClass.AMethod(Func{Task{AutomaticInterfaceExample.Types1.Model}})" /> | ||
void AMethod(Func<Task<global::AutomaticInterfaceExample.Types1.Model>> getValue); | ||
|
||
/// <inheritdoc /> | ||
/// <inheritdoc cref="AutomaticInterfaceExample.DemoClass.AMethod(Func{Task{AutomaticInterfaceExample.Types2.Model}})" /> | ||
void AMethod(Func<Task<global::AutomaticInterfaceExample.Types2.Model>> getValue); | ||
|
||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've expanded the parameter formatting rules so we can use this formatter for parameter signature generation.