Skip to content

Commit ecc3837

Browse files
authored
feat#550375: Support ref and ref readonly return (#611)
* Support ref and ref readonly return * Add test cases * Update test
1 parent 7199864 commit ecc3837

10 files changed

Lines changed: 92 additions & 54 deletions

File tree

mdoc/Consts.cs

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

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

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -517,18 +517,6 @@ protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefin
517517
if (method.IsFinal) modifiers += " sealed";
518518
if (modifiers == " virtual sealed") modifiers = "";
519519

520-
if ((method.ReturnType.IsRequiredModifier
521-
&& ((RequiredModifierType)method.ReturnType).ElementType.IsByReference)
522-
|| method.ReturnType.IsByReference)
523-
{
524-
modifiers += " ref";
525-
}
526-
527-
if (method.ReturnType.IsRequiredModifier && DocUtils.HasCustomAttribute(method.MethodReturnType, Consts.IsReadOnlyAttribute))
528-
{
529-
modifiers += " readonly";
530-
}
531-
532520
switch (method.Name)
533521
{
534522
case "op_Implicit":
@@ -542,6 +530,24 @@ protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefin
542530
return buf.Append (modifiers);
543531
}
544532

533+
protected override StringBuilder AppendRefTypeName(StringBuilder buf, ByReferenceType type, IAttributeParserContext context)
534+
{
535+
buf.Append("ref ");
536+
return base.AppendRefTypeName(buf, type, context);
537+
}
538+
539+
protected override StringBuilder AppendRequiredModifierTypeName(
540+
StringBuilder buf, RequiredModifierType type, IAttributeParserContext context)
541+
{
542+
if (type.ModifierType.FullName == Consts.InAttribute && type.ElementType is ByReferenceType refType)
543+
{
544+
buf.Append("ref readonly ");
545+
return _AppendTypeName(buf, refType.ElementType, context);
546+
}
547+
548+
return base.AppendRequiredModifierTypeName(buf, type, context);
549+
}
550+
545551
protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
546552
{
547553
if (method.IsGenericMethod ())
@@ -585,19 +591,23 @@ private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition meth
585591

586592
private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
587593
{
588-
if (parameter.ParameterType is ByReferenceType)
594+
TypeReference parameterType = parameter.ParameterType;
595+
596+
if (parameterType is ByReferenceType byReferenceType)
589597
{
590598
if (parameter.IsOut)
591599
{
592600
buf.Append ("out ");
593601
}
602+
else if(parameter.IsIn)
603+
{
604+
buf.Append("in ");
605+
}
594606
else
595607
{
596-
if (parameter.HasCustomAttributes && parameter.CustomAttributes.Any (ca => ca.AttributeType.Name == "IsReadOnlyAttribute"))
597-
buf.Append ("in ");
598-
else
599-
buf.Append ("ref ");
608+
buf.Append("ref ");
600609
}
610+
parameterType = byReferenceType.ElementType;
601611
}
602612

603613
if (parameter.HasCustomAttributes)
@@ -609,7 +619,7 @@ private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition pa
609619

610620
var context = AttributeParserContext.Create (parameter);
611621
var isNullableType = context.IsNullable ();
612-
buf.Append (GetTypeName (parameter.ParameterType, context));
622+
buf.Append (GetTypeName (parameterType, context));
613623
buf.Append (GetTypeNullableSymbol (parameter.ParameterType, isNullableType));
614624
buf.Append (" ");
615625
buf.Append (parameter.Name);
@@ -673,9 +683,6 @@ protected override string GetPropertyDeclaration (PropertyDefinition property)
673683
modifiers = "";
674684
buf.Append (modifiers).Append (' ');
675685

676-
if (property.PropertyType.IsByReference)
677-
buf.Append("ref ");
678-
679686
var context = AttributeParserContext.Create (property);
680687
var isNullableType = context.IsNullable ();
681688
var propertyReturnTypeName = GetTypeName (property.PropertyType, context);

mdoc/Mono.Documentation/Updater/Formatters/CppFormatters/CppFullMemberFormatter.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -776,10 +776,9 @@ protected override StringBuilder AppendArrayTypeName(StringBuilder buf, TypeRefe
776776
return buf;
777777
}
778778

779-
protected override StringBuilder AppendRefTypeName(StringBuilder buf, TypeReference type, IAttributeParserContext context)
779+
protected override StringBuilder AppendRefTypeName(StringBuilder buf, ByReferenceType type, IAttributeParserContext context)
780780
{
781-
TypeSpecification spec = type as TypeSpecification;
782-
_AppendTypeName(buf, spec != null ? spec.ElementType : type.GetElementType(), context);
781+
_AppendTypeName(buf, type.ElementType, context);
783782
AppendHat(buf, type);
784783
buf.Append(RefTypeModifier);
785784

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -613,10 +613,9 @@ protected override StringBuilder AppendGenericMethodConstraints(StringBuilder bu
613613
return buf;
614614
}
615615

616-
protected override StringBuilder AppendRefTypeName(StringBuilder buf, TypeReference type, IAttributeParserContext context)
616+
protected override StringBuilder AppendRefTypeName(StringBuilder buf, ByReferenceType type, IAttributeParserContext context)
617617
{
618-
ByReferenceType reftype = type as ByReferenceType;
619-
return AppendTypeName(buf, reftype?.ElementType, context);
618+
return AppendTypeName(buf, type.ElementType, context);
620619
}
621620

622621
protected override StringBuilder AppendModifiers(StringBuilder buf, MethodDefinition method)

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

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -143,24 +143,24 @@ protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type,
143143

144144
StringBuilder interimBuilder = new StringBuilder();
145145

146-
if (ShouldStripModFromTypeName && type is RequiredModifierType)
146+
if (ShouldStripModFromTypeName && type is RequiredModifierType requiredModifierType)
147147
{
148-
AppendRequiredModifierTypeName(interimBuilder, type as RequiredModifierType, context);
148+
AppendRequiredModifierTypeName(interimBuilder, requiredModifierType, context);
149149
return SetBuffer(buf, interimBuilder, useTypeProjection: useTypeProjection);
150150
}
151-
if (ShouldStripModFromTypeName && type is OptionalModifierType)
151+
if (ShouldStripModFromTypeName && type is OptionalModifierType optionalModifierType)
152152
{
153-
AppendOptionalModifierTypeName(interimBuilder, type as OptionalModifierType, context);
153+
AppendOptionalModifierTypeName(interimBuilder, optionalModifierType, context);
154154
return SetBuffer(buf, interimBuilder, useTypeProjection: useTypeProjection);
155155
}
156156
if (type is ArrayType)
157157
{
158158
AppendArrayTypeName(interimBuilder, type, context);
159159
return SetBuffer(buf, interimBuilder, useTypeProjection: useTypeProjection);
160160
}
161-
if (type is ByReferenceType)
161+
if (type is ByReferenceType byReferenceType)
162162
{
163-
AppendRefTypeName (interimBuilder, type, context);
163+
AppendRefTypeName (interimBuilder, byReferenceType, context);
164164
return SetBuffer(buf, interimBuilder, useTypeProjection: useTypeProjection);
165165
}
166166
if (type is PointerType)
@@ -290,10 +290,9 @@ protected virtual string RefTypeModifier
290290
get { return "@"; }
291291
}
292292

293-
protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, IAttributeParserContext context)
293+
protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, ByReferenceType type, IAttributeParserContext context)
294294
{
295-
TypeSpecification spec = type as TypeSpecification;
296-
return _AppendTypeName(buf, spec != null ? spec.ElementType : type.GetElementType(), context);
295+
return _AppendTypeName(buf, type.ElementType, context);
297296
}
298297

299298
protected virtual string PointerModifier

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@ class MsxdocSlashDocMemberFormatter : SlashDocMemberFormatter
77
{
88
public MsxdocSlashDocMemberFormatter(TypeMap map) : base(map) { }
99

10-
protected override StringBuilder AppendRefTypeName(StringBuilder buf, TypeReference type, IAttributeParserContext context)
10+
protected override StringBuilder AppendRefTypeName(StringBuilder buf, ByReferenceType type, IAttributeParserContext context)
1111
{
12-
TypeSpecification spec = type as TypeSpecification;
13-
return _AppendTypeName(buf, spec != null ? spec.ElementType : type.GetElementType(), context).Append(RefTypeModifier);
12+
return _AppendTypeName(buf, type.ElementType, context).Append(RefTypeModifier);
1413
}
1514
}
1615
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReferenc
8282
return buf;
8383
}
8484

85-
protected override StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, IAttributeParserContext context)
85+
protected override StringBuilder AppendRefTypeName (StringBuilder buf, ByReferenceType type, IAttributeParserContext context)
8686
{
8787
return base.AppendRefTypeName (buf, type, context).Append (RefTypeModifier);
8888
}

mdoc/mdoc.Test/BasicTests.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,15 @@ protected MethodDefinition GetMethod(Type type, string name)
8888
return GetMethod(GetType(type), i => i.Name == name);
8989
}
9090

91+
protected PropertyDefinition GetProperty(Type type, Func<PropertyDefinition, bool> query)
92+
{
93+
var properties = GetType(type).Properties;
94+
var member = properties.FirstOrDefault(query)?.Resolve();
95+
if (member == null)
96+
throw new Exception("Did not find the member in the test class");
97+
return member;
98+
}
99+
91100
protected Dictionary<string, List<MemberReference>> GetClassInterface(TypeDefinition type)
92101
{
93102
return DocUtils.GetImplementedMembersFingerprintLookup(type);

mdoc/mdoc.Test/FormatterTests.cs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -256,22 +256,28 @@ public void FuncParams()
256256
Assert.AreEqual("public void SomeMethod4 (out string a, T t, object b = default);", sig);
257257
}
258258

259-
[Test]
260-
public void CSharpReadonlyRefReturn()
259+
[TestCase(typeof(ReadonlyRefClass), "Ref", "public ref int Ref ();")]
260+
[TestCase(typeof(ReadonlyRefClass), "ReadonlyRef", "public ref readonly int ReadonlyRef ();")]
261+
[TestCase(typeof(ReadonlyRefClass), "RefInAndOutMethod", "public void RefInAndOutMethod (ref int a, in int b, out int c);")]
262+
[TestCase(typeof(GenericRefClass<>), "Ref", "public ref T Ref ();")]
263+
[TestCase(typeof(GenericRefClass<>), "ReadonlyRef", "public ref readonly T ReadonlyRef ();")]
264+
[TestCase(typeof(GenericRefClass<>), "RefInAndOutMethod", "public void RefInAndOutMethod (ref T a, in T b, out T c);")]
265+
public void CSharpRefReturnMethodTest(Type type, string methodName, string expectedSignature)
261266
{
262-
var member = GetMethod(typeof(ReadonlyRefClass), m => m.Name == "ReadonlyRef");
263-
var formatter = new CSharpFullMemberFormatter();
264-
var sig = formatter.GetDeclaration(member);
265-
Assert.AreEqual("public ref readonly int ReadonlyRef ();", sig);
267+
var member = GetMethod(type, m => m.Name == methodName);
268+
var actualSignature = formatter.GetDeclaration(member);
269+
Assert.AreEqual(expectedSignature, actualSignature);
266270
}
267271

268-
[Test]
269-
public void CSharpRefReturn()
272+
[TestCase(typeof(ReadonlyRefClass), "RefProperty", "public ref int RefProperty { get; }")]
273+
[TestCase(typeof(ReadonlyRefClass), "Item", "public ref readonly int this[int index] { get; }")]
274+
[TestCase(typeof(GenericRefClass<>), "RefProperty", "public ref T RefProperty { get; }")]
275+
[TestCase(typeof(GenericRefClass<>), "Item", "public ref readonly T this[int index] { get; }")]
276+
public void CSharpRefReturnPropertyTest(Type type, string propertyName, string expectedSignature)
270277
{
271-
var member = GetMethod(typeof(ReadonlyRefClass), m => m.Name == "Ref");
272-
var formatter = new CSharpFullMemberFormatter();
273-
var sig = formatter.GetDeclaration(member);
274-
Assert.AreEqual("public ref int Ref ();", sig);
278+
var member = GetProperty(type, p => p.Name == propertyName);
279+
var actualSignature = formatter.GetDeclaration(member);
280+
Assert.AreEqual(expectedSignature, actualSignature);
275281
}
276282

277283
[Test]

mdoc/mdoc.Test/SampleClasses/ReadonlyRefClass.cs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,27 @@
33
public class ReadonlyRefClass
44
{
55
int i;
6-
76
public ref int Ref() => ref i;
8-
public ref readonly int ReadonlyRef() => ref i;
7+
8+
public ref readonly int ReadonlyRef() => ref i;
9+
10+
public ref int RefProperty { get { return ref i; } }
11+
12+
public ref readonly int this[int index] => throw null;
13+
14+
public void RefInAndOutMethod(ref int a, in int b, out int c) => throw null;
15+
}
16+
17+
public class GenericRefClass<T>
18+
{
19+
public ref T Ref() => throw null;
20+
21+
public ref readonly T ReadonlyRef() => throw null;
22+
23+
public ref T RefProperty => throw null;
24+
25+
public ref readonly T this[int index] => throw null;
26+
27+
public void RefInAndOutMethod(ref T a, in T b, out T c) => throw null;
928
}
1029
}

0 commit comments

Comments
 (0)