diff options
author | Michael Jin <v-michaeljin@microsoft.com> | 2022-01-13 03:03:59 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-13 03:03:59 +0300 |
commit | 5f69faa7be1e10c8f6bc3c3b31cb4a63532496b8 (patch) | |
tree | b925a5d28755119bcb262692e61a12e2352b3c19 /rocks | |
parent | a0a6ce41c0e5292413ca33dcb76d514e608d21e5 (diff) |
Add support for generating the method and generic method comment signature with nested types (#801)
* Add support for generating the method and generic method comment signature with nested types from Xiao Luo
* Use type.GenericParameters.Count instead of custom method
Diffstat (limited to 'rocks')
-rw-r--r-- | rocks/Mono.Cecil.Rocks/DocCommentId.cs | 220 | ||||
-rw-r--r-- | rocks/Test/Mono.Cecil.Tests/DocCommentIdTests.cs | 242 |
2 files changed, 321 insertions, 141 deletions
diff --git a/rocks/Mono.Cecil.Rocks/DocCommentId.cs b/rocks/Mono.Cecil.Rocks/DocCommentId.cs index da74bed..8af12dd 100644 --- a/rocks/Mono.Cecil.Rocks/DocCommentId.cs +++ b/rocks/Mono.Cecil.Rocks/DocCommentId.cs @@ -10,16 +10,18 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; namespace Mono.Cecil.Rocks { - public class DocCommentId - { + public class DocCommentId { + IMemberDefinition commentMember; StringBuilder id; - DocCommentId () + DocCommentId (IMemberDefinition member) { + commentMember = member; id = new StringBuilder (); } @@ -87,54 +89,66 @@ namespace Mono.Cecil.Rocks { void WriteTypeSignature (TypeReference type) { - switch (type.MetadataType) - { - case MetadataType.Array: - WriteArrayTypeSignature ((ArrayType) type); - break; - case MetadataType.ByReference: - WriteTypeSignature (((ByReferenceType) type).ElementType); - id.Append ('@'); - break; - case MetadataType.FunctionPointer: - WriteFunctionPointerTypeSignature ((FunctionPointerType) type); - break; - case MetadataType.GenericInstance: - WriteGenericInstanceTypeSignature ((GenericInstanceType) type); - break; - case MetadataType.Var: + switch (type.MetadataType) { + case MetadataType.Array: + WriteArrayTypeSignature ((ArrayType)type); + break; + case MetadataType.ByReference: + WriteTypeSignature (((ByReferenceType)type).ElementType); + id.Append ('@'); + break; + case MetadataType.FunctionPointer: + WriteFunctionPointerTypeSignature ((FunctionPointerType)type); + break; + case MetadataType.GenericInstance: + WriteGenericInstanceTypeSignature ((GenericInstanceType)type); + break; + case MetadataType.Var: + if (IsGenericMethodTypeParameter (type)) id.Append ('`'); - id.Append (((GenericParameter) type).Position); - break; - case MetadataType.MVar: - id.Append ('`').Append ('`'); - id.Append (((GenericParameter) type).Position); - break; - case MetadataType.OptionalModifier: - WriteModiferTypeSignature ((OptionalModifierType) type, '!'); - break; - case MetadataType.RequiredModifier: - WriteModiferTypeSignature ((RequiredModifierType) type, '|'); - break; - case MetadataType.Pointer: - WriteTypeSignature (((PointerType) type).ElementType); - id.Append ('*'); - break; - default: - WriteTypeFullName (type); - break; + id.Append ('`'); + id.Append (((GenericParameter)type).Position); + break; + case MetadataType.MVar: + id.Append ('`').Append ('`'); + id.Append (((GenericParameter)type).Position); + break; + case MetadataType.OptionalModifier: + WriteModiferTypeSignature ((OptionalModifierType)type, '!'); + break; + case MetadataType.RequiredModifier: + WriteModiferTypeSignature ((RequiredModifierType)type, '|'); + break; + case MetadataType.Pointer: + WriteTypeSignature (((PointerType)type).ElementType); + id.Append ('*'); + break; + default: + WriteTypeFullName (type); + break; } } + bool IsGenericMethodTypeParameter (TypeReference type) + { + if (commentMember is MethodDefinition methodDefinition && type is GenericParameter genericParameter) + return methodDefinition.GenericParameters.Any (i => i.Name == genericParameter.Name); + + return false; + } + void WriteGenericInstanceTypeSignature (GenericInstanceType type) { if (type.ElementType.IsTypeSpecification ()) throw new NotSupportedException (); - WriteTypeFullName (type.ElementType, stripGenericArity: true); - id.Append ('{'); - WriteList (type.GenericArguments, WriteTypeSignature); - id.Append ('}'); + GenericTypeOptions options = new GenericTypeOptions { + IsArgument = true, + IsNestedType = type.IsNested, + Arguments = type.GenericArguments + }; + + WriteTypeFullName (type.ElementType, options); } void WriteList<T> (IList<T> list, Action<T> action) @@ -197,10 +211,15 @@ namespace Mono.Cecil.Rocks { WriteItemName (member.Name); } - void WriteTypeFullName (TypeReference type, bool stripGenericArity = false) + void WriteTypeFullName (TypeReference type) + { + WriteTypeFullName (type, GenericTypeOptions.Empty ()); + } + + void WriteTypeFullName (TypeReference type, GenericTypeOptions options) { if (type.DeclaringType != null) { - WriteTypeFullName (type.DeclaringType); + WriteTypeFullName (type.DeclaringType, options); id.Append ('.'); } @@ -211,18 +230,69 @@ namespace Mono.Cecil.Rocks { var name = type.Name; - if (stripGenericArity) { + if (options.IsArgument) { var index = name.LastIndexOf ('`'); if (index > 0) name = name.Substring (0, index); } id.Append (name); + + WriteGenericTypeParameters (type, options); + } + + void WriteGenericTypeParameters (TypeReference type, GenericTypeOptions options) + { + if (options.IsArgument && IsGenericType (type)) { + id.Append ('{'); + WriteList (GetGenericTypeArguments (type, options), WriteTypeSignature); + id.Append ('}'); + } + } + + static bool IsGenericType (TypeReference type) + { + // When the type is a nested type and that is defined in a generic class, + // the nested type will have generic parameters but sometimes that is not a generic type. + if (type.HasGenericParameters) { + var name = string.Empty; + var index = type.Name.LastIndexOf ('`'); + if (index >= 0) + name = type.Name.Substring (0, index); + + return type.Name.LastIndexOf ('`') == name.Length; + } + + return false; } + IList<TypeReference> GetGenericTypeArguments (TypeReference type, GenericTypeOptions options) + { + if (options.IsNestedType) { + var typeParameterCount = type.GenericParameters.Count; + var typeGenericArguments = options.Arguments.Skip (options.ArgumentIndex).Take (typeParameterCount).ToList (); + + options.ArgumentIndex += typeParameterCount; + + return typeGenericArguments; + } + + return options.Arguments; + } + + //int GetGenericTypeParameterCount (TypeReference type) + //{ + // var returnValue = 0; + // var index = type.Name.LastIndexOf ('`'); + // if (index >= 0) + // returnValue = int.Parse (type.Name.Substring (index + 1)); + + // return returnValue; + //} + void WriteItemName (string name) { - id.Append (name.Replace ('.', '#').Replace('<', '{').Replace('>', '}')); + id.Append (name.Replace('.', '#').Replace('<', '{').Replace('>', '}')); } public override string ToString () @@ -235,30 +305,44 @@ namespace Mono.Cecil.Rocks { if (member == null) throw new ArgumentNullException ("member"); - var documentId = new DocCommentId (); - - switch (member.MetadataToken.TokenType) - { - case TokenType.Field: - documentId.WriteField ((FieldDefinition) member); - break; - case TokenType.Method: - documentId.WriteMethod ((MethodDefinition) member); - break; - case TokenType.TypeDef: - documentId.WriteType ((TypeDefinition) member); - break; - case TokenType.Event: - documentId.WriteEvent ((EventDefinition) member); - break; - case TokenType.Property: - documentId.WriteProperty ((PropertyDefinition) member); - break; - default: - throw new NotSupportedException (member.FullName); + var documentId = new DocCommentId (member); + + switch (member.MetadataToken.TokenType) { + case TokenType.Field: + documentId.WriteField ((FieldDefinition)member); + break; + case TokenType.Method: + documentId.WriteMethod ((MethodDefinition)member); + break; + case TokenType.TypeDef: + documentId.WriteType ((TypeDefinition)member); + break; + case TokenType.Event: + documentId.WriteEvent ((EventDefinition)member); + break; + case TokenType.Property: + documentId.WriteProperty ((PropertyDefinition)member); + break; + default: + throw new NotSupportedException (member.FullName); } return documentId.ToString (); } + + class GenericTypeOptions { + public bool IsArgument { get; set; } + + public bool IsNestedType { get; set; } + + public IList<TypeReference> Arguments { get; set; } + + public int ArgumentIndex { get; set; } + + public static GenericTypeOptions Empty () + { + return new GenericTypeOptions (); + } + } } -} +}
\ No newline at end of file diff --git a/rocks/Test/Mono.Cecil.Tests/DocCommentIdTests.cs b/rocks/Test/Mono.Cecil.Tests/DocCommentIdTests.cs index 244d5ae..8cfc828 100644 --- a/rocks/Test/Mono.Cecil.Tests/DocCommentIdTests.cs +++ b/rocks/Test/Mono.Cecil.Tests/DocCommentIdTests.cs @@ -6,116 +6,181 @@ using NUnit.Framework; using Mono.Cecil.Rocks; -namespace N -{ +namespace N { - /// <summary> - /// ID string generated is "T:N.X". - /// </summary> - public class X : IX<KVP<string, int>> - { - /// <summary> - /// ID string generated is "M:N.X.#ctor". - /// </summary> - public X() { } + /// <summary> + /// ID string generated is "T:N.X". + /// </summary> + public class X : IX<KVP<string, int>> { + /// <summary> + /// ID string generated is "M:N.X.#ctor". + /// </summary> + public X () { } - /// <summary> - /// ID string generated is "M:N.X.#ctor(System.Int32)". - /// </summary> - /// <param name="i">Describe parameter.</param> - public X(int i) { } + /// <summary> + /// ID string generated is "M:N.X.#ctor(System.Int32)". + /// </summary> + /// <param name="i">Describe parameter.</param> + public X (int i) { } - /// <summary> - /// ID string generated is "F:N.X.q". - /// </summary> - public string q; + /// <summary> + /// ID string generated is "F:N.X.q". + /// </summary> + public string q; - /// <summary> - /// ID string generated is "F:N.X.PI". - /// </summary> - public const double PI = 3.14; + /// <summary> + /// ID string generated is "F:N.X.PI". + /// </summary> + public const double PI = 3.14; - /// <summary> - /// ID string generated is "M:N.X.f". - /// </summary> - public int f() { return 1; } + /// <summary> + /// ID string generated is "M:N.X.f". + /// </summary> + public int f () { return 1; } - /// <summary> - /// ID string generated is "M:N.X.bb(System.String,System.Int32@)". - /// </summary> - public int bb(string s, ref int y) { return 1; } + /// <summary> + /// ID string generated is "M:N.X.bb(System.String,System.Int32@)". + /// </summary> + public int bb (string s, ref int y) { return 1; } - /// <summary> - /// ID string generated is "M:N.X.gg(System.Int16[],System.Int32[0:,0:])". - /// </summary> - public int gg(short[] array1, int[,] array) { return 0; } + /// <summary> + /// ID string generated is "M:N.X.gg(System.Int16[],System.Int32[0:,0:])". + /// </summary> + public int gg (short [] array1, int [,] array) { return 0; } - /// <summary> - /// ID string generated is "M:N.X.op_Addition(N.X,N.X)". - /// </summary> - public static X operator +(X x, X xx) { return x; } + /// <summary> + /// ID string generated is "M:N.X.op_Addition(N.X,N.X)". + /// </summary> + public static X operator + (X x, X xx) { return x; } - /// <summary> - /// ID string generated is "P:N.X.prop". - /// </summary> - public int prop { get { return 1; } set { } } + /// <summary> + /// ID string generated is "P:N.X.prop". + /// </summary> + public int prop { get { return 1; } set { } } - /// <summary> - /// ID string generated is "E:N.X.d". - /// </summary> + /// <summary> + /// ID string generated is "E:N.X.d". + /// </summary> #pragma warning disable 67 - public event D d; + public event D d; #pragma warning restore 67 /// <summary> /// ID string generated is "P:N.X.Item(System.String)". /// </summary> - public int this[string s] { get { return 1; } } + public int this [string s] { get { return 1; } } - /// <summary> - /// ID string generated is "T:N.X.Nested". - /// </summary> - public class Nested { } + /// <summary> + /// ID string generated is "T:N.X.Nested". + /// </summary> + public class Nested { } - /// <summary> - /// ID string generated is "T:N.X.D". - /// </summary> - public delegate void D(int i); + /// <summary> + /// ID string generated is "T:N.X.D". + /// </summary> + public delegate void D (int i); - /// <summary> - /// ID string generated is "M:N.X.op_Explicit(N.X)~System.Int32". - /// </summary> - public static explicit operator int(X x) { return 1; } + /// <summary> + /// ID string generated is "M:N.X.op_Explicit(N.X)~System.Int32". + /// </summary> + public static explicit operator int (X x) { return 1; } - public static void Linq (IEnumerable<string> enumerable, Func<string> selector) - { - } + public static void Linq (IEnumerable<string> enumerable, Func<string> selector) + { + } - /// <summary> + /// <summary> /// ID string generated is "M:N.X.N#IX{N#KVP{System#String,System#Int32}}#IXA(N.KVP{System.String,System.Int32})" - /// </summary> - void IX<KVP<string, int>>.IXA (KVP<string, int> k) { } - } + /// </summary> + void IX<KVP<string, int>>.IXA (KVP<string, int> k) { } + } - public interface IX<K> - { + public interface IX<K> { void IXA (K k); } public class KVP<K, T> { } + + public class GenericMethod { + /// <summary> + /// ID string generated is "M:N.GenericMethod.WithNestedType``1(N.GenericType{``0}.NestedType)". + /// </summary> + public void WithNestedType<T> (GenericType<T>.NestedType nestedType) { } + + + /// <summary> + /// ID string generated is "M:N.GenericMethod.WithIntOfNestedType``1(N.GenericType{System.Int32}.NestedType)". + /// </summary> + public void WithIntOfNestedType<T> (GenericType<int>.NestedType nestedType) { } + + + /// <summary> + /// ID string generated is "M:N.GenericMethod.WithNestedGenericType``1(N.GenericType{``0}.NestedGenericType{``0}.NestedType)". + /// </summary> + public void WithNestedGenericType<T> (GenericType<T>.NestedGenericType<T>.NestedType nestedType) { } + + + /// <summary> + /// ID string generated is "M:N.GenericMethod.WithIntOfNestedGenericType``1(N.GenericType{System.Int32}.NestedGenericType{System.Int32}.NestedType)". + /// </summary> + public void WithIntOfNestedGenericType<T> (GenericType<int>.NestedGenericType<int>.NestedType nestedType) { } + + + /// <summary> + /// ID string generated is "M:N.GenericMethod.WithMultipleTypeParameterAndNestedGenericType``2(N.GenericType{``0}.NestedGenericType{``1}.NestedType)". + /// </summary> + public void WithMultipleTypeParameterAndNestedGenericType<T1, T2> (GenericType<T1>.NestedGenericType<T2>.NestedType nestedType) { } + + + /// <summary> + /// ID string generated is "M:N.GenericMethod.WithMultipleTypeParameterAndIntOfNestedGenericType``2(N.GenericType{System.Int32}.NestedGenericType{System.Int32}.NestedType)". + /// </summary> + public void WithMultipleTypeParameterAndIntOfNestedGenericType<T1, T2> (GenericType<int>.NestedGenericType<int>.NestedType nestedType) { } + } + + public class GenericType<T> { + public class NestedType { } + + public class NestedGenericType<TNested> { + public class NestedType { } + + /// <summary> + /// ID string generated is "M:N.GenericType`1.NestedGenericType`1.WithTypeParameterOfGenericMethod``1(System.Collections.Generic.List{``0})" + /// </summary> + public void WithTypeParameterOfGenericMethod<TMethod> (List<TMethod> list) { } + + + /// <summary> + /// ID string generated is "M:N.GenericType`1.NestedGenericType`1.WithTypeParameterOfGenericType(System.Collections.Generic.Dictionary{`0,`1})" + /// </summary> + public void WithTypeParameterOfGenericType (Dictionary<T, TNested> dict) { } + + + /// <summary> + /// ID string generated is "M:N.GenericType`1.NestedGenericType`1.WithTypeParameterOfGenericType``1(System.Collections.Generic.List{`1})" + /// </summary> + public void WithTypeParameterOfNestedGenericType<TMethod> (List<TNested> list) { } + + + /// <summary> + /// ID string generated is "M:N.GenericType`1.NestedGenericType`1.WithTypeParameterOfGenericTypeAndGenericMethod``1(System.Collections.Generic.Dictionary{`1,``0})" + /// </summary> + public void WithTypeParameterOfGenericTypeAndGenericMethod<TMethod> (Dictionary<TNested, TMethod> dict) { } + } + } } namespace Mono.Cecil.Tests { @@ -182,7 +247,7 @@ namespace Mono.Cecil.Tests { AssertDocumentID ("M:N.X.bb(System.String,System.Int32@)", method); } - + [Test] public void MethodWithArrayParameters () { @@ -192,6 +257,32 @@ namespace Mono.Cecil.Tests { AssertDocumentID ("M:N.X.gg(System.Int16[],System.Int32[0:,0:])", method); } + [TestCase ("WithNestedType", "WithNestedType``1(N.GenericType{``0}.NestedType)")] + [TestCase ("WithIntOfNestedType", "WithIntOfNestedType``1(N.GenericType{System.Int32}.NestedType)")] + [TestCase ("WithNestedGenericType", "WithNestedGenericType``1(N.GenericType{``0}.NestedGenericType{``0}.NestedType)")] + [TestCase ("WithIntOfNestedGenericType", "WithIntOfNestedGenericType``1(N.GenericType{System.Int32}.NestedGenericType{System.Int32}.NestedType)")] + [TestCase ("WithMultipleTypeParameterAndNestedGenericType", "WithMultipleTypeParameterAndNestedGenericType``2(N.GenericType{``0}.NestedGenericType{``1}.NestedType)")] + [TestCase ("WithMultipleTypeParameterAndIntOfNestedGenericType", "WithMultipleTypeParameterAndIntOfNestedGenericType``2(N.GenericType{System.Int32}.NestedGenericType{System.Int32}.NestedType)")] + public void GenericMethodWithNestedTypeParameters (string methodName, string docCommentId) + { + var type = GetTestType (typeof (N.GenericMethod)); + var method = type.Methods.Single (m => m.Name == methodName); + + AssertDocumentID ($"M:N.GenericMethod.{docCommentId}", method); + } + + [TestCase ("WithTypeParameterOfGenericMethod", "WithTypeParameterOfGenericMethod``1(System.Collections.Generic.List{``0})")] + [TestCase ("WithTypeParameterOfGenericType", "WithTypeParameterOfGenericType(System.Collections.Generic.Dictionary{`0,`1})")] + [TestCase ("WithTypeParameterOfNestedGenericType", "WithTypeParameterOfNestedGenericType``1(System.Collections.Generic.List{`1})")] + [TestCase ("WithTypeParameterOfGenericTypeAndGenericMethod", "WithTypeParameterOfGenericTypeAndGenericMethod``1(System.Collections.Generic.Dictionary{`1,``0})")] + public void GenericTypeWithTypeParameters (string methodName, string docCommentId) + { + var type = GetTestType (typeof (N.GenericType<>.NestedGenericType<>)); + var method = type.Methods.Single (m => m.Name == methodName); + + AssertDocumentID ($"M:N.GenericType`1.NestedGenericType`1.{docCommentId}", method); + } + [Test] public void OpAddition () { @@ -268,7 +359,7 @@ namespace Mono.Cecil.Tests { public void EII () { var type = GetTestType (); - var method = type.Methods.Where (m => m.Name.Contains("IXA")).First (); + var method = type.Methods.Where (m => m.Name.Contains ("IXA")).First (); AssertDocumentID ("M:N.X.N#IX{N#KVP{System#String,System#Int32}}#IXA(N.KVP{System.String,System.Int32})", method); } @@ -278,9 +369,14 @@ namespace Mono.Cecil.Tests { return typeof (N.X).ToDefinition (); } + TypeDefinition GetTestType (Type type) + { + return type.ToDefinition (); + } + static void AssertDocumentID (string docId, IMemberDefinition member) { Assert.AreEqual (docId, DocCommentId.GetDocCommentId (member)); } } -} +}
\ No newline at end of file |