Skip to content

Commit c27c019

Browse files
authored
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
1 parent 637f821 commit c27c019

7 files changed

Lines changed: 87 additions & 3 deletions

File tree

mdoc/Consts.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,6 @@ public static class Consts
4949
public const string IsByRefLikeAttribute = "System.Runtime.CompilerServices.IsByRefLikeAttribute";
5050
public const string IsReadOnlyAttribute = "System.Runtime.CompilerServices.IsReadOnlyAttribute";
5151
public const string InAttribute = "System.Runtime.InteropServices.InAttribute";
52+
public const string TupleElementNamesAttribute = "System.Runtime.CompilerServices.TupleElementNamesAttribute";
5253
}
5354
}

mdoc/Mono.Documentation/Updater/AttributeParserContext.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,27 @@
33
using Mono.Documentation.Util;
44
using System;
55
using System.Collections.ObjectModel;
6+
using System.Linq;
67

78
namespace Mono.Documentation.Updater
89
{
910
public class AttributeParserContext : IAttributeParserContext
1011
{
1112
private int nullableAttributeIndex;
1213
private int dynamicAttributeIndex;
14+
private int tupleNameAttributeIndex;
1315
private ICustomAttributeProvider provider;
1416
private ReadOnlyCollection<bool?> nullableAttributeFlags;
1517
private ReadOnlyCollection<bool> dynamicAttributeFlags;
18+
private string[] tupleElementNames;
1619

1720
private AttributeParserContext(ICustomAttributeProvider provider)
1821
{
1922
this.provider = provider;
2023

2124
ReadDynamicAttribute();
2225
ReadNullableAttribute();
26+
ReadTupleElementNames();
2327
}
2428

2529
private bool ExistsNullableAttribute
@@ -73,6 +77,11 @@ public bool IsNullable()
7377
return false;
7478
}
7579

80+
public string GetTupleElementName()
81+
{
82+
return (tupleElementNames == null || tupleNameAttributeIndex >= tupleElementNames.Length) ? null : tupleElementNames[tupleNameAttributeIndex++];
83+
}
84+
7685
private void ReadDynamicAttribute()
7786
{
7887
DynamicTypeProvider dynamicTypeProvider = new DynamicTypeProvider(provider);
@@ -88,5 +97,18 @@ private void ReadNullableAttribute()
8897
NullableReferenceTypeProvider nullableReferenceTypeProvider = new NullableReferenceTypeProvider(provider);
8998
nullableAttributeFlags = new ReadOnlyCollection<bool?>(nullableReferenceTypeProvider.GetNullableReferenceTypeFlags());
9099
}
100+
101+
private void ReadTupleElementNames()
102+
{
103+
if (provider != null && provider.HasCustomAttributes)
104+
{
105+
var tupleNamesAttr = provider.CustomAttributes.Where(attr => attr.AttributeType.FullName == Consts.TupleElementNamesAttribute).FirstOrDefault();
106+
if (tupleNamesAttr != null)
107+
{
108+
var constructorArgs = tupleNamesAttr.ConstructorArguments.FirstOrDefault().Value as CustomAttributeArgument[];
109+
tupleElementNames = constructorArgs?.Select(arg => arg.Value as string).ToArray();
110+
}
111+
}
112+
}
91113
}
92114
}

mdoc/Mono.Documentation/Updater/EmptyAttributeParserContext.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,10 @@ public bool IsNullable()
2626
{
2727
return false;
2828
}
29+
30+
public string GetTupleElementName()
31+
{
32+
return null;
33+
}
2934
}
3035
}

mdoc/Mono.Documentation/Updater/Formatters/CSharpFullMemberFormatter.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,9 @@ protected override StringBuilder AppendSpecialGenericNullableValueTypeName (Stri
137137
if (genInst.Name.StartsWith ("ValueTuple`"))
138138
{
139139
buf.Append ("(");
140-
var genArgList = new List<string> ();
140+
var genArgTypeList = new List<string> ();
141+
// tuple element names should be traversed before recursion.
142+
var genArgNameList = genInst.GenericArguments.Select(arg => context.GetTupleElementName()).ToList();
141143
foreach (var item in genInst.GenericArguments)
142144
{
143145
var isNullableType = false;
@@ -147,11 +149,11 @@ protected override StringBuilder AppendSpecialGenericNullableValueTypeName (Stri
147149
}
148150

149151
var underlyingTypeName = GetTypeName (item, context, appendGeneric, useTypeProjection) + GetTypeNullableSymbol (item, isNullableType);
150-
genArgList.Add (underlyingTypeName);
152+
genArgTypeList.Add (underlyingTypeName);
151153
}
154+
var genArgList = genInst.GenericArguments.Select((_, index) => string.Format("{0}{1}", genArgTypeList[index], genArgNameList[index] == null ? String.Empty : (" " + genArgNameList[index])));
152155
buf.Append (string.Join (",", genArgList));
153156
buf.Append (")");
154-
155157
return buf;
156158
}
157159

mdoc/Mono.Documentation/Updater/IAttributeParserContext.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ public interface IAttributeParserContext
55
void NextDynamicFlag();
66
bool IsDynamic();
77
bool IsNullable();
8+
string GetTupleElementName();
89
}
910
}

mdoc/mdoc.Test/FormatterTests.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,39 @@ public void CSharpReadOnlyMemberStructTest(string methodName, string expectedSig
420420
Assert.AreEqual(expectedSignature, methodSignature);
421421
}
422422

423+
[Test]
424+
public void CSharpTupleNamesTypeTest()
425+
{
426+
var type = GetType(typeof(SampleClasses.TupleNamesTestClass<,>));
427+
var typeSignature = formatter.GetDeclaration(type);
428+
Assert.AreEqual("public class TupleNamesTestClass<T1,T2> : IComparable<(T1,T2)>", typeSignature);
429+
}
430+
431+
[Test]
432+
public void CSharpTupleNamesPropertyTest()
433+
{
434+
var property = GetProperty(typeof(SampleClasses.TupleNamesTestClass<,>), m => m.Name == "TuplePropertyType");
435+
var propertySignature = formatter.GetDeclaration(property);
436+
Assert.AreEqual("public (int a,int b) TuplePropertyType { get; }", propertySignature);
437+
}
438+
439+
[Test]
440+
public void CSharpTupleNamesFieldTest()
441+
{
442+
var field = GetField(GetType(typeof(SampleClasses.TupleNamesTestClass<,>)), "TupleField");
443+
var fieldSignature = formatter.GetDeclaration(field);
444+
Assert.AreEqual("public (int a,int b,int c) TupleField;", fieldSignature);
445+
}
446+
447+
[TestCase("TupleMethod", "public (int a,int,int b) TupleMethod ((int,int) t1, (int b,int c,int d) t2, (int,int) t3);")]
448+
[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);")]
449+
public void CSharpTupleNamesMethodTest(string methodName, string expectedSignature)
450+
{
451+
var method = GetMethod(typeof(SampleClasses.TupleNamesTestClass<,>), m => m.Name == methodName);
452+
var methodSignature = formatter.GetDeclaration(method);
453+
Assert.AreEqual(expectedSignature, methodSignature);
454+
}
455+
423456
#region Helper Methods
424457
string RealTypeName(string name){
425458
switch (name) {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System;
2+
3+
namespace mdoc.Test.SampleClasses
4+
{
5+
public class TupleNamesTestClass<T1, T2> : IComparable<ValueTuple<T1, T2>>
6+
{
7+
public (int a, int b) TuplePropertyType { get; }
8+
9+
public (int a, int b, int c) TupleField;
10+
11+
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);
12+
13+
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);
14+
15+
public int CompareTo((T1, T2) other)
16+
{
17+
throw new NotImplementedException();
18+
}
19+
}
20+
}

0 commit comments

Comments
 (0)