You've already forked linux-packaging-mono
Imported Upstream version 6.6.0.89
Former-commit-id: b39a328747c2f3414dc52e009fb6f0aa80ca2492
This commit is contained in:
parent
cf815e07e0
commit
95fdb59ea6
426
external/api-doc-tools/mdoc/Mono.Documentation/Updater/DocumentationEnumerator.cs
vendored
Normal file
426
external/api-doc-tools/mdoc/Mono.Documentation/Updater/DocumentationEnumerator.cs
vendored
Normal file
@@ -0,0 +1,426 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
|
||||
using Mono.Cecil;
|
||||
using StringList = System.Collections.Generic.List<string>;
|
||||
|
||||
using Mono.Documentation.Util;
|
||||
using Mono.Documentation.Updater.Frameworks;
|
||||
|
||||
namespace Mono.Documentation.Updater
|
||||
{
|
||||
public class DocumentationEnumerator
|
||||
{
|
||||
|
||||
public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
|
||||
{
|
||||
return GetDocumentationTypes (assembly, forTypes, null);
|
||||
}
|
||||
|
||||
protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
|
||||
{
|
||||
foreach (TypeDefinition type in assembly.GetTypes ())
|
||||
{
|
||||
if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
|
||||
continue;
|
||||
if (seen != null && seen.Contains (type.FullName))
|
||||
continue;
|
||||
yield return type;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type, FrameworkTypeEntry typeEntry)
|
||||
{
|
||||
foreach (XmlElement oldmember in basefile.SelectNodes ("Type/Members/Member"))
|
||||
{
|
||||
if (oldmember.GetAttribute ("__monodocer-seen__") == "true")
|
||||
{
|
||||
oldmember.RemoveAttribute ("__monodocer-seen__");
|
||||
continue;
|
||||
}
|
||||
if (oldmember.ParentNode == null)
|
||||
continue;
|
||||
|
||||
MemberReference m = GetMember (type, new DocumentationMember (oldmember, typeEntry));
|
||||
if (m == null)
|
||||
{
|
||||
yield return new DocsNodeInfo (oldmember);
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return new DocsNodeInfo (oldmember, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
|
||||
{
|
||||
string membertype = member.MemberType;
|
||||
|
||||
string returntype = member.ReturnType;
|
||||
|
||||
string docName = member.MemberName;
|
||||
|
||||
string[] docTypeParams = GetTypeParameters (docName, member.TypeParameters);
|
||||
|
||||
// If we're using 'magic types', then we might get false positives ... in those cases, we keep searching
|
||||
MemberReference likelyCandidate = null;
|
||||
|
||||
// Loop through all members in this type with the same name
|
||||
var reflectedMembers = GetReflectionMembers (type, docName, membertype).ToArray ();
|
||||
foreach (MemberReference mi in reflectedMembers)
|
||||
{
|
||||
bool matchedMagicType = false;
|
||||
if (mi is TypeDefinition) continue;
|
||||
if (MDocUpdater.GetMemberType (mi) != membertype) continue;
|
||||
|
||||
if (MDocUpdater.IsPrivate (mi))
|
||||
continue;
|
||||
|
||||
IList<ParameterDefinition> pis = null;
|
||||
string[] typeParams = null;
|
||||
if (mi is MethodDefinition)
|
||||
{
|
||||
MethodDefinition mb = (MethodDefinition)mi;
|
||||
pis = mb.Parameters;
|
||||
if (mb.IsGenericMethod ())
|
||||
{
|
||||
IList<GenericParameter> args = mb.GenericParameters;
|
||||
typeParams = args.Select (p => p.Name).ToArray ();
|
||||
}
|
||||
}
|
||||
else if (mi is PropertyDefinition)
|
||||
pis = ((PropertyDefinition)mi).Parameters;
|
||||
|
||||
// check type parameters
|
||||
int methodTcount = member.TypeParameters == null ? 0 : member.TypeParameters.Count;
|
||||
int reflectionTcount = typeParams == null ? 0 : typeParams.Length;
|
||||
if (methodTcount != reflectionTcount)
|
||||
continue;
|
||||
|
||||
// check member parameters
|
||||
int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
|
||||
int pcount = pis == null ? 0 : pis.Count;
|
||||
if (mcount != pcount)
|
||||
continue;
|
||||
|
||||
MethodDefinition mDef = mi as MethodDefinition;
|
||||
if (mDef != null && !mDef.IsConstructor)
|
||||
{
|
||||
// Casting operators can overload based on return type.
|
||||
string rtype = GetReplacedString (
|
||||
MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
|
||||
typeParams, docTypeParams);
|
||||
string originalRType = rtype;
|
||||
if (MDocUpdater.SwitchingToMagicTypes)
|
||||
{
|
||||
rtype = NativeTypeManager.ConvertFromNativeType (rtype);
|
||||
|
||||
}
|
||||
if ((returntype != rtype && originalRType == rtype) ||
|
||||
(MDocUpdater.SwitchingToMagicTypes && returntype != originalRType && returntype != rtype && originalRType != rtype))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (originalRType != rtype)
|
||||
matchedMagicType = true;
|
||||
}
|
||||
|
||||
if (pcount == 0)
|
||||
return mi;
|
||||
bool good = true;
|
||||
for (int i = 0; i < pis.Count; i++)
|
||||
{
|
||||
bool isRefType = pis[i].ParameterType is ByReferenceType;
|
||||
|
||||
string paramType = GetReplacedString (
|
||||
MDocUpdater.GetDocParameterType (pis[i].ParameterType),
|
||||
typeParams, docTypeParams);
|
||||
|
||||
// if magictypes, replace paramType to "classic value" ... so the comparison works
|
||||
string originalParamType = paramType;
|
||||
if (MDocUpdater.SwitchingToMagicTypes)
|
||||
{
|
||||
paramType = NativeTypeManager.ConvertFromNativeType (paramType);
|
||||
}
|
||||
|
||||
string xmlMemberType = member.Parameters[i];
|
||||
|
||||
bool xmlIsRefType = xmlMemberType.Contains ('&');
|
||||
bool refTypesMatch = isRefType == xmlIsRefType;
|
||||
|
||||
if (!refTypesMatch) {
|
||||
good = false;
|
||||
break;
|
||||
}
|
||||
|
||||
xmlMemberType = xmlIsRefType ? xmlMemberType.Substring (0, xmlMemberType.Length - 1) : xmlMemberType;
|
||||
|
||||
if ((!paramType.Equals (xmlMemberType) && paramType.Equals (originalParamType)) ||
|
||||
(MDocUpdater.SwitchingToMagicTypes && !originalParamType.Equals (xmlMemberType) && !paramType.Equals (xmlMemberType) && !paramType.Equals (originalParamType)))
|
||||
{
|
||||
|
||||
// did not match ... if we're dropping the namespace, and the paramType has the dropped
|
||||
// namespace, we should see if it matches when added
|
||||
bool stillDoesntMatch = true;
|
||||
if (MDocUpdater.HasDroppedNamespace (type) && paramType.StartsWith (MDocUpdater.droppedNamespace))
|
||||
{
|
||||
string withDroppedNs = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, xmlMemberType);
|
||||
|
||||
stillDoesntMatch = withDroppedNs != paramType;
|
||||
}
|
||||
|
||||
if (stillDoesntMatch)
|
||||
{
|
||||
good = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (originalParamType != paramType)
|
||||
matchedMagicType = true;
|
||||
|
||||
}
|
||||
if (!good) continue;
|
||||
|
||||
if (MDocUpdater.SwitchingToMagicTypes && likelyCandidate == null && matchedMagicType)
|
||||
{
|
||||
// we matched this on a magic type conversion ... let's keep going to see if there's another one we should look at that matches more closely
|
||||
likelyCandidate = mi;
|
||||
continue;
|
||||
}
|
||||
|
||||
return mi;
|
||||
}
|
||||
|
||||
return likelyCandidate;
|
||||
}
|
||||
|
||||
static string[] GetTypeParameters (string docName, IEnumerable<string> knownParameters)
|
||||
{
|
||||
if (docName[docName.Length - 1] != '>')
|
||||
return null;
|
||||
StringList types = new StringList ();
|
||||
int endToken = docName.Length - 2;
|
||||
int i = docName.Length - 2;
|
||||
do
|
||||
{
|
||||
if (docName[i] == ',' || docName[i] == '<')
|
||||
{
|
||||
types.Add (docName.Substring (i + 1, endToken - i));
|
||||
endToken = i - 1;
|
||||
}
|
||||
if (docName[i] == '<')
|
||||
break;
|
||||
} while (--i >= 0);
|
||||
|
||||
types.Reverse ();
|
||||
var arrayTypes = types.ToArray ();
|
||||
|
||||
if (knownParameters != null && knownParameters.Any () && arrayTypes.Length != knownParameters.Count ())
|
||||
return knownParameters.ToArray ();
|
||||
else
|
||||
return arrayTypes;
|
||||
}
|
||||
|
||||
public static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName, string memberType)
|
||||
{
|
||||
return GetReflectionMembersCore (type, docName, memberType)
|
||||
.Distinct ();
|
||||
}
|
||||
|
||||
private static IEnumerable<MemberReference> GetReflectionMembersCore (TypeDefinition type, string docName, string memberType)
|
||||
{
|
||||
// In case of dropping the namespace, we have to remove the dropped NS
|
||||
// so that docName will match what's in the assembly/type
|
||||
if (MDocUpdater.HasDroppedNamespace (type) && docName.StartsWith (MDocUpdater.droppedNamespace + "."))
|
||||
{
|
||||
int droppedNsLength = MDocUpdater.droppedNamespace.Length;
|
||||
docName = docName.Substring (droppedNsLength + 1, docName.Length - droppedNsLength - 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// need to worry about 4 forms of //@MemberName values:
|
||||
// 1. "Normal" (non-generic) member names: GetEnumerator
|
||||
// - Lookup as-is.
|
||||
// 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
|
||||
// - try as-is, and try type.member (due to "kludge" for property
|
||||
// support.
|
||||
// 3. "Normal" Generic member names: Sort<T> (CSC)
|
||||
// - need to remove generic parameters --> "Sort"
|
||||
// 4. Explicitly-implemented interface members for generic interfaces:
|
||||
// -- System.Collections.Generic.IEnumerable<T>.Current
|
||||
// - Try as-is, and try type.member, *keeping* the generic parameters.
|
||||
// --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
|
||||
// 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
|
||||
// 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
|
||||
// this as (1) or (2).
|
||||
if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1)
|
||||
{
|
||||
int memberCount = 0;
|
||||
|
||||
// Cases 1 & 2
|
||||
foreach (MemberReference mi in type.GetMembers (docName))
|
||||
{
|
||||
memberCount++;
|
||||
yield return mi;
|
||||
}
|
||||
|
||||
if (memberCount == 0 && CountChars (docName, '.') > 0)
|
||||
{
|
||||
|
||||
Func<MemberReference, bool> verifyInterface = (member) =>
|
||||
{
|
||||
var meth = member as MethodDefinition;
|
||||
|
||||
if (meth == null && member is PropertyReference)
|
||||
{
|
||||
var propertyDefinition = ((PropertyReference)member).Resolve ();
|
||||
meth = propertyDefinition.GetMethod ?? propertyDefinition.SetMethod;
|
||||
}
|
||||
return meth != null && (member.Name.Equals (".ctor") || DocUtils.IsExplicitlyImplemented (meth));
|
||||
};
|
||||
|
||||
|
||||
// might be a property; try only type.member instead of
|
||||
// namespace.type.member.
|
||||
var typeMember = DocUtils.GetTypeDotMember (docName);
|
||||
var memberName = DocUtils.GetMember (docName);
|
||||
foreach (MemberReference mi in
|
||||
type.GetMembers (typeMember).Where (verifyInterface))
|
||||
{
|
||||
memberCount++;
|
||||
yield return mi;
|
||||
}
|
||||
|
||||
// some VB libraries use just the member name
|
||||
foreach (MemberReference mi in
|
||||
type.GetMembers (memberName).Where (verifyInterface))
|
||||
{
|
||||
memberCount++;
|
||||
yield return mi;
|
||||
}
|
||||
|
||||
// some VB libraries use a `typemember` naming convention
|
||||
foreach (MemberReference mi in
|
||||
type.GetMembers (typeMember.Replace (".", "")).Where (verifyInterface))
|
||||
{
|
||||
memberCount++;
|
||||
yield return mi;
|
||||
}
|
||||
|
||||
// if we still haven't found the member, there are some VB libraries
|
||||
// that use a different interface name for implementation.
|
||||
if (memberCount == 0)
|
||||
{
|
||||
foreach (MemberReference mi in
|
||||
type
|
||||
.GetMembers()
|
||||
.Where(m => m.Name.StartsWith("I", StringComparison.InvariantCultureIgnoreCase) &&
|
||||
m.Name.EndsWith(memberName, StringComparison.InvariantCultureIgnoreCase))
|
||||
.Where(verifyInterface))
|
||||
{
|
||||
memberCount++;
|
||||
yield return mi;
|
||||
}
|
||||
}
|
||||
|
||||
if (memberCount == 0 && memberType == "Property")
|
||||
{
|
||||
foreach (MemberReference mr in type.GetMembers().Where(x => x is PropertyDefinition))
|
||||
{
|
||||
var method = ((PropertyDefinition) mr).GetMethod ?? ((PropertyDefinition) mr).SetMethod;
|
||||
if (method?.Overrides != null && method.Overrides.Any())
|
||||
{
|
||||
DocUtils.GetInfoForExplicitlyImplementedMethod(method, out var iface, out var ifaceMethod);
|
||||
var newName = DocUtils.GetMemberForProperty(ifaceMethod.Name);
|
||||
if (newName == memberName && verifyInterface(mr) && docName.Contains (iface.Name))
|
||||
yield return mr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
yield break;
|
||||
}
|
||||
// cases 3 & 4
|
||||
int numLt = 0;
|
||||
int numDot = 0;
|
||||
int startLt, startType, startMethod;
|
||||
startLt = startType = startMethod = -1;
|
||||
for (int i = 0; i < docName.Length; ++i)
|
||||
{
|
||||
switch (docName[i])
|
||||
{
|
||||
case '<':
|
||||
if (numLt == 0)
|
||||
{
|
||||
startLt = i;
|
||||
}
|
||||
++numLt;
|
||||
break;
|
||||
case '>':
|
||||
--numLt;
|
||||
if (numLt == 0 && (i + 1) < docName.Length)
|
||||
// there's another character in docName, so this <...> sequence is
|
||||
// probably part of a generic type -- case 4.
|
||||
startLt = -1;
|
||||
break;
|
||||
case '.':
|
||||
startType = startMethod;
|
||||
startMethod = i;
|
||||
++numDot;
|
||||
break;
|
||||
}
|
||||
}
|
||||
string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
|
||||
// case 3
|
||||
foreach (MemberReference mi in type.GetMembers (refName))
|
||||
yield return mi;
|
||||
|
||||
// case 4
|
||||
foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
|
||||
yield return mi;
|
||||
|
||||
// If we _still_ haven't found it, we've hit another generic naming issue:
|
||||
// post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
|
||||
// explicitly-implemented METHOD names (not properties), e.g.
|
||||
// "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
|
||||
// instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
|
||||
// which the XML docs will contain.
|
||||
//
|
||||
// Alas, we can't derive the Mono name from docName, so we need to iterate
|
||||
// over all member names, convert them into CSC format, and compare... :-(
|
||||
if (numDot == 0)
|
||||
yield break;
|
||||
foreach (MemberReference mi in type.GetMembers ())
|
||||
{
|
||||
if (MDocUpdater.GetMemberName (mi) == docName)
|
||||
yield return mi;
|
||||
}
|
||||
}
|
||||
|
||||
static string GetReplacedString (string typeName, string[] from, string[] to)
|
||||
{
|
||||
if (from == null)
|
||||
return typeName;
|
||||
for (int i = 0; i < from.Length; ++i)
|
||||
typeName = typeName.Replace (from[i], to[i]);
|
||||
return typeName;
|
||||
}
|
||||
|
||||
private static int CountChars (string s, char c)
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = 0; i < s.Length; ++i)
|
||||
{
|
||||
if (s[i] == c)
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user