diff options
author | Min Huang <huangmin@microsoft.com> | 2022-03-08 04:28:57 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-08 04:28:57 +0300 |
commit | c27c019e5a52d897dbb54e15bd645ef693e8bc57 (patch) | |
tree | 738e57e055f77bc9c2e726a8e300bde9f87db7c6 | |
parent | 637f82100ff6ced64a35f1c6ffcc5375a87478a3 (diff) |
feat#550401: Support new .net language feature: tuple names (#618)
* Support new .net language feature: tuple names
* update test case
* update test case
* update
* updated
* update test case
* add debug info
* update
* Remove console log
7 files changed, 87 insertions, 3 deletions
diff --git a/mdoc/Consts.cs b/mdoc/Consts.cs index 3ef0b936..4d2ef7c3 100644 --- a/mdoc/Consts.cs +++ b/mdoc/Consts.cs @@ -49,5 +49,6 @@ namespace Mono.Documentation public const string IsByRefLikeAttribute = "System.Runtime.CompilerServices.IsByRefLikeAttribute"; public const string IsReadOnlyAttribute = "System.Runtime.CompilerServices.IsReadOnlyAttribute"; public const string InAttribute = "System.Runtime.InteropServices.InAttribute"; + public const string TupleElementNamesAttribute = "System.Runtime.CompilerServices.TupleElementNamesAttribute"; } } diff --git a/mdoc/Mono.Documentation/Updater/AttributeParserContext.cs b/mdoc/Mono.Documentation/Updater/AttributeParserContext.cs index de9ee35d..e294d7cb 100644 --- a/mdoc/Mono.Documentation/Updater/AttributeParserContext.cs +++ b/mdoc/Mono.Documentation/Updater/AttributeParserContext.cs @@ -3,6 +3,7 @@ using Mono.Cecil; using Mono.Documentation.Util; using System; using System.Collections.ObjectModel; +using System.Linq; namespace Mono.Documentation.Updater { @@ -10,9 +11,11 @@ namespace Mono.Documentation.Updater { private int nullableAttributeIndex; private int dynamicAttributeIndex; + private int tupleNameAttributeIndex; private ICustomAttributeProvider provider; private ReadOnlyCollection<bool?> nullableAttributeFlags; private ReadOnlyCollection<bool> dynamicAttributeFlags; + private string[] tupleElementNames; private AttributeParserContext(ICustomAttributeProvider provider) { @@ -20,6 +23,7 @@ namespace Mono.Documentation.Updater ReadDynamicAttribute(); ReadNullableAttribute(); + ReadTupleElementNames(); } private bool ExistsNullableAttribute @@ -73,6 +77,11 @@ namespace Mono.Documentation.Updater return false; } + public string GetTupleElementName() + { + return (tupleElementNames == null || tupleNameAttributeIndex >= tupleElementNames.Length) ? null : tupleElementNames[tupleNameAttributeIndex++]; + } + private void ReadDynamicAttribute() { DynamicTypeProvider dynamicTypeProvider = new DynamicTypeProvider(provider); @@ -88,5 +97,18 @@ namespace Mono.Documentation.Updater NullableReferenceTypeProvider nullableReferenceTypeProvider = new NullableReferenceTypeProvider(provider); nullableAttributeFlags = new ReadOnlyCollection<bool?>(nullableReferenceTypeProvider.GetNullableReferenceTypeFlags()); } + + private void ReadTupleElementNames() + { + if (provider != null && provider.HasCustomAttributes) + { + var tupleNamesAttr = provider.CustomAttributes.Where(attr => attr.AttributeType.FullName == Consts.TupleElementNamesAttribute).FirstOrDefault(); + if (tupleNamesAttr != null) + { + var constructorArgs = tupleNamesAttr.ConstructorArguments.FirstOrDefault().Value as CustomAttributeArgument[]; + tupleElementNames = constructorArgs?.Select(arg => arg.Value as string).ToArray(); + } + } + } } }
\ No newline at end of file diff --git a/mdoc/Mono.Documentation/Updater/EmptyAttributeParserContext.cs b/mdoc/Mono.Documentation/Updater/EmptyAttributeParserContext.cs index 87aecaa4..71b638fd 100644 --- a/mdoc/Mono.Documentation/Updater/EmptyAttributeParserContext.cs +++ b/mdoc/Mono.Documentation/Updater/EmptyAttributeParserContext.cs @@ -26,5 +26,10 @@ { return false; } + + public string GetTupleElementName() + { + return null; + } } } diff --git a/mdoc/Mono.Documentation/Updater/Formatters/CSharpFullMemberFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/CSharpFullMemberFormatter.cs index 811ff3be..e885df57 100644 --- a/mdoc/Mono.Documentation/Updater/Formatters/CSharpFullMemberFormatter.cs +++ b/mdoc/Mono.Documentation/Updater/Formatters/CSharpFullMemberFormatter.cs @@ -137,7 +137,9 @@ namespace Mono.Documentation.Updater.Formatters if (genInst.Name.StartsWith ("ValueTuple`")) { buf.Append ("("); - var genArgList = new List<string> (); + var genArgTypeList = new List<string> (); + // tuple element names should be traversed before recursion. + var genArgNameList = genInst.GenericArguments.Select(arg => context.GetTupleElementName()).ToList(); foreach (var item in genInst.GenericArguments) { var isNullableType = false; @@ -147,11 +149,11 @@ namespace Mono.Documentation.Updater.Formatters } var underlyingTypeName = GetTypeName (item, context, appendGeneric, useTypeProjection) + GetTypeNullableSymbol (item, isNullableType); - genArgList.Add (underlyingTypeName); + genArgTypeList.Add (underlyingTypeName); } + var genArgList = genInst.GenericArguments.Select((_, index) => string.Format("{0}{1}", genArgTypeList[index], genArgNameList[index] == null ? String.Empty : (" " + genArgNameList[index]))); buf.Append (string.Join (",", genArgList)); buf.Append (")"); - return buf; } diff --git a/mdoc/Mono.Documentation/Updater/IAttributeParserContext.cs b/mdoc/Mono.Documentation/Updater/IAttributeParserContext.cs index aca6ba21..471c661b 100644 --- a/mdoc/Mono.Documentation/Updater/IAttributeParserContext.cs +++ b/mdoc/Mono.Documentation/Updater/IAttributeParserContext.cs @@ -5,5 +5,6 @@ void NextDynamicFlag(); bool IsDynamic(); bool IsNullable(); + string GetTupleElementName(); } } diff --git a/mdoc/mdoc.Test/FormatterTests.cs b/mdoc/mdoc.Test/FormatterTests.cs index ce1009fe..868def32 100644 --- a/mdoc/mdoc.Test/FormatterTests.cs +++ b/mdoc/mdoc.Test/FormatterTests.cs @@ -420,6 +420,39 @@ namespace mdoc.Test Assert.AreEqual(expectedSignature, methodSignature); } + [Test] + public void CSharpTupleNamesTypeTest() + { + var type = GetType(typeof(SampleClasses.TupleNamesTestClass<,>)); + var typeSignature = formatter.GetDeclaration(type); + Assert.AreEqual("public class TupleNamesTestClass<T1,T2> : IComparable<(T1,T2)>", typeSignature); + } + + [Test] + public void CSharpTupleNamesPropertyTest() + { + var property = GetProperty(typeof(SampleClasses.TupleNamesTestClass<,>), m => m.Name == "TuplePropertyType"); + var propertySignature = formatter.GetDeclaration(property); + Assert.AreEqual("public (int a,int b) TuplePropertyType { get; }", propertySignature); + } + + [Test] + public void CSharpTupleNamesFieldTest() + { + var field = GetField(GetType(typeof(SampleClasses.TupleNamesTestClass<,>)), "TupleField"); + var fieldSignature = formatter.GetDeclaration(field); + Assert.AreEqual("public (int a,int b,int c) TupleField;", fieldSignature); + } + + [TestCase("TupleMethod", "public (int a,int,int b) TupleMethod ((int,int) t1, (int b,int c,int d) t2, (int,int) t3);")] + [TestCase("RecursiveTupleMethod", "public ((int a,long b) c,int d) RecursiveTupleMethod ((((int a,long) b,string c) d,(int e,(float f,float g) h) i,int j) t);")] + public void CSharpTupleNamesMethodTest(string methodName, string expectedSignature) + { + var method = GetMethod(typeof(SampleClasses.TupleNamesTestClass<,>), m => m.Name == methodName); + var methodSignature = formatter.GetDeclaration(method); + Assert.AreEqual(expectedSignature, methodSignature); + } + #region Helper Methods string RealTypeName(string name){ switch (name) { diff --git a/mdoc/mdoc.Test/SampleClasses/TupleNamesTestClass.cs b/mdoc/mdoc.Test/SampleClasses/TupleNamesTestClass.cs new file mode 100644 index 00000000..29b3a1e6 --- /dev/null +++ b/mdoc/mdoc.Test/SampleClasses/TupleNamesTestClass.cs @@ -0,0 +1,20 @@ +using System; + +namespace mdoc.Test.SampleClasses +{ + public class TupleNamesTestClass<T1, T2> : IComparable<ValueTuple<T1, T2>> + { + public (int a, int b) TuplePropertyType { get; } + + public (int a, int b, int c) TupleField; + + public (int a, int, int b) TupleMethod((int, int) t1, (int b, int c, int d) t2, ValueTuple<int, int> t3) => (t1.Item1, t2.b, t3.Item2); + + public ((int a, long b) c, int d) RecursiveTupleMethod((((int a, long) b, string c) d, (int e, (float f, float g) h) i, int j) t) => (t.d.b, t.j); + + public int CompareTo((T1, T2) other) + { + throw new NotImplementedException(); + } + } +}
\ No newline at end of file |