From 6194fd40bb81c783c237a0929ec16c2ea9ea45f8 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Sat, 16 May 2020 04:31:58 +0000 Subject: [PATCH 1/9] Add signature parser based on Roslyn doc comments --- .../Linker/SignatureGenerator.PartVisitor.cs | 207 ++++++ src/linker/Linker/SignatureGenerator.cs | 53 ++ src/linker/Linker/SignatureParser.cs | 661 ++++++++++++++++++ src/linker/Linker/TypeReferenceExtensions.cs | 44 ++ .../BaseMemberAssertionAttribute.cs | 9 + .../ExpectGeneratedStringAttribute.cs | 11 + .../ExpectNoParsedStringAttribute.cs | 11 + .../Assertions/ExpectParsedStringAttribute.cs | 11 + .../ExpectUniqueParsedStringAttribute.cs | 11 + .../Mono.Linker.Tests.csproj | 1 + .../Tests/SignatureParserTests.cs | 562 +++++++++++++++ 11 files changed, 1581 insertions(+) create mode 100644 src/linker/Linker/SignatureGenerator.PartVisitor.cs create mode 100644 src/linker/Linker/SignatureGenerator.cs create mode 100644 src/linker/Linker/SignatureParser.cs create mode 100644 test/Mono.Linker.Tests.Cases.Expectations/Assertions/BaseMemberAssertionAttribute.cs create mode 100644 test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectGeneratedStringAttribute.cs create mode 100644 test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectNoParsedStringAttribute.cs create mode 100644 test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectParsedStringAttribute.cs create mode 100644 test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectUniqueParsedStringAttribute.cs create mode 100644 test/Mono.Linker.Tests/Tests/SignatureParserTests.cs diff --git a/src/linker/Linker/SignatureGenerator.PartVisitor.cs b/src/linker/Linker/SignatureGenerator.PartVisitor.cs new file mode 100644 index 000000000000..715b5ed72c32 --- /dev/null +++ b/src/linker/Linker/SignatureGenerator.PartVisitor.cs @@ -0,0 +1,207 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using Mono.Cecil; + +namespace Mono.Linker +{ + + public sealed partial class SignatureGenerator + { + /// + /// A visitor that generates the part of the documentation comment after the initial type + /// and colon. + /// Adapted from Roslyn's DocumentattionCommentIDVisitor.PartVisitor: + /// https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/DocumentationComments/DocumentationCommentIDVisitor.PartVisitor.cs + /// + internal sealed class PartVisitor + { + internal static readonly PartVisitor Instance = new PartVisitor (); + + private PartVisitor () + { + } + + public void VisitArrayType (ArrayType arrayType, StringBuilder builder) + { + VisitTypeReference (arrayType.ElementType, builder); + + // Rank-one arrays are displayed different than rectangular arrays + if (arrayType.IsVector) { + builder.Append ("[]"); + } else { + // C# arrays only support zero lower bounds + if (arrayType.Dimensions[0].LowerBound != 0) + throw new NotImplementedException (); + builder.Append ("[0:"); + for (int i = 1; i < arrayType.Rank; i++) { + if (arrayType.Dimensions[0].LowerBound != 0) + throw new NotImplementedException (); + builder.Append (",0:"); + } + + builder.Append (']'); + } + } + + public void VisitField (FieldDefinition field, StringBuilder builder) + { + VisitTypeReference (field.DeclaringType, builder); + builder.Append ('.').Append (field.Name); + } + + private void VisitParameters (IEnumerable parameters, bool isVararg, StringBuilder builder) + { + builder.Append ('('); + bool needsComma = false; + + foreach (var parameter in parameters) { + if (needsComma) + builder.Append (','); + + // byrefs are tracked on the parameter type, not the parameter, + // so we don't have VisitParameter that Roslyn uses. + VisitTypeReference (parameter.ParameterType, builder); + needsComma = true; + } + + // note: the C# doc comment generator outputs an extra comma for varargs + // methods that also have fixed parameters + if (isVararg && needsComma) + builder.Append (','); + + builder.Append (')'); + } + + public void VisitMethodDefinition (MethodDefinition method, StringBuilder builder) + { + VisitTypeReference (method.DeclaringType, builder); + builder.Append ('.').Append (GetEscapedMetadataName (method)); + + if (method.HasGenericParameters) + builder.Append ("``").Append (method.GenericParameters.Count); + + if (method.HasParameters || (method.CallingConvention == MethodCallingConvention.VarArg)) + VisitParameters (method.Parameters, method.CallingConvention == MethodCallingConvention.VarArg, builder); + + if (method.Name == "op_Implicit" || method.Name == "op_Explicit") { + builder.Append ('~'); + VisitTypeReference (method.ReturnType, builder); + } + } + + public void VisitProperty (PropertyDefinition property, StringBuilder builder) + { + VisitTypeReference (property.DeclaringType, builder); + builder.Append ('.').Append (GetEscapedMetadataName (property)); + + if (property.Parameters.Count > 0) + VisitParameters (property.Parameters, false, builder); + } + + public void VisitEvent (EventDefinition evt, StringBuilder builder) + { + VisitTypeReference (evt.DeclaringType, builder); + builder.Append ('.').Append (GetEscapedMetadataName (evt)); + } + + public void VisitGenericParameter (GenericParameter genericParameter, StringBuilder builder) + { + Debug.Assert ((genericParameter.DeclaringMethod == null) != (genericParameter.DeclaringType == null)); + // Is this a type parameter on a type? + if (genericParameter.DeclaringMethod != null) { + builder.Append ("``"); + } else { + Debug.Assert (genericParameter.DeclaringType != null); + + // If the containing type is nested within other types. + // e.g. A.B.M(T t, U u, V v) should be M(`0, `1, ``0). + // Roslyn needs to add generic arities of parents, but the innermost type redeclares + // all generic parameters so we don't need to add them. + builder.Append ('`'); + } + + builder.Append (genericParameter.Position); + } + + public void VisitTypeReference (TypeReference typeReference, StringBuilder builder) + { + switch (typeReference) { + case ByReferenceType byReferenceType: + VisitByReferenceType (byReferenceType, builder); + return; + case PointerType pointerType: + VisitPointerType (pointerType, builder); + return; + case ArrayType arrayType: + VisitArrayType (arrayType, builder); + return; + case GenericParameter genericParameter: + VisitGenericParameter (genericParameter, builder); + return; + } + + if (typeReference.IsNested) { + VisitTypeReference (typeReference.GetInflatedDeclaringType (), builder); + builder.Append ('.'); + } + + if (!String.IsNullOrEmpty (typeReference.Namespace)) + builder.Append (typeReference.Namespace).Append ('.'); + + // This includes '`n' for mangled generic types + builder.Append (typeReference.Name); + + if (typeReference.HasGenericParameters || !typeReference.IsGenericInstance) + return; + + var genericInstance = typeReference as GenericInstanceType; + + // Compute arity counting only the newly-introduced generic parameters + var declaringType = genericInstance.DeclaringType; + var declaringArity = 0; + if (declaringType != null && declaringType.HasGenericParameters) + declaringArity = declaringType.GenericParameters.Count; + var totalArity = genericInstance.GenericArguments.Count; + var arity = totalArity - declaringArity; + + // Un-mangle the generic type name + var suffixLength = arity.ToString ().Length + 1; + builder.Remove (builder.Length - suffixLength, suffixLength); + + // Append type arguments excluding arguments for re-declared parent generic parameters + builder.Append ('{'); + bool needsComma = false; + for (int i = totalArity - arity; i < totalArity; ++i) { + if (needsComma) + builder.Append (','); + var typeArgument = genericInstance.GenericArguments[i]; + VisitTypeReference (typeArgument, builder); + needsComma = true; + } + builder.Append ('}'); + } + + public void VisitPointerType (PointerType pointerType, StringBuilder builder) + { + VisitTypeReference (pointerType.ElementType, builder); + builder.Append ('*'); + } + + public void VisitByReferenceType (ByReferenceType byReferenceType, StringBuilder builder) + { + VisitTypeReference (byReferenceType.ElementType, builder); + builder.Append ('@'); + } + + private static string GetEscapedMetadataName (IMemberDefinition member) + { + var name = member.Name.Replace ('.', '#'); + // Not sure if the following replacements are necessary, but + // they are included to match Roslyn. + return name.Replace ('<', '{').Replace ('>', '}'); + } + } + } +} \ No newline at end of file diff --git a/src/linker/Linker/SignatureGenerator.cs b/src/linker/Linker/SignatureGenerator.cs new file mode 100644 index 000000000000..57ae373b2282 --- /dev/null +++ b/src/linker/Linker/SignatureGenerator.cs @@ -0,0 +1,53 @@ +using System; +using System.Text; +using Mono.Cecil; + +#nullable enable + +namespace Mono.Linker +{ + /// + /// Generates a signature for a member, in the format used for C# Documentation Comments: + /// https://github.com/dotnet/csharplang/blob/master/spec/documentation-comments.md#id-string-format + /// Adapted from Roslyn's DocumentationCommentIDVisitor: + /// https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/DocumentationComments/DocumentationCommentIDVisitor.cs + /// + public sealed partial class SignatureGenerator + { + public static readonly SignatureGenerator Instance = new SignatureGenerator (); + + private SignatureGenerator () + { + } + + public void VisitMethod (MethodDefinition method, StringBuilder builder) + { + builder.Append ("M:"); + PartVisitor.Instance.VisitMethodDefinition (method, builder); + } + + public void VisitField (FieldDefinition field, StringBuilder builder) + { + builder.Append ("F:"); + PartVisitor.Instance.VisitField (field, builder); + } + + public void VisitEvent (EventDefinition evt, StringBuilder builder) + { + builder.Append ("E:"); + PartVisitor.Instance.VisitEvent (evt, builder); + } + + public void VisitProperty (PropertyDefinition property, StringBuilder builder) + { + builder.Append ("P:"); + PartVisitor.Instance.VisitProperty (property, builder); + } + + public void VisitTypeDefinition (TypeDefinition type, StringBuilder builder) + { + builder.Append ("T:"); + PartVisitor.Instance.VisitTypeReference (type, builder); + } + } +} \ No newline at end of file diff --git a/src/linker/Linker/SignatureParser.cs b/src/linker/Linker/SignatureParser.cs new file mode 100644 index 000000000000..5b2b81a056dd --- /dev/null +++ b/src/linker/Linker/SignatureParser.cs @@ -0,0 +1,661 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using System.Text; +using Mono.Cecil; +using Mono.Collections.Generic; + +namespace Mono.Linker +{ + /// + /// Parses a signature for a member, in the format used for C# Documentation Comments: + /// https://github.com/dotnet/csharplang/blob/master/spec/documentation-comments.md#id-string-format + /// Adapted from Roslyn's DocumentationCommentId: + /// https://github.com/dotnet/roslyn/blob/master/src/Compilers/Core/Portable/DocumentationCommentId.cs + /// + /// + /// Roslyn's API works with ISymbol, which represents a symbol exposed by the compiler. + /// a Symbol has information about the source language, name, metadata name, + /// containing scopes, visibility/accessibility, attributes, etc. + /// This API instead works with the Cecil OM. It can be used to refer to IL definitions + /// where the signature of a member can contain references to instantiated generics. + /// + public static class SignatureParser + { + public static ImmutableArray GetSymbolsForDeclarationId (string id, ModuleDefinition module) + { + if (id == null) + throw new ArgumentNullException (nameof (id)); + + if (module == null) + throw new ArgumentNullException (nameof (module)); + + var results = new List (); + Parser.ParseDeclaredSymbolId (id, module, results); + return results.ToImmutableArray (); + } + + private static class Parser + { + + enum MemberType + { + Type, + Method, + Field, + Property, + Event, + } + + public static bool ParseDeclaredSymbolId (string id, ModuleDefinition module, List results) + { + if (id == null) + return false; + + if (id.Length < 2) + return false; + + int index = 0; + results.Clear (); + ParseDeclaredId (id, ref index, module, results); + return results.Count > 0; + } + + private static void ParseDeclaredId (string id, ref int index, ModuleDefinition module, List results) + { + Debug.Assert (results.Count == 0); + var memberTypeChar = PeekNextChar (id, index); + MemberType memberType; + + switch (memberTypeChar) { + case 'E': + memberType = MemberType.Event; + break; + case 'F': + memberType = MemberType.Field; + break; + case 'M': + memberType = MemberType.Method; + break; + case 'N': + // We do not support namespaces, which do not exist in IL. + return; + case 'P': + memberType = MemberType.Property; + break; + case 'T': + memberType = MemberType.Type; + break; + default: + // Documentation comment id must start with E, F, M, P, or T + return; + } + + index++; + // Note: this allows leaving out the ':'. + if (PeekNextChar (id, index) == ':') + index++; + + // Roslyn resolves types by searching namespaces top-down. + // We don't have namespace info. Instead try treating each part of a + // dotted name as a type first, then as a namespace if it fails + // to resolve to a type. + TypeDefinition? containingType = null; + var nameBuilder = new StringBuilder (); + + string name; + int arity; + + // process dotted names + while (true) { + name = ParseName (id, ref index); + // if we are at the end of the dotted name and still haven't resolved it to + // a type, there are no results. + if (String.IsNullOrEmpty (name)) + return; + nameBuilder.Append (name); + arity = 0; + + // has type parameters? + if (PeekNextChar (id, index) == '`') { + index++; + + bool genericType = true; + + // method type parameters? + // note: this allows `` for type parameters + if (PeekNextChar (id, index) == '`') { + index++; + genericType = false; + } + + arity = ReadNextInteger (id, ref index); + + if (genericType) { + // We need to mangle generic type names but not generic method names. + nameBuilder.Append ('`'); + nameBuilder.Append (arity); + } + } + + // no more dots, so don't loop any more + if (PeekNextChar (id, index) != '.') + break; + + // must be a namespace or type since name continues after dot + index++; + + // try to resolve it as a type + var typeOrNamespaceName = nameBuilder.ToString (); + GetMatchingTypes (module, declaringType: containingType, name: typeOrNamespaceName, results: results); + Debug.Assert (results.Count <= 1); + if (results.Any ()) { + // the name resolved to a type + var result = results.Single (); + Debug.Assert (result is TypeDefinition); + // result becomes the new container + containingType = result as TypeDefinition; + nameBuilder.Clear (); + results.Clear (); + continue; + } + + // it didn't resolve as a type. + + // only types have arity. + if (arity > 0) + return; + + // treat it as a namespace and continue building up the type name + nameBuilder.Append ('.'); + } + + if (containingType == null && memberType != MemberType.Type) + return; + + var memberName = nameBuilder.ToString (); + + switch (memberType) { + case MemberType.Method: + GetMatchingMethods (id, ref index, containingType, memberName, arity, results); + break; + case MemberType.Type: + GetMatchingTypes (module, containingType, memberName, results); + break; + case MemberType.Property: + GetMatchingProperties (id, ref index, containingType, memberName, results); + break; + case MemberType.Event: + GetMatchingEvents (containingType, memberName, results); + break; + case MemberType.Field: + GetMatchingFields (containingType, memberName, results); + break; + } + } + } + + // Roslyn resolves types in a signature to their declaration by searching through namespaces. + // To avoid looking for types by name in all referenced assemblies, we just represent types + // that are part of a signature by their doc comment strings, and we check for matching + // strings when looking for matching member signatures. + private static string? ParseTypeSymbol (string id, ref int index, IGenericParameterProvider? typeParameterContext) + { + var results = new List (); + ParseTypeSymbol (id, ref index, typeParameterContext, results); + if (results.Count == 0) { + return null; + } else { + Debug.Assert (results.Count == 1); + return results[0]; + } + } + + private static void ParseTypeSymbol (string id, ref int index, IGenericParameterProvider? typeParameterContext, List results) + { + // Note: Roslyn has a special case that deviates from the language spec, which + // allows context expressions embedded in a type reference => : + // We do not support this special format. + + Debug.Assert (results.Count == 0); + + if (PeekNextChar (id, index) == '`') + ParseTypeParameterSymbol (id, ref index, typeParameterContext, results); + else + ParseNamedTypeSymbol (id, ref index, typeParameterContext, results); + + // apply any array or pointer constructions to results + var startIndex = index; + var endIndex = index; + + for (int i = 0; i < results.Count; i++) { + index = startIndex; + var typeReference = results[i]; + + while (true) { + if (PeekNextChar (id, index) == '[') { + var boundsStartIndex = index; + var bounds = ParseArrayBounds (id, ref index); + var boundsEndIndex = index; + Debug.Assert (bounds > 0); + // Instead of constructing a representation of the array bounds, we + // use the original input to represent the bounds, and later match it + // against the generated strings for types in signatures. + // This ensures that we will only resolve members with supported array bounds. + typeReference += id.Substring (boundsStartIndex, boundsEndIndex - boundsStartIndex); + continue; + } + + if (PeekNextChar (id, index) == '*') { + index++; + typeReference += '*'; + continue; + } + + break; + } + + if (PeekNextChar (id, index) == '@') { + index++; + typeReference += '@'; + } + + results[i] = typeReference; + endIndex = index; + } + + index = endIndex; + } + + private static void ParseTypeParameterSymbol (string id, ref int index, IGenericParameterProvider? typeParameterContext, List results) + { + // skip the first ` + Debug.Assert (PeekNextChar (id, index) == '`'); + index++; + + Debug.Assert ( + typeParameterContext == null || + (typeParameterContext is MethodDefinition && typeParameterContext.GenericParameterType == GenericParameterType.Method) || + (typeParameterContext is TypeDefinition && typeParameterContext.GenericParameterType == GenericParameterType.Type) + ); + + if (PeekNextChar (id, index) == '`') { + // `` means this is a method type parameter + index++; + var methodTypeParameterIndex = ReadNextInteger (id, ref index); + + if (typeParameterContext is MethodDefinition methodContext) { + var count = methodContext.HasGenericParameters ? methodContext.GenericParameters.Count : 0; + if (count > 0 && methodTypeParameterIndex < count) { + results.Add ($"``{methodTypeParameterIndex}"); + } + } + } else { + // regular type parameter + var typeParameterIndex = ReadNextInteger (id, ref index); + + var typeContext = typeParameterContext is MethodDefinition methodContext + ? methodContext.DeclaringType + : typeParameterContext as TypeDefinition; + + if (typeParameterIndex >= 0 || + typeParameterIndex < typeContext?.GenericParameters.Count) { + // No need to look at declaring types like Roslyn, because type parameters are redeclared. + results.Add ("`" + typeParameterIndex); + } + } + } + + private static void ParseNamedTypeSymbol (string id, ref int index, IGenericParameterProvider? typeParameterContext, List results) + { + Debug.Assert (results.Count == 0); + var nameBuilder = new StringBuilder (); + // loop for dotted names + while (true) { + var name = ParseName (id, ref index); + if (String.IsNullOrEmpty (name)) + return; + + nameBuilder.Append (name); + + List? typeArguments = null; + int arity = 0; + + // type arguments + if (PeekNextChar (id, index) == '{') { + typeArguments = new List (); + if (!ParseTypeArguments (id, ref index, typeParameterContext, typeArguments)) { + continue; + } + + arity = typeArguments.Count; + } + + if (arity != 0) { + Debug.Assert (typeArguments != null && typeArguments.Count != 0); + nameBuilder.Append ('{'); + bool needsComma = false; + foreach (var typeArg in typeArguments) { + if (needsComma) { + nameBuilder.Append (','); + } + nameBuilder.Append (typeArg); + needsComma = true; + } + nameBuilder.Append ('}'); + } + + if (PeekNextChar (id, index) != '.') + break; + + index++; + nameBuilder.Append ('.'); + } + + results.Add (nameBuilder.ToString ()); + } + + private static int ParseArrayBounds (string id, ref int index) + { + index++; // skip '[' + + int bounds = 0; + + while (true) { + // note: the actual bounds are ignored. + // C# only supports arrays with lower bound zero. + // size is not known. + + if (char.IsDigit (PeekNextChar (id, index))) + ReadNextInteger (id, ref index); + + if (PeekNextChar (id, index) == ':') { + index++; + + // note: the spec says that omitting both the lower bounds and the size + // should omit the ':' as well, but this allows for it in the input. + if (char.IsDigit (PeekNextChar (id, index))) + ReadNextInteger (id, ref index); + } + + bounds++; + + if (PeekNextChar (id, index) == ',') { + index++; + continue; + } + + break; + } + + // note: this allows leaving out the closing ']' + if (PeekNextChar (id, index) == ']') + index++; + + return bounds; + } + + private static bool ParseTypeArguments (string id, ref int index, IGenericParameterProvider? typeParameterContext, List typeArguments) + { + index++; // skip over { + + while (true) { + var type = ParseTypeSymbol (id, ref index, typeParameterContext); + + if (type == null) { + // if a type argument cannot be identified, argument list is no good + return false; + } + + // add first one + typeArguments.Add (type); + + if (PeekNextChar (id, index) == ',') { + index++; + continue; + } + + break; + } + + // note: this doesn't require closing } + if (PeekNextChar (id, index) == '}') { + index++; + } + + return true; + } + + private static void GetMatchingTypes (ModuleDefinition module, TypeDefinition? declaringType, string name, List results) + { + Debug.Assert (module != null); + + if (declaringType == null) { + var type = module.GetType (name); + if (type != null) { + results.Add (type); + } + return; + } + + if (!declaringType.HasNestedTypes) + return; + + foreach (var nestedType in declaringType.NestedTypes) { + Debug.Assert (String.IsNullOrEmpty (nestedType.Namespace)); + if (nestedType.Name != name) + continue; + results.Add (nestedType); + return; + } + } + + private static void GetMatchingMethods (string id, ref int index, TypeDefinition? type, string memberName, int arity, List results) + { + if (type == null) + return; + + var parameters = new List (); + var startIndex = index; + var endIndex = index; + + foreach (var method in type.Methods) { + index = startIndex; + if (method.Name != memberName) + continue; + + var methodArity = method.HasGenericParameters ? method.GenericParameters.Count : 0; + if (methodArity != arity) + continue; + + parameters.Clear (); + if (PeekNextChar (id, index) == '(') { + // if the parameters cannot be identified (some error), then the symbol cannot match, try next method symbol + if (!ParseParameterList (id, ref index, method, parameters)) + continue; + } + + if (!AllParametersMatch (method.Parameters, parameters)) + continue; + + if (PeekNextChar (id, index) == '~') { + index++; + string? returnType = ParseTypeSymbol (id, ref index, method); + if (returnType == null) + continue; + + // if return type is specified, then it must match + if (method.ReturnType.GetSignaturePart () == returnType) { + results.Add (method); + endIndex = index; + } + } else { + // no return type specified, then any matches + results.Add (method); + endIndex = index; + } + } + index = endIndex; + } + + private static void GetMatchingProperties (string id, ref int index, TypeDefinition? type, string memberName, List results) + { + if (type == null) + return; + + int startIndex = index; + int endIndex = index; + + List? parameters = null; + // Unlike Roslyn, we don't need to decode property names because we are working + // directly with IL. + foreach (var property in type.Properties) { + index = startIndex; + if (property.Name != memberName) + continue; + if (PeekNextChar (id, index) == '(') { + if (parameters == null) { + parameters = new List (); + } else { + parameters.Clear (); + } + + if (ParseParameterList (id, ref index, property.DeclaringType, parameters) + && AllParametersMatch (property.Parameters, parameters)) { + results.Add (property); + endIndex = index; + } + } else if (property.Parameters.Count == 0) { + results.Add (property); + endIndex = index; + } + } + + index = endIndex; + } + + private static void GetMatchingFields (TypeDefinition? type, string memberName, List results) + { + if (type == null) + return; + foreach (var field in type.Fields) { + if (field.Name != memberName) + continue; + results.Add (field); + } + } + + private static void GetMatchingEvents (TypeDefinition? type, string memberName, List results) + { + if (type == null) + return; + foreach (var evt in type.Events) { + if (evt.Name != memberName) + continue; + results.Add (evt); + } + } + + private static bool AllParametersMatch (Collection methodParameters, List expectedParameters) + { + if (methodParameters.Count != expectedParameters.Count) + return false; + + for (int i = 0; i < expectedParameters.Count; i++) { + if (methodParameters[i].ParameterType.GetSignaturePart () != expectedParameters[i]) + return false; + } + + return true; + } + + private static bool ParseParameterList (string id, ref int index, IGenericParameterProvider typeParameterContext, List parameters) + { + System.Diagnostics.Debug.Assert (typeParameterContext != null); + + index++; // skip over '(' + + if (PeekNextChar (id, index) == ')') { + // note: this will match parameterless methods, or methods with only varargs parameters + index++; + return true; + } + + string? parameter = ParseTypeSymbol (id, ref index, typeParameterContext); + if (parameter == null) + return false; + + parameters.Add (parameter); + + while (PeekNextChar (id, index) == ',') { + index++; + + parameter = ParseTypeSymbol (id, ref index, typeParameterContext); + if (parameter == null) + return false; + + parameters.Add (parameter); + } + + // note: this doesn't require the trailing ')' + if (PeekNextChar (id, index) == ')') { + index++; + } + + return true; + } + + private static char PeekNextChar (string id, int index) + { + return index >= id.Length ? '\0' : id[index]; + } + + private static readonly char[] s_nameDelimiters = { ':', '.', '(', ')', '{', '}', '[', ']', ',', '\'', '@', '*', '`', '~' }; + + private static string ParseName (string id, ref int index) + { + string name; + + int delimiterOffset = id.IndexOfAny (s_nameDelimiters, index); + if (delimiterOffset >= 0) { + name = id.Substring (index, delimiterOffset - index); + index = delimiterOffset; + } else { + name = id.Substring (index); + index = id.Length; + } + + return DecodeName (name); + } + + // undoes dot encodings within names... + private static string DecodeName (string name) + { + if (name.IndexOf ('#') >= 0) + return name.Replace ('#', '.'); + + return name; + } + + private static int ReadNextInteger (string id, ref int index) + { + int n = 0; + + // note: this can overflow + while (index < id.Length && char.IsDigit (id[index])) { + n = n * 10 + (id[index] - '0'); + index++; + } + + return n; + } + } +} \ No newline at end of file diff --git a/src/linker/Linker/TypeReferenceExtensions.cs b/src/linker/Linker/TypeReferenceExtensions.cs index 6910de04c0f0..3cce6f373583 100644 --- a/src/linker/Linker/TypeReferenceExtensions.cs +++ b/src/linker/Linker/TypeReferenceExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Text; using Mono.Cecil; using System.Collections.Generic; using System.Linq; @@ -36,6 +37,42 @@ public static TypeReference GetInflatedBaseType (this TypeReference type) return type.Resolve ()?.BaseType; } + public static TypeReference GetInflatedDeclaringType (this TypeReference type) + { + if (type == null) + return null; + + if (type.IsGenericParameter || type.IsByReference || type.IsPointer) + return null; + + if (type is SentinelType sentinelType) + return sentinelType.ElementType.GetInflatedDeclaringType (); + + if (type is PinnedType pinnedType) + return pinnedType.ElementType.GetInflatedDeclaringType (); + + if (type is RequiredModifierType requiredModifierType) + return requiredModifierType.ElementType.GetInflatedDeclaringType (); + + if (type is GenericInstanceType genericInstance) { + var declaringType = genericInstance.DeclaringType; + + if (declaringType.HasGenericParameters) { + var result = new GenericInstanceType (declaringType); + for (var i = 0; i < declaringType.GenericParameters.Count; ++i) + result.GenericArguments.Add (genericInstance.GenericArguments[i]); + + return result; + } + + return declaringType; + } + + var resolved = type.Resolve (); + System.Diagnostics.Debug.Assert (resolved == type); + return resolved?.DeclaringType; + } + public static IEnumerable GetInflatedInterfaces (this TypeReference typeRef) { var typeDef = typeRef.Resolve (); @@ -187,6 +224,13 @@ public static string ToCecilName (this string fullTypeName) return fullTypeName.Replace ('+', '/'); } + public static string GetSignaturePart (this TypeReference type) + { + var builder = new StringBuilder (); + SignatureGenerator.PartVisitor.Instance.VisitTypeReference (type, builder); + return builder.ToString (); + } + public static bool HasDefaultConstructor (this TypeReference type) { foreach (var m in type.GetMethods ()) { diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/BaseMemberAssertionAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/BaseMemberAssertionAttribute.cs new file mode 100644 index 000000000000..89309c6ba06c --- /dev/null +++ b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/BaseMemberAssertionAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions +{ + [AttributeUsage (AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Event | AttributeTargets.Delegate, AllowMultiple = true)] + public abstract class BaseMemberAssertionAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectGeneratedStringAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectGeneratedStringAttribute.cs new file mode 100644 index 000000000000..a6d41a69aaf0 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectGeneratedStringAttribute.cs @@ -0,0 +1,11 @@ +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions +{ + public class ExpectGeneratedStringAttribute : BaseMemberAssertionAttribute + { + public ExpectGeneratedStringAttribute (string expected) + { + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectNoParsedStringAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectNoParsedStringAttribute.cs new file mode 100644 index 000000000000..2bcab589cf09 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectNoParsedStringAttribute.cs @@ -0,0 +1,11 @@ +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions +{ + public class ExpectNoParsedStringAttribute : BaseMemberAssertionAttribute + { + public ExpectNoParsedStringAttribute (string expected) + { + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectParsedStringAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectParsedStringAttribute.cs new file mode 100644 index 000000000000..743936422690 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectParsedStringAttribute.cs @@ -0,0 +1,11 @@ +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions +{ + public class ExpectParsedStringAttribute : BaseMemberAssertionAttribute + { + public ExpectParsedStringAttribute (string input) + { + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectUniqueParsedStringAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectUniqueParsedStringAttribute.cs new file mode 100644 index 000000000000..79da16fcb2c8 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectUniqueParsedStringAttribute.cs @@ -0,0 +1,11 @@ +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions +{ + public class ExpectUniqueParsedStringAttribute : BaseMemberAssertionAttribute + { + public ExpectUniqueParsedStringAttribute (string input) + { + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj b/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj index 8255852b387f..be4a9a3e7283 100644 --- a/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj +++ b/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj @@ -3,6 +3,7 @@ latest + true diff --git a/test/Mono.Linker.Tests/Tests/SignatureParserTests.cs b/test/Mono.Linker.Tests/Tests/SignatureParserTests.cs new file mode 100644 index 000000000000..ccd19a600f6f --- /dev/null +++ b/test/Mono.Linker.Tests/Tests/SignatureParserTests.cs @@ -0,0 +1,562 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Linq; +using NUnit.Framework; +using Mono.Cecil; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests +{ + [TestFixture] + public class SignatureParserTests + { + [TestCaseSource (nameof (GetMemberAssertionsAsArray), new object[] { typeof (SignatureParserTests) })] + public void TestSignatureParsing (IMemberDefinition member, CustomAttribute customAttribute) + { + var attributeString = (string) customAttribute.ConstructorArguments[0].Value; + switch (customAttribute.AttributeType.Name) { + case nameof (ExpectUniqueParsedStringAttribute): + CheckUniqueParsedString (member, attributeString); + break; + case nameof (ExpectGeneratedStringAttribute): + CheckGeneratedString (member, attributeString); + break; + case nameof (ExpectParsedStringAttribute): + CheckParsedString (member, attributeString); + break; + case nameof (ExpectNoParsedStringAttribute): + CheckNoParsedString (member, attributeString); + break; + default: + throw new NotImplementedException (); + } + } + + public static void CheckUniqueParsedString (IMemberDefinition member, string input) + { + var module = (member as TypeDefinition)?.Module ?? member.DeclaringType?.Module; + Assert.NotNull (module); + var parseResults = SignatureParser.GetSymbolsForDeclarationId (input, module); + Assert.AreEqual (1, parseResults.Length); + Assert.AreEqual (member, parseResults.First ()); + } + + public static void CheckGeneratedString (IMemberDefinition member, string expected) + { + var generator = SignatureGenerator.Instance; + var builder = new StringBuilder (); + switch (member) { + case TypeDefinition type: + generator.VisitTypeDefinition (type, builder); + break; + case MethodDefinition method: + generator.VisitMethod (method, builder); + break; + case FieldDefinition field: + generator.VisitField (field, builder); + break; + case PropertyDefinition property: + generator.VisitProperty (property, builder); + break; + case EventDefinition evt: + generator.VisitEvent (evt, builder); + break; + default: + throw new NotImplementedException (); + } + Assert.AreEqual (expected, builder.ToString ()); + } + + public static void CheckParsedString (IMemberDefinition member, string input) + { + var module = (member as TypeDefinition)?.Module ?? member.DeclaringType?.Module; + Assert.NotNull (module); + var parseResults = SignatureParser.GetSymbolsForDeclarationId (input, module); + CollectionAssert.Contains (parseResults, member); + } + + public static void CheckNoParsedString (IMemberDefinition member, string input) + { + var module = (member as TypeDefinition)?.Module ?? member.DeclaringType?.Module; + Assert.NotNull (module); + var parseResults = SignatureParser.GetSymbolsForDeclarationId (input, module); + Assert.AreEqual (0, parseResults.Length); + } + + static IEnumerable<(IMemberDefinition member, CustomAttribute ca)> GetMemberAssertions (Type type) + { + var assembly = AssemblyDefinition.ReadAssembly (type.Assembly.Location); + var t = assembly.MainModule.GetType (type.Namespace + "." + type.Name); + Assert.NotNull (t); + var results = new List<(IMemberDefinition, CustomAttribute)> (); + CollectMemberAssertions (t, results); + return results; + } + + private static bool IsMemberAssertion (TypeReference attributeType) + { + if (attributeType == null) + return false; + + if (attributeType.Namespace == "Mono.Linker.Tests.Cases.Expectations.Assertions" && attributeType.Name == nameof (BaseMemberAssertionAttribute)) + return true; + + return IsMemberAssertion (attributeType.Resolve ()?.BaseType); + } + + private static void CollectMemberAssertions (TypeDefinition type, List<(IMemberDefinition, CustomAttribute)> results) + { + if (type.HasCustomAttributes) { + foreach (var ca in type.CustomAttributes) { + if (!IsMemberAssertion (ca.AttributeType)) + continue; + results.Add ((type, ca)); + } + } + + foreach (var m in type.Methods) { + if (!m.HasCustomAttributes) + continue; + + foreach (var ca in m.CustomAttributes) { + if (!IsMemberAssertion (ca.AttributeType)) + continue; + results.Add ((m, ca)); + } + } + + foreach (var f in type.Fields) { + if (!f.HasCustomAttributes) + continue; + + foreach (var ca in f.CustomAttributes) { + if (!IsMemberAssertion (ca.AttributeType)) + continue; + results.Add ((f, ca)); + } + } + + foreach (var p in type.Properties) { + if (!p.HasCustomAttributes) + continue; + + foreach (var ca in p.CustomAttributes) { + if (!IsMemberAssertion (ca.AttributeType)) + continue; + results.Add ((p, ca)); + } + } + + foreach (var e in type.Events) { + if (!e.HasCustomAttributes) + continue; + + foreach (var ca in e.CustomAttributes) { + if (!IsMemberAssertion (ca.AttributeType)) + continue; + results.Add ((e, ca)); + } + } + + if (!type.HasNestedTypes) + return; + + foreach (var nested in type.NestedTypes) { + CollectMemberAssertions (nested, results); + } + } + + static IEnumerable GetMemberAssertionsAsArray (Type type) + { + return GetMemberAssertions (type).Select (v => new object[] { v.member, v.ca }); + } + + // testcases + + [ExpectGeneratedString ("T:Mono.Linker.Tests.SignatureParserTests.A")] + [ExpectUniqueParsedString ("T:Mono.Linker.Tests.SignatureParserTests.A")] + public class A + { + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.#ctor")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.#ctor")] + public A () + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.#ctor(System.Int32)")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.#ctor(System.Int32)")] + public A (int a) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.#cctor")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.#cctor")] + static A () + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32[])")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32[])")] + public void M (int[] a) { + } + + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32,System.Int32,System.Int32)~System.Int32")] + public int M (int a, int b, int c) { + return 0; + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.MRef(System.Int32@)")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.MRef(System.Int32@)")] + public void MRef (ref int a) { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.MOut(System.Int32@)")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.MOut(System.Int32@)")] + public void MOut (out int a) + { + a = 5; + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.MIn(System.Int32@)")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.MIn(System.Int32@)")] + public void MIn (in int a) + { + } + + public static int i; + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.MRefReturn")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.MRefReturn")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.MRefReturn~System.Int32@")] + public ref int MRefReturn () + { + return ref i; + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M")] + [ExpectParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M")] // binds to both. + [ExpectParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M()")] // binds to both. + public void M () + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M()")] + [ExpectParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M")] + [ExpectParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M()")] + public void M (__arglist) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32[][])")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32[][])")] + public void M (int[][] a) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32[][0:,0:,0:])")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32[][0:,0:,0:])")] + public void M (int[,,][] a) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32[0:,0:])")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32[0:,0:])")] + public void M (int[,] a) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Object)")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Object)")] + public void M (dynamic d) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32*)")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32*)")] + public unsafe void M (int* a) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M``1(Mono.Linker.Tests.SignatureParserTests.S{Mono.Linker.Tests.SignatureParserTests.G{Mono.Linker.Tests.SignatureParserTests.A,``0}}**[0:,0:,0:][][][0:,0:]@)")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M``1(Mono.Linker.Tests.SignatureParserTests.S{Mono.Linker.Tests.SignatureParserTests.G{Mono.Linker.Tests.SignatureParserTests.A,``0}}**[0:,0:,0:][][][0:,0:]@)")] + public unsafe void M (ref S>**[,][][][,,] a) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Collections.Generic.List{System.Int32[]})")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Collections.Generic.List{System.Int32[]})")] + public void M (List a) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32,)")] + //[ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32,)")] + // there's no way to reference this, since the parsing logic doesn't like it. + public void M (int abo, __arglist) + { + } + + [ExpectGeneratedString ("P:Mono.Linker.Tests.SignatureParserTests.A.Prop")] + [ExpectUniqueParsedString ("P:Mono.Linker.Tests.SignatureParserTests.A.Prop")] + public int Prop { get; set; } + + [ExpectGeneratedString ("F:Mono.Linker.Tests.SignatureParserTests.A.field")] + [ExpectUniqueParsedString ("F:Mono.Linker.Tests.SignatureParserTests.A.field")] + public int field; + + + [ExpectGeneratedString ("E:Mono.Linker.Tests.SignatureParserTests.A.OnEvent")] + [ExpectUniqueParsedString ("E:Mono.Linker.Tests.SignatureParserTests.A.OnEvent")] + public event EventHandler OnEvent; + + [ExpectGeneratedString ("E:Mono.Linker.Tests.SignatureParserTests.A.OnEventInt")] + [ExpectUniqueParsedString ("E:Mono.Linker.Tests.SignatureParserTests.A.OnEventInt")] + public event Action OnEventInt; + + [ExpectGeneratedString ("T:Mono.Linker.Tests.SignatureParserTests.A.Del")] + [ExpectUniqueParsedString ("T:Mono.Linker.Tests.SignatureParserTests.A.Del")] + public delegate int Del (int a, int b); + + [ExpectGeneratedString ("E:Mono.Linker.Tests.SignatureParserTests.A.OnEventDel")] + [ExpectUniqueParsedString ("E:Mono.Linker.Tests.SignatureParserTests.A.OnEventDel")] + public event Del OnEventDel; + + // prevent warning about unused events + public void UseEvents () + { + OnEventDel?.Invoke (1, 2); + OnEventInt?.Invoke (1); + OnEvent?.Invoke (null, null); + } + + [ExpectGeneratedString ("P:Mono.Linker.Tests.SignatureParserTests.A.Item(System.Int32)")] + [ExpectUniqueParsedString ("P:Mono.Linker.Tests.SignatureParserTests.A.Item(System.Int32)")] + public int this[int i] { + get => 0; + set { } + } + + [ExpectGeneratedString ("P:Mono.Linker.Tests.SignatureParserTests.A.Item(System.String,System.Int32)")] + [ExpectUniqueParsedString ("P:Mono.Linker.Tests.SignatureParserTests.A.Item(System.String,System.Int32)")] + public int this[string s, int i] { + get => 0; + set { } + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.op_Implicit(Mono.Linker.Tests.SignatureParserTests.A)~System.Boolean")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.op_Implicit(Mono.Linker.Tests.SignatureParserTests.A)~System.Boolean")] + public static implicit operator bool (A a) => false; + + // C# will not generate a return type for this method, but we will. + // [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.op_Implicit(Mono.Linker.Tests.SignatureParserTests.A)~System.Boolean")] + // [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.op_Implicit(Mono.Linker.Tests.SignatureParserTests.A)~System.Boolean")] + //public static int op_Implicit (A a) => 0; + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.op_UnaryPlus(Mono.Linker.Tests.SignatureParserTests.A)")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.op_UnaryPlus(Mono.Linker.Tests.SignatureParserTests.A)")] + public static A operator + (A a) => null; + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.op_Addition(Mono.Linker.Tests.SignatureParserTests.A,Mono.Linker.Tests.SignatureParserTests.A)")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.op_Addition(Mono.Linker.Tests.SignatureParserTests.A,Mono.Linker.Tests.SignatureParserTests.A)")] + public static A operator + (A left, A right) => null; + } + + public struct S + { + } + + [ExpectGeneratedString ("T:Mono.Linker.Tests.SignatureParserTests.A`1")] + [ExpectUniqueParsedString ("T:Mono.Linker.Tests.SignatureParserTests.A`1")] + public class A + { + [ExpectGeneratedString ("P:Mono.Linker.Tests.SignatureParserTests.A`1.Item(`0)")] + [ExpectUniqueParsedString ("P:Mono.Linker.Tests.SignatureParserTests.A`1.Item(`0)")] + public int this[T t] { + get => 0; + set { } + } + } + + [ExpectUniqueParsedString ("T:Mono.Linker.Tests.SignatureParserTests.B")] + [ExpectGeneratedString ("T:Mono.Linker.Tests.SignatureParserTests.B")] + public class B + { + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.B.Method(Mono.Linker.Tests.SignatureParserTests.G{Mono.Linker.Tests.SignatureParserTests.A{Mono.Linker.Tests.SignatureParserTests.B},System.Collections.Generic.List{Mono.Linker.Tests.SignatureParserTests.A}})")] + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.B.Method(Mono.Linker.Tests.SignatureParserTests.G{Mono.Linker.Tests.SignatureParserTests.A{Mono.Linker.Tests.SignatureParserTests.B},System.Collections.Generic.List{Mono.Linker.Tests.SignatureParserTests.A}})")] + public void Method (G, List> l) + { + } + } + + [ExpectGeneratedString ("T:Mono.Linker.Tests.SignatureParserTests.G`2")] + [ExpectUniqueParsedString ("T:Mono.Linker.Tests.SignatureParserTests.G`2")] + public class G + { + [ExpectGeneratedString ("T:Mono.Linker.Tests.SignatureParserTests.G`2.NG`1")] + [ExpectUniqueParsedString ("T:Mono.Linker.Tests.SignatureParserTests.G`2.NG`1")] + public class NG + { + [ExpectGeneratedString ("T:Mono.Linker.Tests.SignatureParserTests.G`2.NG`1.NG2`1")] + [ExpectUniqueParsedString ("T:Mono.Linker.Tests.SignatureParserTests.G`2.NG`1.NG2`1")] + public class NG2 + { + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.G`2.NG`1.NG2`1.Method``1(`0,`1,`2,`3,``0)")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.G`2.NG`1.NG2`1.Method``1(`0,`1,`2,`3,``0)")] + public void Method (T t, U u, V v, W w, X x) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.G`2.NG`1.NG2`1.Method(Mono.Linker.Tests.SignatureParserTests.G{`0,`1}.NG{`2}.NG2{`3})")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.G`2.NG`1.NG2`1.Method(Mono.Linker.Tests.SignatureParserTests.G{`0,`1}.NG{`2}.NG2{`3})")] + public void Method (NG2 n) + { + } + } + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.G`2.Method(Mono.Linker.Tests.SignatureParserTests.G{`0,`1})")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.G`2.Method(Mono.Linker.Tests.SignatureParserTests.G{`0,`1})")] + public void Method (G g) + { + } + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.Method")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Method")] + public void Method () + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.Method(System.Int32)")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Method(System.Int32)")] + public void Method (int i) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.IntMethod")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.IntMethod")] + public int IntMethod () => 0; + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.Method(Mono.Linker.Tests.SignatureParserTests.G{Mono.Linker.Tests.SignatureParserTests.A,Mono.Linker.Tests.SignatureParserTests.A})")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Method(Mono.Linker.Tests.SignatureParserTests.G{Mono.Linker.Tests.SignatureParserTests.A,Mono.Linker.Tests.SignatureParserTests.A})")] + public void Method (G g) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.Method(Mono.Linker.Tests.SignatureParserTests.G{Mono.Linker.Tests.SignatureParserTests.A,Mono.Linker.Tests.SignatureParserTests.A}.NG{Mono.Linker.Tests.SignatureParserTests.A})")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Method(Mono.Linker.Tests.SignatureParserTests.G{Mono.Linker.Tests.SignatureParserTests.A,Mono.Linker.Tests.SignatureParserTests.A}.NG{Mono.Linker.Tests.SignatureParserTests.A})")] + public void Method (G.NG g) + { + } + + public class Invalid + { + [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.NoReturnType~")] + public int NoReturnType () => 0; + + [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.NoParameters(,)")] + public void NoParameters (int a, int b) + { + } + + [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.NoClosingParen(")] + public void NoClosingParen () { } + + [ExpectNoParsedString ("T:Mono.Linker.Tests.SignatureParserTests.Invalid.Whitespace ")] + [ExpectNoParsedString (" T:Mono.Linker.Tests.SignatureParserTests.Invalid.Whitespace")] + [ExpectNoParsedString ("T: Mono.Linker.Tests.SignatureParserTests.Invalid.Whitespace")] + [ExpectNoParsedString ("T :Mono.Linker.Tests.SignatureParserTests.Invalid.Whitespace")] + [ExpectNoParsedString ("")] + [ExpectNoParsedString (" ")] + public class Whitespace + { + [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.Whitespace.Method(System.Int32, System.Int32)")] + public void Method (int a, int b) + { + } + + [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.Whitespace.Method(Mono.Linker.Tests.SignatureParserTests.Invalid.Generic{System.Int32, System.Int32})")] + public void Method (Generic g) + { + } + } + + public class Generic + { + } + + [ExpectNoParsedString ("T:Mono.Linker.Tests.SignatureParserTests.Invalid.Generic{`1}")] + [ExpectNoParsedString ("T:Mono.Linker.Tests.SignatureParserTests.Invalid.Generic{T}")] + [ExpectNoParsedString ("T:Mono.Linker.Tests.SignatureParserTests.Invalid.Generic")] + public class Generic + { + [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.Generic``1.MethodSyntaxForTypeParameter(`0)")] + + public void MethodSyntaxForTypeParameter (T t) { + } + + [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.Generic`1.MethodSyntaxForTypeGenericArgument(Mono.Linker.Tests.SignatureParserTests.Invalid.Generic{``0})")] + public void MethodSyntaxForTypeGenericArgument (Generic g) { + } + + [ExpectNoParsedString ("P:Mono.Linker.Tests.SignatureParserTests.Invalid.Generic`1.Item(``0)")] + public bool this[T t] { + get => false; + set { } + } + } + + [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.MethodWithGenericInstantiation(Mono.Linker.Tests.SignatureParserTests.Invalid.Generic`1)")] + public void MethodWithGenericInstantiation (Generic g) { + } + + [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.Method(System.Int32[:,:])")] + [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.Method(System.Int32[0:,)")] + public void Method (int[,] a) { + } + + [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.NonGenericMethod(``0)")] + public void NonGenericMethod (int i) { + } + + [ExpectNoParsedString ("P:Mono.Linker.Tests.SignatureParserTests.Invalid.Item(`0)")] + [ExpectNoParsedString ("P:Mono.Linker.Tests.SignatureParserTests.Invalid.Item(``0)")] + public int this[int i] { + get => 0; + set { } + } + + [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.MethodMissingArgumentTypeName(System.)")] + public void MethodMissingArgumentTypeName (int i) { + } + + [ExpectNoParsedString ("T:Mono.Linker.Tests.SignatureParserTests.Invalid.")] + public class NoType { + } + + [ExpectNoParsedString ("T:Mono.Linker.Tests.SignatureParserTests.Invalid.NoParameterType()")] + public void NoParameterType (int i) { + } + + [ExpectNoParsedString ("T:Mono.Linker.Tests.SignatureParserTests.Invalid.NoParameterType(Mono.Linker.Tests.SignatureParserTests.Invalid.Generic{})")] + public void NoGenericParameterType (Generic g) { + } + + // these work, but seem like they shouldn't. + // see https://github.com/dotnet/roslyn/issues/44315 + + [ExpectUniqueParsedString ("TMono.Linker.Tests.SignatureParserTests.Invalid.NoColon")] + public class NoColon { + } + + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.NoClosingParenWithParameters(System.Int32")] + public void NoClosingParenWithParameters (int a) { + } + + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.NoClosingBrace(Mono.Linker.Tests.SignatureParserTests.Invalid.Generic{Mono.Linker.Tests.SignatureParserTests.A)")] + public void NoClosingBrace (Generic g) { + } + } + } +} \ No newline at end of file From 6481bba9a7a5d7d00936f82eb024145d8ba91173 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 18 May 2020 15:59:46 +0000 Subject: [PATCH 2/9] Fix formatting --- .../Tests/SignatureParserTests.cs | 49 ++++++++++++------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/test/Mono.Linker.Tests/Tests/SignatureParserTests.cs b/test/Mono.Linker.Tests/Tests/SignatureParserTests.cs index ccd19a600f6f..7bd8ad485188 100644 --- a/test/Mono.Linker.Tests/Tests/SignatureParserTests.cs +++ b/test/Mono.Linker.Tests/Tests/SignatureParserTests.cs @@ -198,17 +198,20 @@ static A () [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32[])")] [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32[])")] - public void M (int[] a) { + public void M (int[] a) + { } [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32,System.Int32,System.Int32)~System.Int32")] - public int M (int a, int b, int c) { + public int M (int a, int b, int c) + { return 0; } [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.MRef(System.Int32@)")] [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.MRef(System.Int32@)")] - public void MRef (ref int a) { + public void MRef (ref int a) + { } [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.MOut(System.Int32@)")] @@ -280,7 +283,7 @@ public unsafe void M (int* a) [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M``1(Mono.Linker.Tests.SignatureParserTests.S{Mono.Linker.Tests.SignatureParserTests.G{Mono.Linker.Tests.SignatureParserTests.A,``0}}**[0:,0:,0:][][][0:,0:]@)")] [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M``1(Mono.Linker.Tests.SignatureParserTests.S{Mono.Linker.Tests.SignatureParserTests.G{Mono.Linker.Tests.SignatureParserTests.A,``0}}**[0:,0:,0:][][][0:,0:]@)")] - public unsafe void M (ref S>**[,][][][,,] a) + public unsafe void M (ref S>**[,][][][,,] a) { } @@ -375,7 +378,7 @@ public class A public int this[T t] { get => 0; set { } - } + } } [ExpectUniqueParsedString ("T:Mono.Linker.Tests.SignatureParserTests.B")] @@ -493,11 +496,13 @@ public class Generic { [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.Generic``1.MethodSyntaxForTypeParameter(`0)")] - public void MethodSyntaxForTypeParameter (T t) { + public void MethodSyntaxForTypeParameter (T t) + { } [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.Generic`1.MethodSyntaxForTypeGenericArgument(Mono.Linker.Tests.SignatureParserTests.Invalid.Generic{``0})")] - public void MethodSyntaxForTypeGenericArgument (Generic g) { + public void MethodSyntaxForTypeGenericArgument (Generic g) + { } [ExpectNoParsedString ("P:Mono.Linker.Tests.SignatureParserTests.Invalid.Generic`1.Item(``0)")] @@ -508,16 +513,19 @@ public bool this[T t] { } [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.MethodWithGenericInstantiation(Mono.Linker.Tests.SignatureParserTests.Invalid.Generic`1)")] - public void MethodWithGenericInstantiation (Generic g) { + public void MethodWithGenericInstantiation (Generic g) + { } [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.Method(System.Int32[:,:])")] [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.Method(System.Int32[0:,)")] - public void Method (int[,] a) { + public void Method (int[,] a) + { } [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.NonGenericMethod(``0)")] - public void NonGenericMethod (int i) { + public void NonGenericMethod (int i) + { } [ExpectNoParsedString ("P:Mono.Linker.Tests.SignatureParserTests.Invalid.Item(`0)")] @@ -528,34 +536,41 @@ public int this[int i] { } [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.MethodMissingArgumentTypeName(System.)")] - public void MethodMissingArgumentTypeName (int i) { + public void MethodMissingArgumentTypeName (int i) + { } [ExpectNoParsedString ("T:Mono.Linker.Tests.SignatureParserTests.Invalid.")] - public class NoType { + public class NoType + { } [ExpectNoParsedString ("T:Mono.Linker.Tests.SignatureParserTests.Invalid.NoParameterType()")] - public void NoParameterType (int i) { + public void NoParameterType (int i) + { } [ExpectNoParsedString ("T:Mono.Linker.Tests.SignatureParserTests.Invalid.NoParameterType(Mono.Linker.Tests.SignatureParserTests.Invalid.Generic{})")] - public void NoGenericParameterType (Generic g) { + public void NoGenericParameterType (Generic g) + { } // these work, but seem like they shouldn't. // see https://github.com/dotnet/roslyn/issues/44315 [ExpectUniqueParsedString ("TMono.Linker.Tests.SignatureParserTests.Invalid.NoColon")] - public class NoColon { + public class NoColon + { } [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.NoClosingParenWithParameters(System.Int32")] - public void NoClosingParenWithParameters (int a) { + public void NoClosingParenWithParameters (int a) + { } [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.NoClosingBrace(Mono.Linker.Tests.SignatureParserTests.Invalid.Generic{Mono.Linker.Tests.SignatureParserTests.A)")] - public void NoClosingBrace (Generic g) { + public void NoClosingBrace (Generic g) + { } } } From 9deb28288ea50f622c65c00eade78056fc026ab9 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 18 May 2020 16:01:25 +0000 Subject: [PATCH 3/9] PR feedback - SignatureParser -> DocumentationSignatureParser - GetSignaturePart extension moved to DocumentationSignatureParser - Inverted a condition - Added a clarifying comment about generics --- ...entationSignatureGenerator.PartVisitor.cs} | 4 +- ....cs => DocumentationSignatureGenerator.cs} | 6 +- ...ser.cs => DocumentationSignatureParser.cs} | 20 +- src/linker/Linker/TypeReferenceExtensions.cs | 7 - .../DocumentationSignatureParserTests.cs | 577 ++++++++++++++++++ .../Tests/SignatureParserTests.cs | 577 ------------------ 6 files changed, 596 insertions(+), 595 deletions(-) rename src/linker/Linker/{SignatureGenerator.PartVisitor.cs => DocumentationSignatureGenerator.PartVisitor.cs} (97%) rename src/linker/Linker/{SignatureGenerator.cs => DocumentationSignatureGenerator.cs} (86%) rename src/linker/Linker/{SignatureParser.cs => DocumentationSignatureParser.cs} (97%) create mode 100644 test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs delete mode 100644 test/Mono.Linker.Tests/Tests/SignatureParserTests.cs diff --git a/src/linker/Linker/SignatureGenerator.PartVisitor.cs b/src/linker/Linker/DocumentationSignatureGenerator.PartVisitor.cs similarity index 97% rename from src/linker/Linker/SignatureGenerator.PartVisitor.cs rename to src/linker/Linker/DocumentationSignatureGenerator.PartVisitor.cs index 715b5ed72c32..f4bce27c1b7b 100644 --- a/src/linker/Linker/SignatureGenerator.PartVisitor.cs +++ b/src/linker/Linker/DocumentationSignatureGenerator.PartVisitor.cs @@ -7,7 +7,7 @@ namespace Mono.Linker { - public sealed partial class SignatureGenerator + public sealed partial class DocumentationSignatureGenerator { /// /// A visitor that generates the part of the documentation comment after the initial type @@ -153,6 +153,8 @@ public void VisitTypeReference (TypeReference typeReference, StringBuilder build // This includes '`n' for mangled generic types builder.Append (typeReference.Name); + // For uninstantiated generic types (we already built the mangled name) + // or non-generic types, we are done. if (typeReference.HasGenericParameters || !typeReference.IsGenericInstance) return; diff --git a/src/linker/Linker/SignatureGenerator.cs b/src/linker/Linker/DocumentationSignatureGenerator.cs similarity index 86% rename from src/linker/Linker/SignatureGenerator.cs rename to src/linker/Linker/DocumentationSignatureGenerator.cs index 57ae373b2282..4e20e2aa2db8 100644 --- a/src/linker/Linker/SignatureGenerator.cs +++ b/src/linker/Linker/DocumentationSignatureGenerator.cs @@ -12,11 +12,11 @@ namespace Mono.Linker /// Adapted from Roslyn's DocumentationCommentIDVisitor: /// https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/DocumentationComments/DocumentationCommentIDVisitor.cs /// - public sealed partial class SignatureGenerator + public sealed partial class DocumentationSignatureGenerator { - public static readonly SignatureGenerator Instance = new SignatureGenerator (); + public static readonly DocumentationSignatureGenerator Instance = new DocumentationSignatureGenerator (); - private SignatureGenerator () + private DocumentationSignatureGenerator () { } diff --git a/src/linker/Linker/SignatureParser.cs b/src/linker/Linker/DocumentationSignatureParser.cs similarity index 97% rename from src/linker/Linker/SignatureParser.cs rename to src/linker/Linker/DocumentationSignatureParser.cs index 5b2b81a056dd..433e2d12a71c 100644 --- a/src/linker/Linker/SignatureParser.cs +++ b/src/linker/Linker/DocumentationSignatureParser.cs @@ -28,7 +28,7 @@ namespace Mono.Linker /// This API instead works with the Cecil OM. It can be used to refer to IL definitions /// where the signature of a member can contain references to instantiated generics. /// - public static class SignatureParser + public static class DocumentationSignatureParser { public static ImmutableArray GetSymbolsForDeclarationId (string id, ModuleDefinition module) { @@ -40,7 +40,14 @@ public static ImmutableArray GetSymbolsForDeclarationId (stri var results = new List (); Parser.ParseDeclaredSymbolId (id, module, results); - return results.ToImmutableArray (); + return results.ToImmutableArray(); + } + + public static string GetSignaturePart (this TypeReference type) + { + var builder = new StringBuilder (); + DocumentationSignatureGenerator.PartVisitor.Instance.VisitTypeReference (type, builder); + return builder.ToString (); } private static class Parser @@ -211,12 +218,11 @@ private static void ParseDeclaredId (string id, ref int index, ModuleDefinition { var results = new List (); ParseTypeSymbol (id, ref index, typeParameterContext, results); - if (results.Count == 0) { - return null; - } else { - Debug.Assert (results.Count == 1); + if (results.Count == 1) return results[0]; - } + + Debug.Assert (results.Count == 0); + return null; } private static void ParseTypeSymbol (string id, ref int index, IGenericParameterProvider? typeParameterContext, List results) diff --git a/src/linker/Linker/TypeReferenceExtensions.cs b/src/linker/Linker/TypeReferenceExtensions.cs index 3cce6f373583..8498d788969d 100644 --- a/src/linker/Linker/TypeReferenceExtensions.cs +++ b/src/linker/Linker/TypeReferenceExtensions.cs @@ -224,13 +224,6 @@ public static string ToCecilName (this string fullTypeName) return fullTypeName.Replace ('+', '/'); } - public static string GetSignaturePart (this TypeReference type) - { - var builder = new StringBuilder (); - SignatureGenerator.PartVisitor.Instance.VisitTypeReference (type, builder); - return builder.ToString (); - } - public static bool HasDefaultConstructor (this TypeReference type) { foreach (var m in type.GetMethods ()) { diff --git a/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs b/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs new file mode 100644 index 000000000000..99e401bf9df0 --- /dev/null +++ b/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs @@ -0,0 +1,577 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Linq; +using NUnit.Framework; +using Mono.Cecil; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests +{ + [TestFixture] + public class DocumentationSignatureParserTests + { + [TestCaseSource (nameof (GetMemberAssertionsAsArray), new object[] { typeof (DocumentationSignatureParserTests) })] + public void TestSignatureParsing (IMemberDefinition member, CustomAttribute customAttribute) + { + var attributeString = (string) customAttribute.ConstructorArguments[0].Value; + switch (customAttribute.AttributeType.Name) { + case nameof (ExpectUniqueParsedStringAttribute): + CheckUniqueParsedString (member, attributeString); + break; + case nameof (ExpectGeneratedStringAttribute): + CheckGeneratedString (member, attributeString); + break; + case nameof (ExpectParsedStringAttribute): + CheckParsedString (member, attributeString); + break; + case nameof (ExpectNoParsedStringAttribute): + CheckNoParsedString (member, attributeString); + break; + default: + throw new NotImplementedException (); + } + } + + public static void CheckUniqueParsedString (IMemberDefinition member, string input) + { + var module = (member as TypeDefinition)?.Module ?? member.DeclaringType?.Module; + Assert.NotNull (module); + var parseResults = DocumentationSignatureParser.GetSymbolsForDeclarationId (input, module); + Assert.AreEqual (1, parseResults.Length); + Assert.AreEqual (member, parseResults.First ()); + } + + public static void CheckGeneratedString (IMemberDefinition member, string expected) + { + var generator = DocumentationSignatureGenerator.Instance; + var builder = new StringBuilder (); + switch (member) { + case TypeDefinition type: + generator.VisitTypeDefinition (type, builder); + break; + case MethodDefinition method: + generator.VisitMethod (method, builder); + break; + case FieldDefinition field: + generator.VisitField (field, builder); + break; + case PropertyDefinition property: + generator.VisitProperty (property, builder); + break; + case EventDefinition evt: + generator.VisitEvent (evt, builder); + break; + default: + throw new NotImplementedException (); + } + Assert.AreEqual (expected, builder.ToString ()); + } + + public static void CheckParsedString (IMemberDefinition member, string input) + { + var module = (member as TypeDefinition)?.Module ?? member.DeclaringType?.Module; + Assert.NotNull (module); + var parseResults = DocumentationSignatureParser.GetSymbolsForDeclarationId (input, module); + CollectionAssert.Contains (parseResults, member); + } + + public static void CheckNoParsedString (IMemberDefinition member, string input) + { + var module = (member as TypeDefinition)?.Module ?? member.DeclaringType?.Module; + Assert.NotNull (module); + var parseResults = DocumentationSignatureParser.GetSymbolsForDeclarationId (input, module); + Assert.AreEqual (0, parseResults.Length); + } + + static IEnumerable<(IMemberDefinition member, CustomAttribute ca)> GetMemberAssertions (Type type) + { + var assembly = AssemblyDefinition.ReadAssembly (type.Assembly.Location); + var t = assembly.MainModule.GetType (type.Namespace + "." + type.Name); + Assert.NotNull (t); + var results = new List<(IMemberDefinition, CustomAttribute)> (); + CollectMemberAssertions (t, results); + return results; + } + + private static bool IsMemberAssertion (TypeReference attributeType) + { + if (attributeType == null) + return false; + + if (attributeType.Namespace == "Mono.Linker.Tests.Cases.Expectations.Assertions" && attributeType.Name == nameof (BaseMemberAssertionAttribute)) + return true; + + return IsMemberAssertion (attributeType.Resolve ()?.BaseType); + } + + private static void CollectMemberAssertions (TypeDefinition type, List<(IMemberDefinition, CustomAttribute)> results) + { + if (type.HasCustomAttributes) { + foreach (var ca in type.CustomAttributes) { + if (!IsMemberAssertion (ca.AttributeType)) + continue; + results.Add ((type, ca)); + } + } + + foreach (var m in type.Methods) { + if (!m.HasCustomAttributes) + continue; + + foreach (var ca in m.CustomAttributes) { + if (!IsMemberAssertion (ca.AttributeType)) + continue; + results.Add ((m, ca)); + } + } + + foreach (var f in type.Fields) { + if (!f.HasCustomAttributes) + continue; + + foreach (var ca in f.CustomAttributes) { + if (!IsMemberAssertion (ca.AttributeType)) + continue; + results.Add ((f, ca)); + } + } + + foreach (var p in type.Properties) { + if (!p.HasCustomAttributes) + continue; + + foreach (var ca in p.CustomAttributes) { + if (!IsMemberAssertion (ca.AttributeType)) + continue; + results.Add ((p, ca)); + } + } + + foreach (var e in type.Events) { + if (!e.HasCustomAttributes) + continue; + + foreach (var ca in e.CustomAttributes) { + if (!IsMemberAssertion (ca.AttributeType)) + continue; + results.Add ((e, ca)); + } + } + + if (!type.HasNestedTypes) + return; + + foreach (var nested in type.NestedTypes) { + CollectMemberAssertions (nested, results); + } + } + + static IEnumerable GetMemberAssertionsAsArray (Type type) + { + return GetMemberAssertions (type).Select (v => new object[] { v.member, v.ca }); + } + + // testcases + + [ExpectGeneratedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.A")] + [ExpectUniqueParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.A")] + public class A + { + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.#ctor")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.#ctor")] + public A () + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.#ctor(System.Int32)")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.#ctor(System.Int32)")] + public A (int a) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.#cctor")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.#cctor")] + static A () + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32[])")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32[])")] + public void M (int[] a) + { + } + + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32,System.Int32,System.Int32)~System.Int32")] + public int M (int a, int b, int c) + { + return 0; + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MRef(System.Int32@)")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MRef(System.Int32@)")] + public void MRef (ref int a) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MOut(System.Int32@)")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MOut(System.Int32@)")] + public void MOut (out int a) + { + a = 5; + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MIn(System.Int32@)")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MIn(System.Int32@)")] + public void MIn (in int a) + { + } + + public static int i; + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MRefReturn")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MRefReturn")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MRefReturn~System.Int32@")] + public ref int MRefReturn () + { + return ref i; + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M")] + [ExpectParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M")] // binds to both. + [ExpectParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M()")] // binds to both. + public void M () + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M()")] + [ExpectParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M")] + [ExpectParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M()")] + public void M (__arglist) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32[][])")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32[][])")] + public void M (int[][] a) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32[][0:,0:,0:])")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32[][0:,0:,0:])")] + public void M (int[,,][] a) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32[0:,0:])")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32[0:,0:])")] + public void M (int[,] a) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Object)")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Object)")] + public void M (dynamic d) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32*)")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32*)")] + public unsafe void M (int* a) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M``1(Mono.Linker.Tests.DocumentationSignatureParserTests.S{Mono.Linker.Tests.DocumentationSignatureParserTests.G{Mono.Linker.Tests.DocumentationSignatureParserTests.A,``0}}**[0:,0:,0:][][][0:,0:]@)")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M``1(Mono.Linker.Tests.DocumentationSignatureParserTests.S{Mono.Linker.Tests.DocumentationSignatureParserTests.G{Mono.Linker.Tests.DocumentationSignatureParserTests.A,``0}}**[0:,0:,0:][][][0:,0:]@)")] + public unsafe void M (ref S>**[,][][][,,] a) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Collections.Generic.List{System.Int32[]})")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Collections.Generic.List{System.Int32[]})")] + public void M (List a) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32,)")] + //[ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32,)")] + // there's no way to reference this, since the parsing logic doesn't like it. + public void M (int abo, __arglist) + { + } + + [ExpectGeneratedString ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.A.Prop")] + [ExpectUniqueParsedString ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.A.Prop")] + public int Prop { get; set; } + + [ExpectGeneratedString ("F:Mono.Linker.Tests.DocumentationSignatureParserTests.A.field")] + [ExpectUniqueParsedString ("F:Mono.Linker.Tests.DocumentationSignatureParserTests.A.field")] + public int field; + + + [ExpectGeneratedString ("E:Mono.Linker.Tests.DocumentationSignatureParserTests.A.OnEvent")] + [ExpectUniqueParsedString ("E:Mono.Linker.Tests.DocumentationSignatureParserTests.A.OnEvent")] + public event EventHandler OnEvent; + + [ExpectGeneratedString ("E:Mono.Linker.Tests.DocumentationSignatureParserTests.A.OnEventInt")] + [ExpectUniqueParsedString ("E:Mono.Linker.Tests.DocumentationSignatureParserTests.A.OnEventInt")] + public event Action OnEventInt; + + [ExpectGeneratedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.A.Del")] + [ExpectUniqueParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.A.Del")] + public delegate int Del (int a, int b); + + [ExpectGeneratedString ("E:Mono.Linker.Tests.DocumentationSignatureParserTests.A.OnEventDel")] + [ExpectUniqueParsedString ("E:Mono.Linker.Tests.DocumentationSignatureParserTests.A.OnEventDel")] + public event Del OnEventDel; + + // prevent warning about unused events + public void UseEvents () + { + OnEventDel?.Invoke (1, 2); + OnEventInt?.Invoke (1); + OnEvent?.Invoke (null, null); + } + + [ExpectGeneratedString ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.A.Item(System.Int32)")] + [ExpectUniqueParsedString ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.A.Item(System.Int32)")] + public int this[int i] { + get => 0; + set { } + } + + [ExpectGeneratedString ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.A.Item(System.String,System.Int32)")] + [ExpectUniqueParsedString ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.A.Item(System.String,System.Int32)")] + public int this[string s, int i] { + get => 0; + set { } + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.op_Implicit(Mono.Linker.Tests.DocumentationSignatureParserTests.A)~System.Boolean")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.op_Implicit(Mono.Linker.Tests.DocumentationSignatureParserTests.A)~System.Boolean")] + public static implicit operator bool (A a) => false; + + // C# will not generate a return type for this method, but we will. + // [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.op_Implicit(Mono.Linker.Tests.DocumentationSignatureParserTests.A)~System.Boolean")] + // [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.op_Implicit(Mono.Linker.Tests.DocumentationSignatureParserTests.A)~System.Boolean")] + //public static int op_Implicit (A a) => 0; + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.op_UnaryPlus(Mono.Linker.Tests.DocumentationSignatureParserTests.A)")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.op_UnaryPlus(Mono.Linker.Tests.DocumentationSignatureParserTests.A)")] + public static A operator + (A a) => null; + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.op_Addition(Mono.Linker.Tests.DocumentationSignatureParserTests.A,Mono.Linker.Tests.DocumentationSignatureParserTests.A)")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.op_Addition(Mono.Linker.Tests.DocumentationSignatureParserTests.A,Mono.Linker.Tests.DocumentationSignatureParserTests.A)")] + public static A operator + (A left, A right) => null; + } + + public struct S + { + } + + [ExpectGeneratedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.A`1")] + [ExpectUniqueParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.A`1")] + public class A + { + [ExpectGeneratedString ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.A`1.Item(`0)")] + [ExpectUniqueParsedString ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.A`1.Item(`0)")] + public int this[T t] { + get => 0; + set { } + } + } + + [ExpectUniqueParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.B")] + [ExpectGeneratedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.B")] + public class B + { + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.B.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{Mono.Linker.Tests.DocumentationSignatureParserTests.A{Mono.Linker.Tests.DocumentationSignatureParserTests.B},System.Collections.Generic.List{Mono.Linker.Tests.DocumentationSignatureParserTests.A}})")] + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.B.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{Mono.Linker.Tests.DocumentationSignatureParserTests.A{Mono.Linker.Tests.DocumentationSignatureParserTests.B},System.Collections.Generic.List{Mono.Linker.Tests.DocumentationSignatureParserTests.A}})")] + public void Method (G, List> l) + { + } + } + + [ExpectGeneratedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2")] + [ExpectUniqueParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2")] + public class G + { + [ExpectGeneratedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.NG`1")] + [ExpectUniqueParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.NG`1")] + public class NG + { + [ExpectGeneratedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.NG`1.NG2`1")] + [ExpectUniqueParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.NG`1.NG2`1")] + public class NG2 + { + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.NG`1.NG2`1.Method``1(`0,`1,`2,`3,``0)")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.NG`1.NG2`1.Method``1(`0,`1,`2,`3,``0)")] + public void Method (T t, U u, V v, W w, X x) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.NG`1.NG2`1.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{`0,`1}.NG{`2}.NG2{`3})")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.NG`1.NG2`1.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{`0,`1}.NG{`2}.NG2{`3})")] + public void Method (NG2 n) + { + } + } + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{`0,`1})")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{`0,`1})")] + public void Method (G g) + { + } + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Method")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Method")] + public void Method () + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Method(System.Int32)")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Method(System.Int32)")] + public void Method (int i) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.IntMethod")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.IntMethod")] + public int IntMethod () => 0; + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{Mono.Linker.Tests.DocumentationSignatureParserTests.A,Mono.Linker.Tests.DocumentationSignatureParserTests.A})")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{Mono.Linker.Tests.DocumentationSignatureParserTests.A,Mono.Linker.Tests.DocumentationSignatureParserTests.A})")] + public void Method (G g) + { + } + + [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{Mono.Linker.Tests.DocumentationSignatureParserTests.A,Mono.Linker.Tests.DocumentationSignatureParserTests.A}.NG{Mono.Linker.Tests.DocumentationSignatureParserTests.A})")] + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{Mono.Linker.Tests.DocumentationSignatureParserTests.A,Mono.Linker.Tests.DocumentationSignatureParserTests.A}.NG{Mono.Linker.Tests.DocumentationSignatureParserTests.A})")] + public void Method (G.NG g) + { + } + + public class Invalid + { + [ExpectNoParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoReturnType~")] + public int NoReturnType () => 0; + + [ExpectNoParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoParameters(,)")] + public void NoParameters (int a, int b) + { + } + + [ExpectNoParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoClosingParen(")] + public void NoClosingParen () { } + + [ExpectNoParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Whitespace ")] + [ExpectNoParsedString (" T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Whitespace")] + [ExpectNoParsedString ("T: Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Whitespace")] + [ExpectNoParsedString ("T :Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Whitespace")] + [ExpectNoParsedString ("")] + [ExpectNoParsedString (" ")] + public class Whitespace + { + [ExpectNoParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Whitespace.Method(System.Int32, System.Int32)")] + public void Method (int a, int b) + { + } + + [ExpectNoParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Whitespace.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic{System.Int32, System.Int32})")] + public void Method (Generic g) + { + } + } + + public class Generic + { + } + + [ExpectNoParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic{`1}")] + [ExpectNoParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic{T}")] + [ExpectNoParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic")] + public class Generic + { + [ExpectNoParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic``1.MethodSyntaxForTypeParameter(`0)")] + + public void MethodSyntaxForTypeParameter (T t) + { + } + + [ExpectNoParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic`1.MethodSyntaxForTypeGenericArgument(Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic{``0})")] + public void MethodSyntaxForTypeGenericArgument (Generic g) + { + } + + [ExpectNoParsedString ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic`1.Item(``0)")] + public bool this[T t] { + get => false; + set { } + } + } + + [ExpectNoParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.MethodWithGenericInstantiation(Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic`1)")] + public void MethodWithGenericInstantiation (Generic g) + { + } + + [ExpectNoParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Method(System.Int32[:,:])")] + [ExpectNoParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Method(System.Int32[0:,)")] + public void Method (int[,] a) + { + } + + [ExpectNoParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NonGenericMethod(``0)")] + public void NonGenericMethod (int i) + { + } + + [ExpectNoParsedString ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Item(`0)")] + [ExpectNoParsedString ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Item(``0)")] + public int this[int i] { + get => 0; + set { } + } + + [ExpectNoParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.MethodMissingArgumentTypeName(System.)")] + public void MethodMissingArgumentTypeName (int i) + { + } + + [ExpectNoParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.")] + public class NoType + { + } + + [ExpectNoParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoParameterType()")] + public void NoParameterType (int i) + { + } + + [ExpectNoParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoParameterType(Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic{})")] + public void NoGenericParameterType (Generic g) + { + } + + // these work, but seem like they shouldn't. + // see https://github.com/dotnet/roslyn/issues/44315 + + [ExpectUniqueParsedString ("TMono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoColon")] + public class NoColon + { + } + + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoClosingParenWithParameters(System.Int32")] + public void NoClosingParenWithParameters (int a) + { + } + + [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoClosingBrace(Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic{Mono.Linker.Tests.DocumentationSignatureParserTests.A)")] + public void NoClosingBrace (Generic g) + { + } + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests/Tests/SignatureParserTests.cs b/test/Mono.Linker.Tests/Tests/SignatureParserTests.cs deleted file mode 100644 index 7bd8ad485188..000000000000 --- a/test/Mono.Linker.Tests/Tests/SignatureParserTests.cs +++ /dev/null @@ -1,577 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Linq; -using NUnit.Framework; -using Mono.Cecil; -using Mono.Linker.Tests.Cases.Expectations.Assertions; - -namespace Mono.Linker.Tests -{ - [TestFixture] - public class SignatureParserTests - { - [TestCaseSource (nameof (GetMemberAssertionsAsArray), new object[] { typeof (SignatureParserTests) })] - public void TestSignatureParsing (IMemberDefinition member, CustomAttribute customAttribute) - { - var attributeString = (string) customAttribute.ConstructorArguments[0].Value; - switch (customAttribute.AttributeType.Name) { - case nameof (ExpectUniqueParsedStringAttribute): - CheckUniqueParsedString (member, attributeString); - break; - case nameof (ExpectGeneratedStringAttribute): - CheckGeneratedString (member, attributeString); - break; - case nameof (ExpectParsedStringAttribute): - CheckParsedString (member, attributeString); - break; - case nameof (ExpectNoParsedStringAttribute): - CheckNoParsedString (member, attributeString); - break; - default: - throw new NotImplementedException (); - } - } - - public static void CheckUniqueParsedString (IMemberDefinition member, string input) - { - var module = (member as TypeDefinition)?.Module ?? member.DeclaringType?.Module; - Assert.NotNull (module); - var parseResults = SignatureParser.GetSymbolsForDeclarationId (input, module); - Assert.AreEqual (1, parseResults.Length); - Assert.AreEqual (member, parseResults.First ()); - } - - public static void CheckGeneratedString (IMemberDefinition member, string expected) - { - var generator = SignatureGenerator.Instance; - var builder = new StringBuilder (); - switch (member) { - case TypeDefinition type: - generator.VisitTypeDefinition (type, builder); - break; - case MethodDefinition method: - generator.VisitMethod (method, builder); - break; - case FieldDefinition field: - generator.VisitField (field, builder); - break; - case PropertyDefinition property: - generator.VisitProperty (property, builder); - break; - case EventDefinition evt: - generator.VisitEvent (evt, builder); - break; - default: - throw new NotImplementedException (); - } - Assert.AreEqual (expected, builder.ToString ()); - } - - public static void CheckParsedString (IMemberDefinition member, string input) - { - var module = (member as TypeDefinition)?.Module ?? member.DeclaringType?.Module; - Assert.NotNull (module); - var parseResults = SignatureParser.GetSymbolsForDeclarationId (input, module); - CollectionAssert.Contains (parseResults, member); - } - - public static void CheckNoParsedString (IMemberDefinition member, string input) - { - var module = (member as TypeDefinition)?.Module ?? member.DeclaringType?.Module; - Assert.NotNull (module); - var parseResults = SignatureParser.GetSymbolsForDeclarationId (input, module); - Assert.AreEqual (0, parseResults.Length); - } - - static IEnumerable<(IMemberDefinition member, CustomAttribute ca)> GetMemberAssertions (Type type) - { - var assembly = AssemblyDefinition.ReadAssembly (type.Assembly.Location); - var t = assembly.MainModule.GetType (type.Namespace + "." + type.Name); - Assert.NotNull (t); - var results = new List<(IMemberDefinition, CustomAttribute)> (); - CollectMemberAssertions (t, results); - return results; - } - - private static bool IsMemberAssertion (TypeReference attributeType) - { - if (attributeType == null) - return false; - - if (attributeType.Namespace == "Mono.Linker.Tests.Cases.Expectations.Assertions" && attributeType.Name == nameof (BaseMemberAssertionAttribute)) - return true; - - return IsMemberAssertion (attributeType.Resolve ()?.BaseType); - } - - private static void CollectMemberAssertions (TypeDefinition type, List<(IMemberDefinition, CustomAttribute)> results) - { - if (type.HasCustomAttributes) { - foreach (var ca in type.CustomAttributes) { - if (!IsMemberAssertion (ca.AttributeType)) - continue; - results.Add ((type, ca)); - } - } - - foreach (var m in type.Methods) { - if (!m.HasCustomAttributes) - continue; - - foreach (var ca in m.CustomAttributes) { - if (!IsMemberAssertion (ca.AttributeType)) - continue; - results.Add ((m, ca)); - } - } - - foreach (var f in type.Fields) { - if (!f.HasCustomAttributes) - continue; - - foreach (var ca in f.CustomAttributes) { - if (!IsMemberAssertion (ca.AttributeType)) - continue; - results.Add ((f, ca)); - } - } - - foreach (var p in type.Properties) { - if (!p.HasCustomAttributes) - continue; - - foreach (var ca in p.CustomAttributes) { - if (!IsMemberAssertion (ca.AttributeType)) - continue; - results.Add ((p, ca)); - } - } - - foreach (var e in type.Events) { - if (!e.HasCustomAttributes) - continue; - - foreach (var ca in e.CustomAttributes) { - if (!IsMemberAssertion (ca.AttributeType)) - continue; - results.Add ((e, ca)); - } - } - - if (!type.HasNestedTypes) - return; - - foreach (var nested in type.NestedTypes) { - CollectMemberAssertions (nested, results); - } - } - - static IEnumerable GetMemberAssertionsAsArray (Type type) - { - return GetMemberAssertions (type).Select (v => new object[] { v.member, v.ca }); - } - - // testcases - - [ExpectGeneratedString ("T:Mono.Linker.Tests.SignatureParserTests.A")] - [ExpectUniqueParsedString ("T:Mono.Linker.Tests.SignatureParserTests.A")] - public class A - { - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.#ctor")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.#ctor")] - public A () - { - } - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.#ctor(System.Int32)")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.#ctor(System.Int32)")] - public A (int a) - { - } - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.#cctor")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.#cctor")] - static A () - { - } - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32[])")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32[])")] - public void M (int[] a) - { - } - - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32,System.Int32,System.Int32)~System.Int32")] - public int M (int a, int b, int c) - { - return 0; - } - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.MRef(System.Int32@)")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.MRef(System.Int32@)")] - public void MRef (ref int a) - { - } - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.MOut(System.Int32@)")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.MOut(System.Int32@)")] - public void MOut (out int a) - { - a = 5; - } - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.MIn(System.Int32@)")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.MIn(System.Int32@)")] - public void MIn (in int a) - { - } - - public static int i; - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.MRefReturn")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.MRefReturn")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.MRefReturn~System.Int32@")] - public ref int MRefReturn () - { - return ref i; - } - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M")] - [ExpectParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M")] // binds to both. - [ExpectParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M()")] // binds to both. - public void M () - { - } - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M()")] - [ExpectParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M")] - [ExpectParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M()")] - public void M (__arglist) - { - } - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32[][])")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32[][])")] - public void M (int[][] a) - { - } - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32[][0:,0:,0:])")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32[][0:,0:,0:])")] - public void M (int[,,][] a) - { - } - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32[0:,0:])")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32[0:,0:])")] - public void M (int[,] a) - { - } - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Object)")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Object)")] - public void M (dynamic d) - { - } - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32*)")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32*)")] - public unsafe void M (int* a) - { - } - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M``1(Mono.Linker.Tests.SignatureParserTests.S{Mono.Linker.Tests.SignatureParserTests.G{Mono.Linker.Tests.SignatureParserTests.A,``0}}**[0:,0:,0:][][][0:,0:]@)")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M``1(Mono.Linker.Tests.SignatureParserTests.S{Mono.Linker.Tests.SignatureParserTests.G{Mono.Linker.Tests.SignatureParserTests.A,``0}}**[0:,0:,0:][][][0:,0:]@)")] - public unsafe void M (ref S>**[,][][][,,] a) - { - } - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Collections.Generic.List{System.Int32[]})")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Collections.Generic.List{System.Int32[]})")] - public void M (List a) - { - } - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32,)")] - //[ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.M(System.Int32,)")] - // there's no way to reference this, since the parsing logic doesn't like it. - public void M (int abo, __arglist) - { - } - - [ExpectGeneratedString ("P:Mono.Linker.Tests.SignatureParserTests.A.Prop")] - [ExpectUniqueParsedString ("P:Mono.Linker.Tests.SignatureParserTests.A.Prop")] - public int Prop { get; set; } - - [ExpectGeneratedString ("F:Mono.Linker.Tests.SignatureParserTests.A.field")] - [ExpectUniqueParsedString ("F:Mono.Linker.Tests.SignatureParserTests.A.field")] - public int field; - - - [ExpectGeneratedString ("E:Mono.Linker.Tests.SignatureParserTests.A.OnEvent")] - [ExpectUniqueParsedString ("E:Mono.Linker.Tests.SignatureParserTests.A.OnEvent")] - public event EventHandler OnEvent; - - [ExpectGeneratedString ("E:Mono.Linker.Tests.SignatureParserTests.A.OnEventInt")] - [ExpectUniqueParsedString ("E:Mono.Linker.Tests.SignatureParserTests.A.OnEventInt")] - public event Action OnEventInt; - - [ExpectGeneratedString ("T:Mono.Linker.Tests.SignatureParserTests.A.Del")] - [ExpectUniqueParsedString ("T:Mono.Linker.Tests.SignatureParserTests.A.Del")] - public delegate int Del (int a, int b); - - [ExpectGeneratedString ("E:Mono.Linker.Tests.SignatureParserTests.A.OnEventDel")] - [ExpectUniqueParsedString ("E:Mono.Linker.Tests.SignatureParserTests.A.OnEventDel")] - public event Del OnEventDel; - - // prevent warning about unused events - public void UseEvents () - { - OnEventDel?.Invoke (1, 2); - OnEventInt?.Invoke (1); - OnEvent?.Invoke (null, null); - } - - [ExpectGeneratedString ("P:Mono.Linker.Tests.SignatureParserTests.A.Item(System.Int32)")] - [ExpectUniqueParsedString ("P:Mono.Linker.Tests.SignatureParserTests.A.Item(System.Int32)")] - public int this[int i] { - get => 0; - set { } - } - - [ExpectGeneratedString ("P:Mono.Linker.Tests.SignatureParserTests.A.Item(System.String,System.Int32)")] - [ExpectUniqueParsedString ("P:Mono.Linker.Tests.SignatureParserTests.A.Item(System.String,System.Int32)")] - public int this[string s, int i] { - get => 0; - set { } - } - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.op_Implicit(Mono.Linker.Tests.SignatureParserTests.A)~System.Boolean")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.op_Implicit(Mono.Linker.Tests.SignatureParserTests.A)~System.Boolean")] - public static implicit operator bool (A a) => false; - - // C# will not generate a return type for this method, but we will. - // [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.op_Implicit(Mono.Linker.Tests.SignatureParserTests.A)~System.Boolean")] - // [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.op_Implicit(Mono.Linker.Tests.SignatureParserTests.A)~System.Boolean")] - //public static int op_Implicit (A a) => 0; - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.op_UnaryPlus(Mono.Linker.Tests.SignatureParserTests.A)")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.op_UnaryPlus(Mono.Linker.Tests.SignatureParserTests.A)")] - public static A operator + (A a) => null; - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.A.op_Addition(Mono.Linker.Tests.SignatureParserTests.A,Mono.Linker.Tests.SignatureParserTests.A)")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.A.op_Addition(Mono.Linker.Tests.SignatureParserTests.A,Mono.Linker.Tests.SignatureParserTests.A)")] - public static A operator + (A left, A right) => null; - } - - public struct S - { - } - - [ExpectGeneratedString ("T:Mono.Linker.Tests.SignatureParserTests.A`1")] - [ExpectUniqueParsedString ("T:Mono.Linker.Tests.SignatureParserTests.A`1")] - public class A - { - [ExpectGeneratedString ("P:Mono.Linker.Tests.SignatureParserTests.A`1.Item(`0)")] - [ExpectUniqueParsedString ("P:Mono.Linker.Tests.SignatureParserTests.A`1.Item(`0)")] - public int this[T t] { - get => 0; - set { } - } - } - - [ExpectUniqueParsedString ("T:Mono.Linker.Tests.SignatureParserTests.B")] - [ExpectGeneratedString ("T:Mono.Linker.Tests.SignatureParserTests.B")] - public class B - { - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.B.Method(Mono.Linker.Tests.SignatureParserTests.G{Mono.Linker.Tests.SignatureParserTests.A{Mono.Linker.Tests.SignatureParserTests.B},System.Collections.Generic.List{Mono.Linker.Tests.SignatureParserTests.A}})")] - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.B.Method(Mono.Linker.Tests.SignatureParserTests.G{Mono.Linker.Tests.SignatureParserTests.A{Mono.Linker.Tests.SignatureParserTests.B},System.Collections.Generic.List{Mono.Linker.Tests.SignatureParserTests.A}})")] - public void Method (G, List> l) - { - } - } - - [ExpectGeneratedString ("T:Mono.Linker.Tests.SignatureParserTests.G`2")] - [ExpectUniqueParsedString ("T:Mono.Linker.Tests.SignatureParserTests.G`2")] - public class G - { - [ExpectGeneratedString ("T:Mono.Linker.Tests.SignatureParserTests.G`2.NG`1")] - [ExpectUniqueParsedString ("T:Mono.Linker.Tests.SignatureParserTests.G`2.NG`1")] - public class NG - { - [ExpectGeneratedString ("T:Mono.Linker.Tests.SignatureParserTests.G`2.NG`1.NG2`1")] - [ExpectUniqueParsedString ("T:Mono.Linker.Tests.SignatureParserTests.G`2.NG`1.NG2`1")] - public class NG2 - { - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.G`2.NG`1.NG2`1.Method``1(`0,`1,`2,`3,``0)")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.G`2.NG`1.NG2`1.Method``1(`0,`1,`2,`3,``0)")] - public void Method (T t, U u, V v, W w, X x) - { - } - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.G`2.NG`1.NG2`1.Method(Mono.Linker.Tests.SignatureParserTests.G{`0,`1}.NG{`2}.NG2{`3})")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.G`2.NG`1.NG2`1.Method(Mono.Linker.Tests.SignatureParserTests.G{`0,`1}.NG{`2}.NG2{`3})")] - public void Method (NG2 n) - { - } - } - } - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.G`2.Method(Mono.Linker.Tests.SignatureParserTests.G{`0,`1})")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.G`2.Method(Mono.Linker.Tests.SignatureParserTests.G{`0,`1})")] - public void Method (G g) - { - } - } - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.Method")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Method")] - public void Method () - { - } - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.Method(System.Int32)")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Method(System.Int32)")] - public void Method (int i) - { - } - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.IntMethod")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.IntMethod")] - public int IntMethod () => 0; - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.Method(Mono.Linker.Tests.SignatureParserTests.G{Mono.Linker.Tests.SignatureParserTests.A,Mono.Linker.Tests.SignatureParserTests.A})")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Method(Mono.Linker.Tests.SignatureParserTests.G{Mono.Linker.Tests.SignatureParserTests.A,Mono.Linker.Tests.SignatureParserTests.A})")] - public void Method (G g) - { - } - - [ExpectGeneratedString ("M:Mono.Linker.Tests.SignatureParserTests.Method(Mono.Linker.Tests.SignatureParserTests.G{Mono.Linker.Tests.SignatureParserTests.A,Mono.Linker.Tests.SignatureParserTests.A}.NG{Mono.Linker.Tests.SignatureParserTests.A})")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Method(Mono.Linker.Tests.SignatureParserTests.G{Mono.Linker.Tests.SignatureParserTests.A,Mono.Linker.Tests.SignatureParserTests.A}.NG{Mono.Linker.Tests.SignatureParserTests.A})")] - public void Method (G.NG g) - { - } - - public class Invalid - { - [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.NoReturnType~")] - public int NoReturnType () => 0; - - [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.NoParameters(,)")] - public void NoParameters (int a, int b) - { - } - - [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.NoClosingParen(")] - public void NoClosingParen () { } - - [ExpectNoParsedString ("T:Mono.Linker.Tests.SignatureParserTests.Invalid.Whitespace ")] - [ExpectNoParsedString (" T:Mono.Linker.Tests.SignatureParserTests.Invalid.Whitespace")] - [ExpectNoParsedString ("T: Mono.Linker.Tests.SignatureParserTests.Invalid.Whitespace")] - [ExpectNoParsedString ("T :Mono.Linker.Tests.SignatureParserTests.Invalid.Whitespace")] - [ExpectNoParsedString ("")] - [ExpectNoParsedString (" ")] - public class Whitespace - { - [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.Whitespace.Method(System.Int32, System.Int32)")] - public void Method (int a, int b) - { - } - - [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.Whitespace.Method(Mono.Linker.Tests.SignatureParserTests.Invalid.Generic{System.Int32, System.Int32})")] - public void Method (Generic g) - { - } - } - - public class Generic - { - } - - [ExpectNoParsedString ("T:Mono.Linker.Tests.SignatureParserTests.Invalid.Generic{`1}")] - [ExpectNoParsedString ("T:Mono.Linker.Tests.SignatureParserTests.Invalid.Generic{T}")] - [ExpectNoParsedString ("T:Mono.Linker.Tests.SignatureParserTests.Invalid.Generic")] - public class Generic - { - [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.Generic``1.MethodSyntaxForTypeParameter(`0)")] - - public void MethodSyntaxForTypeParameter (T t) - { - } - - [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.Generic`1.MethodSyntaxForTypeGenericArgument(Mono.Linker.Tests.SignatureParserTests.Invalid.Generic{``0})")] - public void MethodSyntaxForTypeGenericArgument (Generic g) - { - } - - [ExpectNoParsedString ("P:Mono.Linker.Tests.SignatureParserTests.Invalid.Generic`1.Item(``0)")] - public bool this[T t] { - get => false; - set { } - } - } - - [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.MethodWithGenericInstantiation(Mono.Linker.Tests.SignatureParserTests.Invalid.Generic`1)")] - public void MethodWithGenericInstantiation (Generic g) - { - } - - [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.Method(System.Int32[:,:])")] - [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.Method(System.Int32[0:,)")] - public void Method (int[,] a) - { - } - - [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.NonGenericMethod(``0)")] - public void NonGenericMethod (int i) - { - } - - [ExpectNoParsedString ("P:Mono.Linker.Tests.SignatureParserTests.Invalid.Item(`0)")] - [ExpectNoParsedString ("P:Mono.Linker.Tests.SignatureParserTests.Invalid.Item(``0)")] - public int this[int i] { - get => 0; - set { } - } - - [ExpectNoParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.MethodMissingArgumentTypeName(System.)")] - public void MethodMissingArgumentTypeName (int i) - { - } - - [ExpectNoParsedString ("T:Mono.Linker.Tests.SignatureParserTests.Invalid.")] - public class NoType - { - } - - [ExpectNoParsedString ("T:Mono.Linker.Tests.SignatureParserTests.Invalid.NoParameterType()")] - public void NoParameterType (int i) - { - } - - [ExpectNoParsedString ("T:Mono.Linker.Tests.SignatureParserTests.Invalid.NoParameterType(Mono.Linker.Tests.SignatureParserTests.Invalid.Generic{})")] - public void NoGenericParameterType (Generic g) - { - } - - // these work, but seem like they shouldn't. - // see https://github.com/dotnet/roslyn/issues/44315 - - [ExpectUniqueParsedString ("TMono.Linker.Tests.SignatureParserTests.Invalid.NoColon")] - public class NoColon - { - } - - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.NoClosingParenWithParameters(System.Int32")] - public void NoClosingParenWithParameters (int a) - { - } - - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.SignatureParserTests.Invalid.NoClosingBrace(Mono.Linker.Tests.SignatureParserTests.Invalid.Generic{Mono.Linker.Tests.SignatureParserTests.A)")] - public void NoClosingBrace (Generic g) - { - } - } - } -} \ No newline at end of file From b22e8eda075dfbf771d9e1eb0b0f934896ad7caf Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 18 May 2020 16:12:53 +0000 Subject: [PATCH 4/9] Avoid ImmutableArray To let it build on mono --- src/linker/Linker/DocumentationSignatureParser.cs | 5 ++--- .../Tests/DocumentationSignatureParserTests.cs | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/linker/Linker/DocumentationSignatureParser.cs b/src/linker/Linker/DocumentationSignatureParser.cs index 433e2d12a71c..b431d5141293 100644 --- a/src/linker/Linker/DocumentationSignatureParser.cs +++ b/src/linker/Linker/DocumentationSignatureParser.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Text; @@ -30,7 +29,7 @@ namespace Mono.Linker /// public static class DocumentationSignatureParser { - public static ImmutableArray GetSymbolsForDeclarationId (string id, ModuleDefinition module) + public static IEnumerable GetSymbolsForDeclarationId (string id, ModuleDefinition module) { if (id == null) throw new ArgumentNullException (nameof (id)); @@ -40,7 +39,7 @@ public static ImmutableArray GetSymbolsForDeclarationId (stri var results = new List (); Parser.ParseDeclaredSymbolId (id, module, results); - return results.ToImmutableArray(); + return results; } public static string GetSignaturePart (this TypeReference type) diff --git a/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs b/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs index 99e401bf9df0..82201a0fa359 100644 --- a/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs +++ b/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs @@ -38,7 +38,7 @@ public static void CheckUniqueParsedString (IMemberDefinition member, string inp var module = (member as TypeDefinition)?.Module ?? member.DeclaringType?.Module; Assert.NotNull (module); var parseResults = DocumentationSignatureParser.GetSymbolsForDeclarationId (input, module); - Assert.AreEqual (1, parseResults.Length); + Assert.AreEqual (1, parseResults.Count ()); Assert.AreEqual (member, parseResults.First ()); } @@ -81,7 +81,7 @@ public static void CheckNoParsedString (IMemberDefinition member, string input) var module = (member as TypeDefinition)?.Module ?? member.DeclaringType?.Module; Assert.NotNull (module); var parseResults = DocumentationSignatureParser.GetSymbolsForDeclarationId (input, module); - Assert.AreEqual (0, parseResults.Length); + Assert.AreEqual (0, parseResults.Count ()); } static IEnumerable<(IMemberDefinition member, CustomAttribute ca)> GetMemberAssertions (Type type) From a3c12dad6aca91f21be925e69c0ed0a1cb79ee76 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 18 May 2020 21:33:19 +0000 Subject: [PATCH 5/9] Rename test attributes And add comments clarifying their behavior --- .../BaseMemberAssertionAttribute.cs | 3 + ...ResolvedDocumentationSignatureAttribute.cs | 13 + ...eneratedDocumentationSignatureAttribute.cs | 13 + .../ExpectGeneratedStringAttribute.cs | 11 - .../ExpectNoParsedStringAttribute.cs | 11 - .../Assertions/ExpectParsedStringAttribute.cs | 11 - ...ResolvedDocumentationSignatureAttribute.cs | 13 + .../ExpectUniqueParsedStringAttribute.cs | 11 - ...resolvedDocumentationSignatureAttribute.cs | 13 + .../DocumentationSignatureParserTests.cs | 266 +++++++++--------- 10 files changed, 188 insertions(+), 177 deletions(-) create mode 100644 test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectExactlyResolvedDocumentationSignatureAttribute.cs create mode 100644 test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectGeneratedDocumentationSignatureAttribute.cs delete mode 100644 test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectGeneratedStringAttribute.cs delete mode 100644 test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectNoParsedStringAttribute.cs delete mode 100644 test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectParsedStringAttribute.cs create mode 100644 test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectResolvedDocumentationSignatureAttribute.cs delete mode 100644 test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectUniqueParsedStringAttribute.cs create mode 100644 test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectUnresolvedDocumentationSignatureAttribute.cs diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/BaseMemberAssertionAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/BaseMemberAssertionAttribute.cs index 89309c6ba06c..563ebfb8f611 100644 --- a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/BaseMemberAssertionAttribute.cs +++ b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/BaseMemberAssertionAttribute.cs @@ -2,6 +2,9 @@ namespace Mono.Linker.Tests.Cases.Expectations.Assertions { + /// A base class for attributes that make assertions about a particular member. + // The test infrastructure is expected to check the assertion on the member to which + // the attribute is applied. [AttributeUsage (AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Event | AttributeTargets.Delegate, AllowMultiple = true)] public abstract class BaseMemberAssertionAttribute : Attribute { diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectExactlyResolvedDocumentationSignatureAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectExactlyResolvedDocumentationSignatureAttribute.cs new file mode 100644 index 000000000000..19d582509dc6 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectExactlyResolvedDocumentationSignatureAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions +{ + /// Asserts that the given documentation signature string resolves to the + // member with this attribute, and only that member. + public class ExpectExactlyResolvedDocumentationSignatureAttribute : BaseMemberAssertionAttribute + { + public ExpectExactlyResolvedDocumentationSignatureAttribute (string input) + { + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectGeneratedDocumentationSignatureAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectGeneratedDocumentationSignatureAttribute.cs new file mode 100644 index 000000000000..2a4402199557 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectGeneratedDocumentationSignatureAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions +{ + /// Asserts that the member to which this attribute is applied has the given + /// documentation signature. + public class ExpectGeneratedDocumentationSignatureAttribute : BaseMemberAssertionAttribute + { + public ExpectGeneratedDocumentationSignatureAttribute (string expected) + { + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectGeneratedStringAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectGeneratedStringAttribute.cs deleted file mode 100644 index a6d41a69aaf0..000000000000 --- a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectGeneratedStringAttribute.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace Mono.Linker.Tests.Cases.Expectations.Assertions -{ - public class ExpectGeneratedStringAttribute : BaseMemberAssertionAttribute - { - public ExpectGeneratedStringAttribute (string expected) - { - } - } -} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectNoParsedStringAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectNoParsedStringAttribute.cs deleted file mode 100644 index 2bcab589cf09..000000000000 --- a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectNoParsedStringAttribute.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace Mono.Linker.Tests.Cases.Expectations.Assertions -{ - public class ExpectNoParsedStringAttribute : BaseMemberAssertionAttribute - { - public ExpectNoParsedStringAttribute (string expected) - { - } - } -} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectParsedStringAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectParsedStringAttribute.cs deleted file mode 100644 index 743936422690..000000000000 --- a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectParsedStringAttribute.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace Mono.Linker.Tests.Cases.Expectations.Assertions -{ - public class ExpectParsedStringAttribute : BaseMemberAssertionAttribute - { - public ExpectParsedStringAttribute (string input) - { - } - } -} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectResolvedDocumentationSignatureAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectResolvedDocumentationSignatureAttribute.cs new file mode 100644 index 000000000000..da9bfde4c5bc --- /dev/null +++ b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectResolvedDocumentationSignatureAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions +{ + /// Asserts that the given documentation signature string resolves to the + // member with this attribute. + public class ExpectResolvedDocumentationSignatureAttribute : BaseMemberAssertionAttribute + { + public ExpectResolvedDocumentationSignatureAttribute (string input) + { + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectUniqueParsedStringAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectUniqueParsedStringAttribute.cs deleted file mode 100644 index 79da16fcb2c8..000000000000 --- a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectUniqueParsedStringAttribute.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace Mono.Linker.Tests.Cases.Expectations.Assertions -{ - public class ExpectUniqueParsedStringAttribute : BaseMemberAssertionAttribute - { - public ExpectUniqueParsedStringAttribute (string input) - { - } - } -} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectUnresolvedDocumentationSignatureAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectUnresolvedDocumentationSignatureAttribute.cs new file mode 100644 index 000000000000..9c71567ac5b3 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectUnresolvedDocumentationSignatureAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions +{ + /// Asserts that the given documentation signature string does not resolve + /// to the member with this attribute. + public class ExpectUnresolvedDocumentationSignatureAttribute : BaseMemberAssertionAttribute + { + public ExpectUnresolvedDocumentationSignatureAttribute (string expected) + { + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs b/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs index 82201a0fa359..a90bb5047f4e 100644 --- a/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs +++ b/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs @@ -16,17 +16,17 @@ public void TestSignatureParsing (IMemberDefinition member, CustomAttribute cust { var attributeString = (string) customAttribute.ConstructorArguments[0].Value; switch (customAttribute.AttributeType.Name) { - case nameof (ExpectUniqueParsedStringAttribute): + case nameof (ExpectExactlyResolvedDocumentationSignatureAttribute): CheckUniqueParsedString (member, attributeString); break; - case nameof (ExpectGeneratedStringAttribute): + case nameof (ExpectGeneratedDocumentationSignatureAttribute): CheckGeneratedString (member, attributeString); break; - case nameof (ExpectParsedStringAttribute): + case nameof (ExpectResolvedDocumentationSignatureAttribute): CheckParsedString (member, attributeString); break; - case nameof (ExpectNoParsedStringAttribute): - CheckNoParsedString (member, attributeString); + case nameof (ExpectUnresolvedDocumentationSignatureAttribute): + CheckUnresolvedDocumentationSignature (member, attributeString); break; default: throw new NotImplementedException (); @@ -76,12 +76,12 @@ public static void CheckParsedString (IMemberDefinition member, string input) CollectionAssert.Contains (parseResults, member); } - public static void CheckNoParsedString (IMemberDefinition member, string input) + public static void CheckUnresolvedDocumentationSignature (IMemberDefinition member, string input) { var module = (member as TypeDefinition)?.Module ?? member.DeclaringType?.Module; Assert.NotNull (module); var parseResults = DocumentationSignatureParser.GetSymbolsForDeclarationId (input, module); - Assert.AreEqual (0, parseResults.Count ()); + CollectionAssert.DoesNotContain (parseResults, member); } static IEnumerable<(IMemberDefinition member, CustomAttribute ca)> GetMemberAssertions (Type type) @@ -174,155 +174,155 @@ static IEnumerable GetMemberAssertionsAsArray (Type type) // testcases - [ExpectGeneratedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.A")] - [ExpectUniqueParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.A")] + [ExpectGeneratedDocumentationSignature ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.A")] + [ExpectExactlyResolvedDocumentationSignature ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.A")] public class A { - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.#ctor")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.#ctor")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.#ctor")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.#ctor")] public A () { } - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.#ctor(System.Int32)")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.#ctor(System.Int32)")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.#ctor(System.Int32)")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.#ctor(System.Int32)")] public A (int a) { } - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.#cctor")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.#cctor")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.#cctor")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.#cctor")] static A () { } - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32[])")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32[])")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32[])")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32[])")] public void M (int[] a) { } - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32,System.Int32,System.Int32)~System.Int32")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32,System.Int32,System.Int32)~System.Int32")] public int M (int a, int b, int c) { return 0; } - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MRef(System.Int32@)")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MRef(System.Int32@)")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MRef(System.Int32@)")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MRef(System.Int32@)")] public void MRef (ref int a) { } - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MOut(System.Int32@)")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MOut(System.Int32@)")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MOut(System.Int32@)")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MOut(System.Int32@)")] public void MOut (out int a) { a = 5; } - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MIn(System.Int32@)")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MIn(System.Int32@)")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MIn(System.Int32@)")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MIn(System.Int32@)")] public void MIn (in int a) { } public static int i; - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MRefReturn")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MRefReturn")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MRefReturn~System.Int32@")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MRefReturn")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MRefReturn")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.MRefReturn~System.Int32@")] public ref int MRefReturn () { return ref i; } - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M")] - [ExpectParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M")] // binds to both. - [ExpectParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M()")] // binds to both. + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M")] + [ExpectResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M")] // binds to both. + [ExpectResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M()")] // binds to both. public void M () { } - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M()")] - [ExpectParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M")] - [ExpectParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M()")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M()")] + [ExpectResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M")] + [ExpectResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M()")] public void M (__arglist) { } - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32[][])")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32[][])")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32[][])")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32[][])")] public void M (int[][] a) { } - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32[][0:,0:,0:])")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32[][0:,0:,0:])")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32[][0:,0:,0:])")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32[][0:,0:,0:])")] public void M (int[,,][] a) { } - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32[0:,0:])")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32[0:,0:])")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32[0:,0:])")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32[0:,0:])")] public void M (int[,] a) { } - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Object)")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Object)")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Object)")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Object)")] public void M (dynamic d) { } - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32*)")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32*)")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32*)")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32*)")] public unsafe void M (int* a) { } - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M``1(Mono.Linker.Tests.DocumentationSignatureParserTests.S{Mono.Linker.Tests.DocumentationSignatureParserTests.G{Mono.Linker.Tests.DocumentationSignatureParserTests.A,``0}}**[0:,0:,0:][][][0:,0:]@)")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M``1(Mono.Linker.Tests.DocumentationSignatureParserTests.S{Mono.Linker.Tests.DocumentationSignatureParserTests.G{Mono.Linker.Tests.DocumentationSignatureParserTests.A,``0}}**[0:,0:,0:][][][0:,0:]@)")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M``1(Mono.Linker.Tests.DocumentationSignatureParserTests.S{Mono.Linker.Tests.DocumentationSignatureParserTests.G{Mono.Linker.Tests.DocumentationSignatureParserTests.A,``0}}**[0:,0:,0:][][][0:,0:]@)")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M``1(Mono.Linker.Tests.DocumentationSignatureParserTests.S{Mono.Linker.Tests.DocumentationSignatureParserTests.G{Mono.Linker.Tests.DocumentationSignatureParserTests.A,``0}}**[0:,0:,0:][][][0:,0:]@)")] public unsafe void M (ref S>**[,][][][,,] a) { } - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Collections.Generic.List{System.Int32[]})")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Collections.Generic.List{System.Int32[]})")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Collections.Generic.List{System.Int32[]})")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Collections.Generic.List{System.Int32[]})")] public void M (List a) { } - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32,)")] - //[ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32,)")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32,)")] + //[ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.M(System.Int32,)")] // there's no way to reference this, since the parsing logic doesn't like it. public void M (int abo, __arglist) { } - [ExpectGeneratedString ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.A.Prop")] - [ExpectUniqueParsedString ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.A.Prop")] + [ExpectGeneratedDocumentationSignature ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.A.Prop")] + [ExpectExactlyResolvedDocumentationSignature ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.A.Prop")] public int Prop { get; set; } - [ExpectGeneratedString ("F:Mono.Linker.Tests.DocumentationSignatureParserTests.A.field")] - [ExpectUniqueParsedString ("F:Mono.Linker.Tests.DocumentationSignatureParserTests.A.field")] + [ExpectGeneratedDocumentationSignature ("F:Mono.Linker.Tests.DocumentationSignatureParserTests.A.field")] + [ExpectExactlyResolvedDocumentationSignature ("F:Mono.Linker.Tests.DocumentationSignatureParserTests.A.field")] public int field; - [ExpectGeneratedString ("E:Mono.Linker.Tests.DocumentationSignatureParserTests.A.OnEvent")] - [ExpectUniqueParsedString ("E:Mono.Linker.Tests.DocumentationSignatureParserTests.A.OnEvent")] + [ExpectGeneratedDocumentationSignature ("E:Mono.Linker.Tests.DocumentationSignatureParserTests.A.OnEvent")] + [ExpectExactlyResolvedDocumentationSignature ("E:Mono.Linker.Tests.DocumentationSignatureParserTests.A.OnEvent")] public event EventHandler OnEvent; - [ExpectGeneratedString ("E:Mono.Linker.Tests.DocumentationSignatureParserTests.A.OnEventInt")] - [ExpectUniqueParsedString ("E:Mono.Linker.Tests.DocumentationSignatureParserTests.A.OnEventInt")] + [ExpectGeneratedDocumentationSignature ("E:Mono.Linker.Tests.DocumentationSignatureParserTests.A.OnEventInt")] + [ExpectExactlyResolvedDocumentationSignature ("E:Mono.Linker.Tests.DocumentationSignatureParserTests.A.OnEventInt")] public event Action OnEventInt; - [ExpectGeneratedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.A.Del")] - [ExpectUniqueParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.A.Del")] + [ExpectGeneratedDocumentationSignature ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.A.Del")] + [ExpectExactlyResolvedDocumentationSignature ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.A.Del")] public delegate int Del (int a, int b); - [ExpectGeneratedString ("E:Mono.Linker.Tests.DocumentationSignatureParserTests.A.OnEventDel")] - [ExpectUniqueParsedString ("E:Mono.Linker.Tests.DocumentationSignatureParserTests.A.OnEventDel")] + [ExpectGeneratedDocumentationSignature ("E:Mono.Linker.Tests.DocumentationSignatureParserTests.A.OnEventDel")] + [ExpectExactlyResolvedDocumentationSignature ("E:Mono.Linker.Tests.DocumentationSignatureParserTests.A.OnEventDel")] public event Del OnEventDel; // prevent warning about unused events @@ -333,35 +333,35 @@ public void UseEvents () OnEvent?.Invoke (null, null); } - [ExpectGeneratedString ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.A.Item(System.Int32)")] - [ExpectUniqueParsedString ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.A.Item(System.Int32)")] + [ExpectGeneratedDocumentationSignature ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.A.Item(System.Int32)")] + [ExpectExactlyResolvedDocumentationSignature ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.A.Item(System.Int32)")] public int this[int i] { get => 0; set { } } - [ExpectGeneratedString ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.A.Item(System.String,System.Int32)")] - [ExpectUniqueParsedString ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.A.Item(System.String,System.Int32)")] + [ExpectGeneratedDocumentationSignature ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.A.Item(System.String,System.Int32)")] + [ExpectExactlyResolvedDocumentationSignature ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.A.Item(System.String,System.Int32)")] public int this[string s, int i] { get => 0; set { } } - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.op_Implicit(Mono.Linker.Tests.DocumentationSignatureParserTests.A)~System.Boolean")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.op_Implicit(Mono.Linker.Tests.DocumentationSignatureParserTests.A)~System.Boolean")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.op_Implicit(Mono.Linker.Tests.DocumentationSignatureParserTests.A)~System.Boolean")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.op_Implicit(Mono.Linker.Tests.DocumentationSignatureParserTests.A)~System.Boolean")] public static implicit operator bool (A a) => false; // C# will not generate a return type for this method, but we will. - // [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.op_Implicit(Mono.Linker.Tests.DocumentationSignatureParserTests.A)~System.Boolean")] - // [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.op_Implicit(Mono.Linker.Tests.DocumentationSignatureParserTests.A)~System.Boolean")] + // [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.op_Implicit(Mono.Linker.Tests.DocumentationSignatureParserTests.A)~System.Boolean")] + // [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.op_Implicit(Mono.Linker.Tests.DocumentationSignatureParserTests.A)~System.Boolean")] //public static int op_Implicit (A a) => 0; - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.op_UnaryPlus(Mono.Linker.Tests.DocumentationSignatureParserTests.A)")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.op_UnaryPlus(Mono.Linker.Tests.DocumentationSignatureParserTests.A)")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.op_UnaryPlus(Mono.Linker.Tests.DocumentationSignatureParserTests.A)")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.op_UnaryPlus(Mono.Linker.Tests.DocumentationSignatureParserTests.A)")] public static A operator + (A a) => null; - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.op_Addition(Mono.Linker.Tests.DocumentationSignatureParserTests.A,Mono.Linker.Tests.DocumentationSignatureParserTests.A)")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.op_Addition(Mono.Linker.Tests.DocumentationSignatureParserTests.A,Mono.Linker.Tests.DocumentationSignatureParserTests.A)")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.op_Addition(Mono.Linker.Tests.DocumentationSignatureParserTests.A,Mono.Linker.Tests.DocumentationSignatureParserTests.A)")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.A.op_Addition(Mono.Linker.Tests.DocumentationSignatureParserTests.A,Mono.Linker.Tests.DocumentationSignatureParserTests.A)")] public static A operator + (A left, A right) => null; } @@ -369,117 +369,117 @@ public struct S { } - [ExpectGeneratedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.A`1")] - [ExpectUniqueParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.A`1")] + [ExpectGeneratedDocumentationSignature ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.A`1")] + [ExpectExactlyResolvedDocumentationSignature ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.A`1")] public class A { - [ExpectGeneratedString ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.A`1.Item(`0)")] - [ExpectUniqueParsedString ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.A`1.Item(`0)")] + [ExpectGeneratedDocumentationSignature ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.A`1.Item(`0)")] + [ExpectExactlyResolvedDocumentationSignature ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.A`1.Item(`0)")] public int this[T t] { get => 0; set { } } } - [ExpectUniqueParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.B")] - [ExpectGeneratedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.B")] + [ExpectExactlyResolvedDocumentationSignature ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.B")] + [ExpectGeneratedDocumentationSignature ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.B")] public class B { - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.B.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{Mono.Linker.Tests.DocumentationSignatureParserTests.A{Mono.Linker.Tests.DocumentationSignatureParserTests.B},System.Collections.Generic.List{Mono.Linker.Tests.DocumentationSignatureParserTests.A}})")] - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.B.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{Mono.Linker.Tests.DocumentationSignatureParserTests.A{Mono.Linker.Tests.DocumentationSignatureParserTests.B},System.Collections.Generic.List{Mono.Linker.Tests.DocumentationSignatureParserTests.A}})")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.B.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{Mono.Linker.Tests.DocumentationSignatureParserTests.A{Mono.Linker.Tests.DocumentationSignatureParserTests.B},System.Collections.Generic.List{Mono.Linker.Tests.DocumentationSignatureParserTests.A}})")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.B.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{Mono.Linker.Tests.DocumentationSignatureParserTests.A{Mono.Linker.Tests.DocumentationSignatureParserTests.B},System.Collections.Generic.List{Mono.Linker.Tests.DocumentationSignatureParserTests.A}})")] public void Method (G, List> l) { } } - [ExpectGeneratedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2")] - [ExpectUniqueParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2")] + [ExpectGeneratedDocumentationSignature ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2")] + [ExpectExactlyResolvedDocumentationSignature ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2")] public class G { - [ExpectGeneratedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.NG`1")] - [ExpectUniqueParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.NG`1")] + [ExpectGeneratedDocumentationSignature ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.NG`1")] + [ExpectExactlyResolvedDocumentationSignature ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.NG`1")] public class NG { - [ExpectGeneratedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.NG`1.NG2`1")] - [ExpectUniqueParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.NG`1.NG2`1")] + [ExpectGeneratedDocumentationSignature ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.NG`1.NG2`1")] + [ExpectExactlyResolvedDocumentationSignature ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.NG`1.NG2`1")] public class NG2 { - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.NG`1.NG2`1.Method``1(`0,`1,`2,`3,``0)")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.NG`1.NG2`1.Method``1(`0,`1,`2,`3,``0)")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.NG`1.NG2`1.Method``1(`0,`1,`2,`3,``0)")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.NG`1.NG2`1.Method``1(`0,`1,`2,`3,``0)")] public void Method (T t, U u, V v, W w, X x) { } - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.NG`1.NG2`1.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{`0,`1}.NG{`2}.NG2{`3})")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.NG`1.NG2`1.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{`0,`1}.NG{`2}.NG2{`3})")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.NG`1.NG2`1.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{`0,`1}.NG{`2}.NG2{`3})")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.NG`1.NG2`1.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{`0,`1}.NG{`2}.NG2{`3})")] public void Method (NG2 n) { } } } - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{`0,`1})")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{`0,`1})")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{`0,`1})")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.G`2.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{`0,`1})")] public void Method (G g) { } } - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Method")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Method")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Method")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Method")] public void Method () { } - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Method(System.Int32)")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Method(System.Int32)")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Method(System.Int32)")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Method(System.Int32)")] public void Method (int i) { } - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.IntMethod")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.IntMethod")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.IntMethod")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.IntMethod")] public int IntMethod () => 0; - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{Mono.Linker.Tests.DocumentationSignatureParserTests.A,Mono.Linker.Tests.DocumentationSignatureParserTests.A})")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{Mono.Linker.Tests.DocumentationSignatureParserTests.A,Mono.Linker.Tests.DocumentationSignatureParserTests.A})")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{Mono.Linker.Tests.DocumentationSignatureParserTests.A,Mono.Linker.Tests.DocumentationSignatureParserTests.A})")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{Mono.Linker.Tests.DocumentationSignatureParserTests.A,Mono.Linker.Tests.DocumentationSignatureParserTests.A})")] public void Method (G g) { } - [ExpectGeneratedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{Mono.Linker.Tests.DocumentationSignatureParserTests.A,Mono.Linker.Tests.DocumentationSignatureParserTests.A}.NG{Mono.Linker.Tests.DocumentationSignatureParserTests.A})")] - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{Mono.Linker.Tests.DocumentationSignatureParserTests.A,Mono.Linker.Tests.DocumentationSignatureParserTests.A}.NG{Mono.Linker.Tests.DocumentationSignatureParserTests.A})")] + [ExpectGeneratedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{Mono.Linker.Tests.DocumentationSignatureParserTests.A,Mono.Linker.Tests.DocumentationSignatureParserTests.A}.NG{Mono.Linker.Tests.DocumentationSignatureParserTests.A})")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.G{Mono.Linker.Tests.DocumentationSignatureParserTests.A,Mono.Linker.Tests.DocumentationSignatureParserTests.A}.NG{Mono.Linker.Tests.DocumentationSignatureParserTests.A})")] public void Method (G.NG g) { } public class Invalid { - [ExpectNoParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoReturnType~")] + [ExpectUnresolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoReturnType~")] public int NoReturnType () => 0; - [ExpectNoParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoParameters(,)")] + [ExpectUnresolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoParameters(,)")] public void NoParameters (int a, int b) { } - [ExpectNoParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoClosingParen(")] + [ExpectUnresolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoClosingParen(")] public void NoClosingParen () { } - [ExpectNoParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Whitespace ")] - [ExpectNoParsedString (" T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Whitespace")] - [ExpectNoParsedString ("T: Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Whitespace")] - [ExpectNoParsedString ("T :Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Whitespace")] - [ExpectNoParsedString ("")] - [ExpectNoParsedString (" ")] + [ExpectUnresolvedDocumentationSignature ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Whitespace ")] + [ExpectUnresolvedDocumentationSignature (" T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Whitespace")] + [ExpectUnresolvedDocumentationSignature ("T: Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Whitespace")] + [ExpectUnresolvedDocumentationSignature ("T :Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Whitespace")] + [ExpectUnresolvedDocumentationSignature ("")] + [ExpectUnresolvedDocumentationSignature (" ")] public class Whitespace { - [ExpectNoParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Whitespace.Method(System.Int32, System.Int32)")] + [ExpectUnresolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Whitespace.Method(System.Int32, System.Int32)")] public void Method (int a, int b) { } - [ExpectNoParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Whitespace.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic{System.Int32, System.Int32})")] + [ExpectUnresolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Whitespace.Method(Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic{System.Int32, System.Int32})")] public void Method (Generic g) { } @@ -489,68 +489,68 @@ public class Generic { } - [ExpectNoParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic{`1}")] - [ExpectNoParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic{T}")] - [ExpectNoParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic")] + [ExpectUnresolvedDocumentationSignature ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic{`1}")] + [ExpectUnresolvedDocumentationSignature ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic{T}")] + [ExpectUnresolvedDocumentationSignature ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic")] public class Generic { - [ExpectNoParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic``1.MethodSyntaxForTypeParameter(`0)")] + [ExpectUnresolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic``1.MethodSyntaxForTypeParameter(`0)")] public void MethodSyntaxForTypeParameter (T t) { } - [ExpectNoParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic`1.MethodSyntaxForTypeGenericArgument(Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic{``0})")] + [ExpectUnresolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic`1.MethodSyntaxForTypeGenericArgument(Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic{``0})")] public void MethodSyntaxForTypeGenericArgument (Generic g) { } - [ExpectNoParsedString ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic`1.Item(``0)")] + [ExpectUnresolvedDocumentationSignature ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic`1.Item(``0)")] public bool this[T t] { get => false; set { } } } - [ExpectNoParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.MethodWithGenericInstantiation(Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic`1)")] + [ExpectUnresolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.MethodWithGenericInstantiation(Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic`1)")] public void MethodWithGenericInstantiation (Generic g) { } - [ExpectNoParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Method(System.Int32[:,:])")] - [ExpectNoParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Method(System.Int32[0:,)")] + [ExpectUnresolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Method(System.Int32[:,:])")] + [ExpectUnresolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Method(System.Int32[0:,)")] public void Method (int[,] a) { } - [ExpectNoParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NonGenericMethod(``0)")] + [ExpectUnresolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NonGenericMethod(``0)")] public void NonGenericMethod (int i) { } - [ExpectNoParsedString ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Item(`0)")] - [ExpectNoParsedString ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Item(``0)")] + [ExpectUnresolvedDocumentationSignature ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Item(`0)")] + [ExpectUnresolvedDocumentationSignature ("P:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Item(``0)")] public int this[int i] { get => 0; set { } } - [ExpectNoParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.MethodMissingArgumentTypeName(System.)")] + [ExpectUnresolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.MethodMissingArgumentTypeName(System.)")] public void MethodMissingArgumentTypeName (int i) { } - [ExpectNoParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.")] + [ExpectUnresolvedDocumentationSignature ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.")] public class NoType { } - [ExpectNoParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoParameterType()")] + [ExpectUnresolvedDocumentationSignature ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoParameterType()")] public void NoParameterType (int i) { } - [ExpectNoParsedString ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoParameterType(Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic{})")] + [ExpectUnresolvedDocumentationSignature ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoParameterType(Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic{})")] public void NoGenericParameterType (Generic g) { } @@ -558,17 +558,17 @@ public void NoGenericParameterType (Generic g) // these work, but seem like they shouldn't. // see https://github.com/dotnet/roslyn/issues/44315 - [ExpectUniqueParsedString ("TMono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoColon")] + [ExpectExactlyResolvedDocumentationSignature ("TMono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoColon")] public class NoColon { } - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoClosingParenWithParameters(System.Int32")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoClosingParenWithParameters(System.Int32")] public void NoClosingParenWithParameters (int a) { } - [ExpectUniqueParsedString ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoClosingBrace(Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic{Mono.Linker.Tests.DocumentationSignatureParserTests.A)")] + [ExpectExactlyResolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoClosingBrace(Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.Generic{Mono.Linker.Tests.DocumentationSignatureParserTests.A)")] public void NoClosingBrace (Generic g) { } From ebc03dbaf262fa10a35a282d5e047a4c1dafb897 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 18 May 2020 22:48:30 +0000 Subject: [PATCH 6/9] Fix mono test failure There were two issues: - Nunit doesn't like assertions inside of a TestCaseSource method. - On mono, the test infra failed to resolve the new assertion attributes --- .../Tests/DocumentationSignatureParserTests.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs b/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs index a90bb5047f4e..75965f964a10 100644 --- a/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs +++ b/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; -using System.Text; +using System.IO; using System.Linq; +using System.Text; using NUnit.Framework; using Mono.Cecil; using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Extensions; namespace Mono.Linker.Tests { @@ -86,9 +88,12 @@ public static void CheckUnresolvedDocumentationSignature (IMemberDefinition memb static IEnumerable<(IMemberDefinition member, CustomAttribute ca)> GetMemberAssertions (Type type) { - var assembly = AssemblyDefinition.ReadAssembly (type.Assembly.Location); + var resolver = new DefaultAssemblyResolver (); + resolver.AddSearchDirectory (Path.GetDirectoryName (type.Assembly.Location)); + var assembly = resolver.Resolve (new AssemblyNameReference (type.Assembly.GetName ().Name, null)); var t = assembly.MainModule.GetType (type.Namespace + "." + type.Name); - Assert.NotNull (t); + if (t == null) + throw new InvalidOperationException ($"type {type} not found in {assembly}"); var results = new List<(IMemberDefinition, CustomAttribute)> (); CollectMemberAssertions (t, results); return results; @@ -99,10 +104,10 @@ private static bool IsMemberAssertion (TypeReference attributeType) if (attributeType == null) return false; - if (attributeType.Namespace == "Mono.Linker.Tests.Cases.Expectations.Assertions" && attributeType.Name == nameof (BaseMemberAssertionAttribute)) - return true; + if (attributeType.Namespace != "Mono.Linker.Tests.Cases.Expectations.Assertions") + return false; - return IsMemberAssertion (attributeType.Resolve ()?.BaseType); + return attributeType.Resolve ().DerivesFrom (nameof (BaseMemberAssertionAttribute)); } private static void CollectMemberAssertions (TypeDefinition type, List<(IMemberDefinition, CustomAttribute)> results) From 647924d8de77640db131c8079fc3c6166f637e99 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Tue, 26 May 2020 18:21:21 +0000 Subject: [PATCH 7/9] Factor parser for use from DynamicDependencyAttribute - Allow parsing a signature without a member type prefix - Allow parsing a member without a type name - Allow matching multiple member kinds - Rename some public methods to more closely match linker terminology - Add a few more tests for edge cases --- .../Linker/DocumentationSignatureParser.cs | 332 ++++++++++-------- .../DocumentationSignatureParserTests.cs | 37 +- 2 files changed, 217 insertions(+), 152 deletions(-) diff --git a/src/linker/Linker/DocumentationSignatureParser.cs b/src/linker/Linker/DocumentationSignatureParser.cs index b431d5141293..1b21b66ffab1 100644 --- a/src/linker/Linker/DocumentationSignatureParser.cs +++ b/src/linker/Linker/DocumentationSignatureParser.cs @@ -29,7 +29,18 @@ namespace Mono.Linker /// public static class DocumentationSignatureParser { - public static IEnumerable GetSymbolsForDeclarationId (string id, ModuleDefinition module) + [Flags] + public enum MemberType + { + Method = 0x0001, + Field = 0x0002, + Type = 0x0004, + Property = 0x0008, + Event = 0x0010, + All = Method | Field | Type | Property | Event + } + + public static IEnumerable GetMembersForDocumentationSignature (string id, ModuleDefinition module) { if (id == null) throw new ArgumentNullException (nameof (id)); @@ -38,7 +49,7 @@ public static IEnumerable GetSymbolsForDeclarationId (string throw new ArgumentNullException (nameof (module)); var results = new List (); - Parser.ParseDeclaredSymbolId (id, module, results); + ParseDocumentationSignature (id, module, results); return results; } @@ -49,171 +60,194 @@ public static string GetSignaturePart (this TypeReference type) return builder.ToString (); } - private static class Parser + static bool ParseDocumentationSignature (string id, ModuleDefinition module, List results) { + if (id == null) + return false; - enum MemberType - { - Type, - Method, - Field, - Property, - Event, - } + if (id.Length < 2) + return false; - public static bool ParseDeclaredSymbolId (string id, ModuleDefinition module, List results) - { - if (id == null) - return false; + int index = 0; + results.Clear (); + ParseSignature (id, ref index, module, results); + return results.Count > 0; + } - if (id.Length < 2) - return false; + static void ParseSignature (string id, ref int index, ModuleDefinition module, List results) + { + Debug.Assert (results.Count == 0); + var memberTypeChar = PeekNextChar (id, index); + MemberType memberType; - int index = 0; - results.Clear (); - ParseDeclaredId (id, ref index, module, results); - return results.Count > 0; + switch (memberTypeChar) { + case 'E': + memberType = MemberType.Event; + break; + case 'F': + memberType = MemberType.Field; + break; + case 'M': + memberType = MemberType.Method; + break; + case 'N': + // We do not support namespaces, which do not exist in IL. + return; + case 'P': + memberType = MemberType.Property; + break; + case 'T': + memberType = MemberType.Type; + break; + default: + // Documentation comment id must start with E, F, M, P, or T + return; } - private static void ParseDeclaredId (string id, ref int index, ModuleDefinition module, List results) - { - Debug.Assert (results.Count == 0); - var memberTypeChar = PeekNextChar (id, index); - MemberType memberType; + index++; + // Note: this allows leaving out the ':'. + if (PeekNextChar (id, index) == ':') + index++; + + ParseSignaturePart (id, ref index, module, memberType, results); + } - switch (memberTypeChar) { - case 'E': - memberType = MemberType.Event; - break; - case 'F': - memberType = MemberType.Field; - break; - case 'M': - memberType = MemberType.Method; - break; - case 'N': - // We do not support namespaces, which do not exist in IL. + // Parses and resolves a fully-qualified (namespace and nested types but no assembly) member signature, + // without the member type prefix. The results include all members matching the specified member types. + public static void ParseSignaturePart (string id, ref int index, ModuleDefinition module, MemberType memberTypes, List results) + { + // Roslyn resolves types by searching namespaces top-down. + // We don't have namespace info. Instead try treating each part of a + // dotted name as a type first, then as a namespace if it fails + // to resolve to a type. + TypeDefinition? containingType = null; + var nameBuilder = new StringBuilder (); + + string name; + int arity; + + // process dotted names + while (true) { + (name, arity) = ParseTypeOrNamespaceName (id, ref index, nameBuilder); + // if we are at the end of the dotted name and still haven't resolved it to + // a type, there are no results. + if (String.IsNullOrEmpty (name)) return; - case 'P': - memberType = MemberType.Property; - break; - case 'T': - memberType = MemberType.Type; + + // no more dots, so don't loop any more + if (PeekNextChar (id, index) != '.') break; - default: - // Documentation comment id must start with E, F, M, P, or T - return; - } + // must be a namespace or type since name continues after dot index++; - // Note: this allows leaving out the ':'. - if (PeekNextChar (id, index) == ':') - index++; - // Roslyn resolves types by searching namespaces top-down. - // We don't have namespace info. Instead try treating each part of a - // dotted name as a type first, then as a namespace if it fails - // to resolve to a type. - TypeDefinition? containingType = null; - var nameBuilder = new StringBuilder (); + // try to resolve it as a type + var typeOrNamespaceName = nameBuilder.ToString (); + GetMatchingTypes (module, declaringType: containingType, name: typeOrNamespaceName, results: results); + Debug.Assert (results.Count <= 1); + if (results.Any ()) { + // the name resolved to a type + var result = results.Single (); + Debug.Assert (result is TypeDefinition); + // result becomes the new container + containingType = result as TypeDefinition; + nameBuilder.Clear (); + results.Clear (); + continue; + } - string name; - int arity; + // it didn't resolve as a type. - // process dotted names - while (true) { - name = ParseName (id, ref index); - // if we are at the end of the dotted name and still haven't resolved it to - // a type, there are no results. - if (String.IsNullOrEmpty (name)) - return; - nameBuilder.Append (name); - arity = 0; - - // has type parameters? - if (PeekNextChar (id, index) == '`') { - index++; + // only types have arity. + if (arity > 0) + return; - bool genericType = true; + // treat it as a namespace and continue building up the type name + nameBuilder.Append ('.'); + } - // method type parameters? - // note: this allows `` for type parameters - if (PeekNextChar (id, index) == '`') { - index++; - genericType = false; - } + var memberName = nameBuilder.ToString (); + GetMatchingMembers (id, ref index, module, containingType, memberName, arity, memberTypes, results); + } - arity = ReadNextInteger (id, ref index); + // Gets all members of the specified member kinds of the containing type, with + // mathing name, arity, and signature at the current index (for methods and properties). + // This will also resolve types from the given module if no containing type is given. + public static void GetMatchingMembers (string id, ref int index, ModuleDefinition module, TypeDefinition? containingType, string memberName, int arity, MemberType memberTypes, List results) + { + if (memberTypes.HasFlag (MemberType.Type)) + GetMatchingTypes (module, containingType, memberName, results); - if (genericType) { - // We need to mangle generic type names but not generic method names. - nameBuilder.Append ('`'); - nameBuilder.Append (arity); - } - } + if (containingType == null) + return; - // no more dots, so don't loop any more - if (PeekNextChar (id, index) != '.') - break; + int startIndex = index; + int endIndex = index; - // must be a namespace or type since name continues after dot - index++; + if (memberTypes.HasFlag (MemberType.Method)) { + GetMatchingMethods (id, ref index, containingType, memberName, arity, results); + endIndex = index; + index = startIndex; + } - // try to resolve it as a type - var typeOrNamespaceName = nameBuilder.ToString (); - GetMatchingTypes (module, declaringType: containingType, name: typeOrNamespaceName, results: results); - Debug.Assert (results.Count <= 1); - if (results.Any ()) { - // the name resolved to a type - var result = results.Single (); - Debug.Assert (result is TypeDefinition); - // result becomes the new container - containingType = result as TypeDefinition; - nameBuilder.Clear (); - results.Clear (); - continue; - } + if (memberTypes.HasFlag (MemberType.Property)) { + GetMatchingProperties (id, ref index, containingType, memberName, results); + endIndex = index; + index = startIndex; + } - // it didn't resolve as a type. + index = endIndex; - // only types have arity. - if (arity > 0) - return; + if (memberTypes.HasFlag (MemberType.Event)) + GetMatchingEvents (containingType, memberName, results); - // treat it as a namespace and continue building up the type name - nameBuilder.Append ('.'); - } + if (memberTypes.HasFlag (MemberType.Field)) + GetMatchingFields (containingType, memberName, results); + } - if (containingType == null && memberType != MemberType.Type) - return; + // Parses a part of a dotted declaration name, including generic definitions. + // Returns the name (either a namespace or the unmangled name of a C# type) and an arity + // which may be non-zero for generic types. + public static (string name, int arity) ParseTypeOrNamespaceName (string id, ref int index, StringBuilder nameBuilder) + { + var name = ParseName (id, ref index); + // don't parse ` after an empty name + if (string.IsNullOrEmpty (name)) + return (name, 0); - var memberName = nameBuilder.ToString (); + nameBuilder.Append (name); + var arity = 0; - switch (memberType) { - case MemberType.Method: - GetMatchingMethods (id, ref index, containingType, memberName, arity, results); - break; - case MemberType.Type: - GetMatchingTypes (module, containingType, memberName, results); - break; - case MemberType.Property: - GetMatchingProperties (id, ref index, containingType, memberName, results); - break; - case MemberType.Event: - GetMatchingEvents (containingType, memberName, results); - break; - case MemberType.Field: - GetMatchingFields (containingType, memberName, results); - break; + // has type parameters? + if (PeekNextChar (id, index) == '`') { + index++; + + bool genericType = true; + + // method type parameters? + // note: this allows `` for type parameters + if (PeekNextChar (id, index) == '`') { + index++; + genericType = false; + } + + arity = ReadNextInteger (id, ref index); + + if (genericType) { + // We need to mangle generic type names but not generic method names. + nameBuilder.Append ('`'); + nameBuilder.Append (arity); } } + + return (name, arity); } // Roslyn resolves types in a signature to their declaration by searching through namespaces. // To avoid looking for types by name in all referenced assemblies, we just represent types // that are part of a signature by their doc comment strings, and we check for matching // strings when looking for matching member signatures. - private static string? ParseTypeSymbol (string id, ref int index, IGenericParameterProvider? typeParameterContext) + static string? ParseTypeSymbol (string id, ref int index, IGenericParameterProvider? typeParameterContext) { var results = new List (); ParseTypeSymbol (id, ref index, typeParameterContext, results); @@ -224,7 +258,7 @@ private static void ParseDeclaredId (string id, ref int index, ModuleDefinition return null; } - private static void ParseTypeSymbol (string id, ref int index, IGenericParameterProvider? typeParameterContext, List results) + static void ParseTypeSymbol (string id, ref int index, IGenericParameterProvider? typeParameterContext, List results) { // Note: Roslyn has a special case that deviates from the language spec, which // allows context expressions embedded in a type reference => : @@ -280,7 +314,7 @@ private static void ParseTypeSymbol (string id, ref int index, IGenericParameter index = endIndex; } - private static void ParseTypeParameterSymbol (string id, ref int index, IGenericParameterProvider? typeParameterContext, List results) + static void ParseTypeParameterSymbol (string id, ref int index, IGenericParameterProvider? typeParameterContext, List results) { // skip the first ` Debug.Assert (PeekNextChar (id, index) == '`'); @@ -319,7 +353,7 @@ private static void ParseTypeParameterSymbol (string id, ref int index, IGeneric } } - private static void ParseNamedTypeSymbol (string id, ref int index, IGenericParameterProvider? typeParameterContext, List results) + static void ParseNamedTypeSymbol (string id, ref int index, IGenericParameterProvider? typeParameterContext, List results) { Debug.Assert (results.Count == 0); var nameBuilder = new StringBuilder (); @@ -368,7 +402,7 @@ private static void ParseNamedTypeSymbol (string id, ref int index, IGenericPara results.Add (nameBuilder.ToString ()); } - private static int ParseArrayBounds (string id, ref int index) + static int ParseArrayBounds (string id, ref int index) { index++; // skip '[' @@ -408,7 +442,7 @@ private static int ParseArrayBounds (string id, ref int index) return bounds; } - private static bool ParseTypeArguments (string id, ref int index, IGenericParameterProvider? typeParameterContext, List typeArguments) + static bool ParseTypeArguments (string id, ref int index, IGenericParameterProvider? typeParameterContext, List typeArguments) { index++; // skip over { @@ -439,7 +473,7 @@ private static bool ParseTypeArguments (string id, ref int index, IGenericParame return true; } - private static void GetMatchingTypes (ModuleDefinition module, TypeDefinition? declaringType, string name, List results) + static void GetMatchingTypes (ModuleDefinition module, TypeDefinition? declaringType, string name, List results) { Debug.Assert (module != null); @@ -463,7 +497,7 @@ private static void GetMatchingTypes (ModuleDefinition module, TypeDefinition? d } } - private static void GetMatchingMethods (string id, ref int index, TypeDefinition? type, string memberName, int arity, List results) + static void GetMatchingMethods (string id, ref int index, TypeDefinition? type, string memberName, int arity, List results) { if (type == null) return; @@ -488,6 +522,8 @@ private static void GetMatchingMethods (string id, ref int index, TypeDefinition continue; } + // note: this allows extra characters at the end + if (!AllParametersMatch (method.Parameters, parameters)) continue; @@ -511,7 +547,7 @@ private static void GetMatchingMethods (string id, ref int index, TypeDefinition index = endIndex; } - private static void GetMatchingProperties (string id, ref int index, TypeDefinition? type, string memberName, List results) + static void GetMatchingProperties (string id, ref int index, TypeDefinition? type, string memberName, List results) { if (type == null) return; @@ -547,7 +583,7 @@ private static void GetMatchingProperties (string id, ref int index, TypeDefinit index = endIndex; } - private static void GetMatchingFields (TypeDefinition? type, string memberName, List results) + static void GetMatchingFields (TypeDefinition? type, string memberName, List results) { if (type == null) return; @@ -558,7 +594,7 @@ private static void GetMatchingFields (TypeDefinition? type, string memberName, } } - private static void GetMatchingEvents (TypeDefinition? type, string memberName, List results) + static void GetMatchingEvents (TypeDefinition? type, string memberName, List results) { if (type == null) return; @@ -569,7 +605,7 @@ private static void GetMatchingEvents (TypeDefinition? type, string memberName, } } - private static bool AllParametersMatch (Collection methodParameters, List expectedParameters) + static bool AllParametersMatch (Collection methodParameters, List expectedParameters) { if (methodParameters.Count != expectedParameters.Count) return false; @@ -582,7 +618,7 @@ private static bool AllParametersMatch (Collection methodPa return true; } - private static bool ParseParameterList (string id, ref int index, IGenericParameterProvider typeParameterContext, List parameters) + static bool ParseParameterList (string id, ref int index, IGenericParameterProvider typeParameterContext, List parameters) { System.Diagnostics.Debug.Assert (typeParameterContext != null); @@ -618,14 +654,14 @@ private static bool ParseParameterList (string id, ref int index, IGenericParame return true; } - private static char PeekNextChar (string id, int index) + static char PeekNextChar (string id, int index) { return index >= id.Length ? '\0' : id[index]; } - private static readonly char[] s_nameDelimiters = { ':', '.', '(', ')', '{', '}', '[', ']', ',', '\'', '@', '*', '`', '~' }; + static readonly char[] s_nameDelimiters = { ':', '.', '(', ')', '{', '}', '[', ']', ',', '\'', '@', '*', '`', '~' }; - private static string ParseName (string id, ref int index) + static string ParseName (string id, ref int index) { string name; @@ -642,7 +678,7 @@ private static string ParseName (string id, ref int index) } // undoes dot encodings within names... - private static string DecodeName (string name) + static string DecodeName (string name) { if (name.IndexOf ('#') >= 0) return name.Replace ('#', '.'); @@ -650,7 +686,7 @@ private static string DecodeName (string name) return name; } - private static int ReadNextInteger (string id, ref int index) + static int ReadNextInteger (string id, ref int index) { int n = 0; diff --git a/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs b/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs index 75965f964a10..7f9ab64a80aa 100644 --- a/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs +++ b/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs @@ -39,7 +39,7 @@ public static void CheckUniqueParsedString (IMemberDefinition member, string inp { var module = (member as TypeDefinition)?.Module ?? member.DeclaringType?.Module; Assert.NotNull (module); - var parseResults = DocumentationSignatureParser.GetSymbolsForDeclarationId (input, module); + var parseResults = DocumentationSignatureParser.GetMembersForDocumentationSignature (input, module); Assert.AreEqual (1, parseResults.Count ()); Assert.AreEqual (member, parseResults.First ()); } @@ -74,7 +74,7 @@ public static void CheckParsedString (IMemberDefinition member, string input) { var module = (member as TypeDefinition)?.Module ?? member.DeclaringType?.Module; Assert.NotNull (module); - var parseResults = DocumentationSignatureParser.GetSymbolsForDeclarationId (input, module); + var parseResults = DocumentationSignatureParser.GetMembersForDocumentationSignature (input, module); CollectionAssert.Contains (parseResults, member); } @@ -82,7 +82,7 @@ public static void CheckUnresolvedDocumentationSignature (IMemberDefinition memb { var module = (member as TypeDefinition)?.Module ?? member.DeclaringType?.Module; Assert.NotNull (module); - var parseResults = DocumentationSignatureParser.GetSymbolsForDeclarationId (input, module); + var parseResults = DocumentationSignatureParser.GetMembersForDocumentationSignature (input, module); CollectionAssert.DoesNotContain (parseResults, member); } @@ -548,6 +548,17 @@ public void MethodMissingArgumentTypeName (int i) [ExpectUnresolvedDocumentationSignature ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.")] public class NoType { + [ExpectUnresolvedDocumentationSignature ("M:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid..Method")] + public void Method () + { + } + } + + // prevent warning about unused events + public void UseEvents () + { + OnEvent?.Invoke (null, null); + OnEventArgs?.Invoke (null, null); } [ExpectUnresolvedDocumentationSignature ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoParameterType()")] @@ -560,7 +571,14 @@ public void NoGenericParameterType (Generic g) { } - // these work, but seem like they shouldn't. + // our parser won't match fields with `, unlike roslyn. + [ExpectUnresolvedDocumentationSignature ("F:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.field`gibberish")] + public int field; + + [ExpectUnresolvedDocumentationSignature ("E:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.OnEvent`gibberish")] + public event EventHandler OnEvent; + + // the below work, but seem like they shouldn't. // see https://github.com/dotnet/roslyn/issues/44315 [ExpectExactlyResolvedDocumentationSignature ("TMono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoColon")] @@ -577,6 +595,17 @@ public void NoClosingParenWithParameters (int a) public void NoClosingBrace (Generic g) { } + + [ExpectExactlyResolvedDocumentationSignature ("F:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.fieldArgs(gibberish")] + public int fieldArgs; + + [ExpectExactlyResolvedDocumentationSignature ("E:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.OnEventArgs(gibberish")] + public event EventHandler OnEventArgs; + + [ExpectExactlyResolvedDocumentationSignature ("T:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NestedType{")] + public class NestedType + { + } } } } \ No newline at end of file From c8613ebc8c26db1d640976a9f103b07d3957f486 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Tue, 26 May 2020 18:47:56 +0000 Subject: [PATCH 8/9] PR feedback - License headers - Don't use string interpolation - Don't use extension method for GetSignaturePart - Remove unnecessary nullable context - Remove unnecessary IndexOf --- .../DocumentationSignatureGenerator.PartVisitor.cs | 4 ++++ .../Linker/DocumentationSignatureGenerator.cs | 6 ++++-- src/linker/Linker/DocumentationSignatureParser.cs | 13 +++++-------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/linker/Linker/DocumentationSignatureGenerator.PartVisitor.cs b/src/linker/Linker/DocumentationSignatureGenerator.PartVisitor.cs index f4bce27c1b7b..4fa6202efffb 100644 --- a/src/linker/Linker/DocumentationSignatureGenerator.PartVisitor.cs +++ b/src/linker/Linker/DocumentationSignatureGenerator.PartVisitor.cs @@ -1,3 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + using System; using System.Collections.Generic; using System.Diagnostics; diff --git a/src/linker/Linker/DocumentationSignatureGenerator.cs b/src/linker/Linker/DocumentationSignatureGenerator.cs index 4e20e2aa2db8..3289b189d7cf 100644 --- a/src/linker/Linker/DocumentationSignatureGenerator.cs +++ b/src/linker/Linker/DocumentationSignatureGenerator.cs @@ -1,9 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + using System; using System.Text; using Mono.Cecil; -#nullable enable - namespace Mono.Linker { /// diff --git a/src/linker/Linker/DocumentationSignatureParser.cs b/src/linker/Linker/DocumentationSignatureParser.cs index 1b21b66ffab1..a9a0c191059f 100644 --- a/src/linker/Linker/DocumentationSignatureParser.cs +++ b/src/linker/Linker/DocumentationSignatureParser.cs @@ -53,7 +53,7 @@ public static IEnumerable GetMembersForDocumentationSignature return results; } - public static string GetSignaturePart (this TypeReference type) + static string GetSignaturePart (TypeReference type) { var builder = new StringBuilder (); DocumentationSignatureGenerator.PartVisitor.Instance.VisitTypeReference (type, builder); @@ -334,7 +334,7 @@ static void ParseTypeParameterSymbol (string id, ref int index, IGenericParamete if (typeParameterContext is MethodDefinition methodContext) { var count = methodContext.HasGenericParameters ? methodContext.GenericParameters.Count : 0; if (count > 0 && methodTypeParameterIndex < count) { - results.Add ($"``{methodTypeParameterIndex}"); + results.Add ("``" + methodTypeParameterIndex); } } } else { @@ -534,7 +534,7 @@ static void GetMatchingMethods (string id, ref int index, TypeDefinition? type, continue; // if return type is specified, then it must match - if (method.ReturnType.GetSignaturePart () == returnType) { + if (GetSignaturePart (method.ReturnType) == returnType) { results.Add (method); endIndex = index; } @@ -611,7 +611,7 @@ static bool AllParametersMatch (Collection methodParameters return false; for (int i = 0; i < expectedParameters.Count; i++) { - if (methodParameters[i].ParameterType.GetSignaturePart () != expectedParameters[i]) + if (GetSignaturePart (methodParameters[i].ParameterType) != expectedParameters[i]) return false; } @@ -680,10 +680,7 @@ static string ParseName (string id, ref int index) // undoes dot encodings within names... static string DecodeName (string name) { - if (name.IndexOf ('#') >= 0) - return name.Replace ('#', '.'); - - return name; + return name.Replace ('#', '.'); } static int ReadNextInteger (string id, ref int index) From f016525b52945b263aa2e0cdfb6fae23446ba60e Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Tue, 26 May 2020 18:53:11 +0000 Subject: [PATCH 9/9] Update link for edge cases --- .../Tests/DocumentationSignatureParserTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs b/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs index 7f9ab64a80aa..2745cd63831b 100644 --- a/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs +++ b/test/Mono.Linker.Tests/Tests/DocumentationSignatureParserTests.cs @@ -578,8 +578,7 @@ public void NoGenericParameterType (Generic g) [ExpectUnresolvedDocumentationSignature ("E:Mono.Linker.Tests.DocumentationSignatureParserTests.Invalid.OnEvent`gibberish")] public event EventHandler OnEvent; - // the below work, but seem like they shouldn't. - // see https://github.com/dotnet/roslyn/issues/44315 + // the below work, but seem like they shouldn't. See https://github.com/mono/linker/issues/1214. [ExpectExactlyResolvedDocumentationSignature ("TMono.Linker.Tests.DocumentationSignatureParserTests.Invalid.NoColon")] public class NoColon