diff options
author | Min Huang <huangmin@microsoft.com> | 2022-03-21 06:17:32 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-21 06:17:32 +0300 |
commit | b24bc75b10a6a2fb6987546b96559c5768ba7093 (patch) | |
tree | 8e59c30b7e0d070e69abb52a1ba8190d248cd22a | |
parent | 4cf39bde45ffdeddec87aad9edce7f2c0f78b9ee (diff) |
feat#550401:Support C# 9 nint/nuint (#620)
* Support C# 9 nint/nuint
* update
* update
* update
* update
* update
* update
* update
* update
* fix test cases
* fix test cases
* fix integration test
* add unit test
-rw-r--r-- | mdoc/Consts.cs | 1 | ||||
-rw-r--r-- | mdoc/Mono.Documentation/Updater/AttributeParserContext.cs | 39 | ||||
-rw-r--r-- | mdoc/Mono.Documentation/Updater/EmptyAttributeParserContext.cs | 5 | ||||
-rw-r--r-- | mdoc/Mono.Documentation/Updater/Formatters/AttributeFormatters/AttributeFormatter.cs | 36 | ||||
-rw-r--r-- | mdoc/Mono.Documentation/Updater/Formatters/CSharpFullMemberFormatter.cs | 15 | ||||
-rw-r--r-- | mdoc/Mono.Documentation/Updater/IAttributeParserContext.cs | 1 | ||||
-rw-r--r-- | mdoc/mdoc.Test/FormatterTests.cs | 31 | ||||
-rw-r--r-- | mdoc/mdoc.Test/MDocUpdaterTests.cs | 12 | ||||
-rw-r--r-- | mdoc/mdoc.Test/NullableReferenceTypesTests.cs | 16 | ||||
-rw-r--r-- | mdoc/mdoc.Test/SampleClasses/NativeIntClass.cs | 30 |
10 files changed, 148 insertions, 38 deletions
diff --git a/mdoc/Consts.cs b/mdoc/Consts.cs index e047f2af..cc165d1f 100644 --- a/mdoc/Consts.cs +++ b/mdoc/Consts.cs @@ -51,5 +51,6 @@ namespace Mono.Documentation public const string InAttribute = "System.Runtime.InteropServices.InAttribute"; public const string TupleElementNamesAttribute = "System.Runtime.CompilerServices.TupleElementNamesAttribute"; public const string IsExternalInit = "System.Runtime.CompilerServices.IsExternalInit"; + public const string NativeIntegerAttribute = "System.Runtime.CompilerServices.NativeIntegerAttribute"; } } diff --git a/mdoc/Mono.Documentation/Updater/AttributeParserContext.cs b/mdoc/Mono.Documentation/Updater/AttributeParserContext.cs index e294d7cb..a472ec10 100644 --- a/mdoc/Mono.Documentation/Updater/AttributeParserContext.cs +++ b/mdoc/Mono.Documentation/Updater/AttributeParserContext.cs @@ -12,10 +12,12 @@ namespace Mono.Documentation.Updater private int nullableAttributeIndex; private int dynamicAttributeIndex; private int tupleNameAttributeIndex; + private int nativeIntegerAttributeIndex; private ICustomAttributeProvider provider; private ReadOnlyCollection<bool?> nullableAttributeFlags; private ReadOnlyCollection<bool> dynamicAttributeFlags; private string[] tupleElementNames; + private bool[] nativeIntegerFlags; private AttributeParserContext(ICustomAttributeProvider provider) { @@ -24,6 +26,7 @@ namespace Mono.Documentation.Updater ReadDynamicAttribute(); ReadNullableAttribute(); ReadTupleElementNames(); + ReadNativeIntegerAttribute(); } private bool ExistsNullableAttribute @@ -82,6 +85,11 @@ namespace Mono.Documentation.Updater return (tupleElementNames == null || tupleNameAttributeIndex >= tupleElementNames.Length) ? null : tupleElementNames[tupleNameAttributeIndex++]; } + public bool IsNativeInteger() + { + return nativeIntegerFlags != null && nativeIntegerAttributeIndex < nativeIntegerFlags.Length && nativeIntegerFlags[nativeIntegerAttributeIndex++]; + } + private void ReadDynamicAttribute() { DynamicTypeProvider dynamicTypeProvider = new DynamicTypeProvider(provider); @@ -100,15 +108,28 @@ namespace Mono.Documentation.Updater 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(); - } - } + tupleElementNames = ReadCustomAttributeValue<string>(Consts.TupleElementNamesAttribute); + } + + private void ReadNativeIntegerAttribute() + { + nativeIntegerFlags = ReadCustomAttributeValue<bool>( + Consts.NativeIntegerAttribute, + () => new bool[] { true }); + } + + private T[] ReadCustomAttributeValue<T>(string attributeName, Func<T[]> init = null) + { + if (provider == null || !provider.HasCustomAttributes) return null; + + var customAttribute = provider.CustomAttributes.Where(attr => attr.AttributeType.FullName == attributeName).FirstOrDefault(); + + if (customAttribute == null) return null; + + if (!customAttribute.HasConstructorArguments) return init?.Invoke(); + + var constructorArgs = customAttribute.ConstructorArguments[0].Value as CustomAttributeArgument[]; + return constructorArgs?.Select(arg => (T)arg.Value).ToArray(); } } }
\ No newline at end of file diff --git a/mdoc/Mono.Documentation/Updater/EmptyAttributeParserContext.cs b/mdoc/Mono.Documentation/Updater/EmptyAttributeParserContext.cs index 71b638fd..c21a8222 100644 --- a/mdoc/Mono.Documentation/Updater/EmptyAttributeParserContext.cs +++ b/mdoc/Mono.Documentation/Updater/EmptyAttributeParserContext.cs @@ -31,5 +31,10 @@ { return null; } + + public bool IsNativeInteger() + { + return false; + } } } diff --git a/mdoc/Mono.Documentation/Updater/Formatters/AttributeFormatters/AttributeFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/AttributeFormatters/AttributeFormatter.cs index b8076d73..9bb23667 100644 --- a/mdoc/Mono.Documentation/Updater/Formatters/AttributeFormatters/AttributeFormatter.cs +++ b/mdoc/Mono.Documentation/Updater/Formatters/AttributeFormatters/AttributeFormatter.cs @@ -77,15 +77,6 @@ namespace Mono.Documentation.Updater.Formatters return false; } - TypeDefinition attrType = attribute.AttributeType as TypeDefinition; - if (attrType != null && !DocUtils.IsPublic(attrType) - || (FormatterManager.SlashdocFormatter.GetName(attribute.AttributeType) == null) - || Array.IndexOf(IgnorableAttributes, attribute.AttributeType.FullName) >= 0) - { - rval = null; - return false; - } - var fields = new List<string>(); for (int i = 0; i < attribute.ConstructorArguments.Count; ++i) @@ -127,11 +118,34 @@ namespace Mono.Documentation.Updater.Formatters private bool IsIgnoredAttribute(CustomAttribute customAttribute) { + var attrType = customAttribute.AttributeType; + + if (attrType == null) return true; + // An Obsolete attribute with a known string is added to all ref-like structs // https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/span-safety.md#metadata-representation-or-ref-like-structs - return customAttribute.AttributeType.FullName == typeof(ObsoleteAttribute).FullName + if (attrType.FullName == typeof(ObsoleteAttribute).FullName && customAttribute.HasConstructorArguments - && customAttribute.ConstructorArguments.First().Value.ToString() == Consts.RefTypeObsoleteString; + && customAttribute.ConstructorArguments.First().Value.ToString() == Consts.RefTypeObsoleteString) + { + return true; + } + + // Expose this attribute in ECMAXML to let ECMA2YML pick up + // https://ceapex.visualstudio.com/Engineering/_workitems/edit/550401 + if (attrType.FullName == Consts.NativeIntegerAttribute) + { + return false; + } + + var attrTypeDef = attrType as TypeDefinition; + if (attrTypeDef != null && !DocUtils.IsPublic(attrTypeDef) || (FormatterManager.SlashdocFormatter.GetName(attrType) == null) + || Array.IndexOf(IgnorableAttributes, attrType.FullName) >= 0) + { + return true; + } + + return false; } // FIXME: get TypeReferences instead of string comparison? diff --git a/mdoc/Mono.Documentation/Updater/Formatters/CSharpFullMemberFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/CSharpFullMemberFormatter.cs index 3a36a5e7..06368855 100644 --- a/mdoc/Mono.Documentation/Updater/Formatters/CSharpFullMemberFormatter.cs +++ b/mdoc/Mono.Documentation/Updater/Formatters/CSharpFullMemberFormatter.cs @@ -12,6 +12,12 @@ namespace Mono.Documentation.Updater.Formatters public CSharpFullMemberFormatter() : this(null) {} public CSharpFullMemberFormatter(TypeMap map) : base(map) { } + private static readonly Dictionary<string, string> NativeIntTypeMap = new Dictionary<string, string>() + { + { "System.IntPtr", "nint" }, + { "System.UIntPtr", "nuint" }, + }; + public override string Language { get { return "C#"; } @@ -25,7 +31,7 @@ namespace Mono.Documentation.Updater.Formatters return buf; } - protected virtual string GetCSharpType (string t) + protected virtual string GetCSharpType(string t) { // make sure there are no modifiers in the type string (add them back before returning) string typeToCompare = t; @@ -83,6 +89,11 @@ namespace Mono.Documentation.Updater.Formatters return base.AppendTypeName (buf, type, context); } + if (NativeIntTypeMap.TryGetValue(t, out string typeName) && context.IsNativeInteger()) + { + return buf.Append(typeName); + } + string s = GetCSharpType (t); if (s != null) { @@ -152,7 +163,7 @@ namespace Mono.Documentation.Updater.Formatters 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 (string.Join (", ", genArgList)); buf.Append (")"); return buf; } diff --git a/mdoc/Mono.Documentation/Updater/IAttributeParserContext.cs b/mdoc/Mono.Documentation/Updater/IAttributeParserContext.cs index 471c661b..21feede9 100644 --- a/mdoc/Mono.Documentation/Updater/IAttributeParserContext.cs +++ b/mdoc/Mono.Documentation/Updater/IAttributeParserContext.cs @@ -6,5 +6,6 @@ bool IsDynamic(); bool IsNullable(); string GetTupleElementName(); + bool IsNativeInteger(); } } diff --git a/mdoc/mdoc.Test/FormatterTests.cs b/mdoc/mdoc.Test/FormatterTests.cs index fd987dec..b39d10c4 100644 --- a/mdoc/mdoc.Test/FormatterTests.cs +++ b/mdoc/mdoc.Test/FormatterTests.cs @@ -297,7 +297,7 @@ namespace mdoc.Test var member = GetMethod(typeof(NullablesAndTuples), m => m.Name == "TupleReturn"); var formatter = new CSharpFullMemberFormatter(); var sig = formatter.GetDeclaration(member); - Assert.AreEqual("public (int,string) TupleReturn ();", sig); + Assert.AreEqual("public (int, string) TupleReturn ();", sig); } [Test] @@ -425,7 +425,7 @@ namespace mdoc.Test { var type = GetType(typeof(SampleClasses.TupleNamesTestClass<,>)); var typeSignature = formatter.GetDeclaration(type); - Assert.AreEqual("public class TupleNamesTestClass<T1,T2> : IComparable<(T1,T2)>", typeSignature); + Assert.AreEqual("public class TupleNamesTestClass<T1,T2> : IComparable<(T1, T2)>", typeSignature); } [Test] @@ -433,7 +433,7 @@ namespace mdoc.Test { 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); + Assert.AreEqual("public (int a, int b) TuplePropertyType { get; }", propertySignature); } [Test] @@ -441,11 +441,11 @@ namespace mdoc.Test { var field = GetField(GetType(typeof(SampleClasses.TupleNamesTestClass<,>)), "TupleField"); var fieldSignature = formatter.GetDeclaration(field); - Assert.AreEqual("public (int a,int b,int c) TupleField;", fieldSignature); + 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);")] + [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); @@ -464,6 +464,25 @@ namespace mdoc.Test Assert.AreEqual(expectedSignature, propertySignature); } + [Test] + public void CSharpNativeIntGenericTypeTest() + { + var type = GetType(typeof(SampleClasses.GenericNativeIntClass<>)); + var typeSignature = formatter.GetDeclaration(type); + Assert.AreEqual("public class GenericNativeIntClass<nint>", typeSignature); + } + + [TestCase("Method1", "public (nint, nuint) Method1 (nint a, nuint b, IntPtr c, UIntPtr d);")] + [TestCase("Method2", "public (nint, nuint) Method2 (List<nint> a, Dictionary<int,nuint> b);")] + [TestCase("Method3", "public (nint, nuint) Method3 ((nint, nuint) a, (nuint, IntPtr) b, (UIntPtr, string) c);")] + [TestCase("Method4", "public (((nint a, IntPtr) b, UIntPtr c) d, (nint e, (nuint f, IntPtr g) h) i) Method4 ();")] + public void CSharpNativeIntMethodTest(string methodName, string expectedSignature) + { + var method = GetMethod(typeof(SampleClasses.NativeIntClass), 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/MDocUpdaterTests.cs b/mdoc/mdoc.Test/MDocUpdaterTests.cs index 4e4a117c..6924df61 100644 --- a/mdoc/mdoc.Test/MDocUpdaterTests.cs +++ b/mdoc/mdoc.Test/MDocUpdaterTests.cs @@ -1,11 +1,9 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Reflection; using System.Xml; using mdoc.Test.SampleClasses; using Mono.Cecil; -using Mono.Cecil.Rocks; using Mono.Collections.Generic; using Mono.Documentation; using Mono.Documentation.Updater; @@ -32,6 +30,16 @@ namespace mdoc.Test } [Test] + public void Test_GetCustomAttributes_EmitNativeIntegerAttribute() + { + var method = GetMethod(typeof(SampleClasses.NativeIntClass), "Method1"); + static CustomAttribute GetNativeIntegerAttr(ParameterDefinition p) => p?.CustomAttributes.Where(attr => attr.AttributeType.FullName == Consts.NativeIntegerAttribute).FirstOrDefault(); + Assert.IsNotNull(GetNativeIntegerAttr(method.Parameters[0])); + Assert.IsTrue(formatter.TryGetAttributeString(GetNativeIntegerAttr(method.Parameters[0]), out string rval)); + Assert.IsNull(GetNativeIntegerAttr(method.Parameters[2])); + } + + [Test] public void Test_GetDocParameterType_CppGenericParameterType_ReturnsTypeWithGenericParameters() { var method = GetMethod(typeof(Cpp.GenericBase<>), "BaseMethod2"); diff --git a/mdoc/mdoc.Test/NullableReferenceTypesTests.cs b/mdoc/mdoc.Test/NullableReferenceTypesTests.cs index 5a166f47..f6cc3b40 100644 --- a/mdoc/mdoc.Test/NullableReferenceTypesTests.cs +++ b/mdoc/mdoc.Test/NullableReferenceTypesTests.cs @@ -36,10 +36,10 @@ namespace mdoc.Test [TestCase("Tuple<int,int>?", "NullableTupleOfValueType")] [TestCase("Tuple<int?,int?>", "TupleOfNullableValueType")] [TestCase("Tuple<int?,int?>?", "NullableTupleOfNullableValueType")] - [TestCase("(int,int)", "ValueTupleOfValueType")] - [TestCase("(int,int)?", "NullableValueTupleOfValueType")] - [TestCase("(int?,int?)", "ValueTupleOfNullableValueType")] - [TestCase("(int?,int?)?", "NullableValueTupleOfNullableValueType")] + [TestCase("(int, int)", "ValueTupleOfValueType")] + [TestCase("(int, int)?", "NullableValueTupleOfValueType")] + [TestCase("(int?, int?)", "ValueTupleOfNullableValueType")] + [TestCase("(int?, int?)?", "NullableValueTupleOfNullableValueType")] [TestCase("ICollection<int>", "InterfaceOfValueType")] [TestCase("ICollection<int>?", "NullableInterfaceOfValueType")] [TestCase("ICollection<int?>?", "NullableInterfaceOfNullableValueType")] @@ -83,10 +83,10 @@ namespace mdoc.Test [TestCase("Tuple<string,string>?", "NullableTupleOfReferenceType")] [TestCase("Tuple<string?,string?>", "TupleOfNullableReferenceType")] [TestCase("Tuple<string?,string?>?", "NullableTupleOfNullableReferenceType")] - [TestCase("(string,string)", "ValueTupleOfReferenceType")] - [TestCase("(string,string)?", "NullableValueTupleOfReferenceType")] - [TestCase("(string?,string?)", "ValueTupleOfNullableReferenceType")] - [TestCase("(string?,string?)?", "NullableValueTupleOfNullableReferenceType")] + [TestCase("(string, string)", "ValueTupleOfReferenceType")] + [TestCase("(string, string)?", "NullableValueTupleOfReferenceType")] + [TestCase("(string?, string?)", "ValueTupleOfNullableReferenceType")] + [TestCase("(string?, string?)?", "NullableValueTupleOfNullableReferenceType")] [TestCase("ICollection<string>", "InterfaceOfReferenceType")] [TestCase("ICollection<string>?", "NullableInterfaceOfReferenceType")] [TestCase("ICollection<string?>?", "NullableInterfaceOfNullableReferenceType")] diff --git a/mdoc/mdoc.Test/SampleClasses/NativeIntClass.cs b/mdoc/mdoc.Test/SampleClasses/NativeIntClass.cs new file mode 100644 index 00000000..19fed899 --- /dev/null +++ b/mdoc/mdoc.Test/SampleClasses/NativeIntClass.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; + +namespace mdoc.Test.SampleClasses +{ + public class NativeIntClass + { + public (nint, nuint) Method1(nint a, nuint b, IntPtr c, UIntPtr d) + { + return (a + c, b + d); + } + + public (nint, nuint) Method2(List<nint> a, Dictionary<int, nuint> b) + { + return (a[0], b[0]); + } + + public (nint, nuint) Method3((nint, nuint) a, (nuint, IntPtr) b, (UIntPtr, string) c) + { + return (a.Item1 + b.Item2, b.Item1 + c.Item1); + } + + public (((nint a, IntPtr) b, UIntPtr c) d, (nint e, (nuint f, IntPtr g) h) i) Method4() => throw null; + } + + public class GenericNativeIntClass<nint> + { + + } +}
\ No newline at end of file |