Skip to content

Commit 428d65a

Browse files
committed
improve re-mapping
1 parent f147df6 commit 428d65a

File tree

2 files changed

+111
-42
lines changed

2 files changed

+111
-42
lines changed

src/InheritDoc/CecilExtensions.cs

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ internal static class CecilExtensions
3232
// http://sourceroslyn.io/#Microsoft.CodeAnalysis.CSharp/DocumentationComments/DocumentationCommentIDVisitor.cs
3333
// http://sourceroslyn.io/#Microsoft.CodeAnalysis.CSharp/DocumentationComments/DocumentationCommentIDVisitor.PartVisitor.cs
3434
// Different compilers/tools generate different encodings, so we generate a list of candidates that includes each style.
35-
public static string GetDocID(this TypeDefinition t) => encodeTypeName(t).Select(t => "T:" + t).First();
35+
public static string GetDocID(this TypeReference t) => encodeTypeName(t).Select(t => "T:" + t).First();
3636

3737
public static IEnumerable<string> GetDocID(this EventDefinition e) => encodeTypeName(e.DeclaringType).SelectMany(t => encodeMemberName(e.Name).Select(m => "E:" + t + "." + m));
3838

@@ -158,6 +158,68 @@ public static IEnumerable<MethodDefinition> GetBaseCandidates(this MethodDefinit
158158
yield return md;
159159
}
160160

161+
public static TypeReference? GetNextBase(this TypeDefinition t, TypeDefinition bt)
162+
{
163+
var rb = t;
164+
do rb = rb.BaseType?.Resolve();
165+
while (rb is not null && rb != bt);
166+
167+
if (rb == bt)
168+
return t.BaseType!;
169+
170+
foreach (var i in t.Interfaces)
171+
{
172+
rb = i.InterfaceType.Resolve();
173+
if (rb == bt || rb.GetNextBase(bt) is not null)
174+
return i.InterfaceType;
175+
}
176+
177+
return null;
178+
}
179+
180+
public static Dictionary<string, TypeReference>? GetTypeParamMap(this TypeDefinition t, TypeDefinition? bt)
181+
{
182+
if (!t.HasGenericParameters && !(bt?.HasGenericParameters).GetValueOrDefault())
183+
return null;
184+
185+
var ctm = new Dictionary<string, TypeReference>();
186+
187+
if ((bt?.HasGenericParameters).GetValueOrDefault())
188+
{
189+
var stack = new Stack<TypeReference>();
190+
var nbt = (TypeReference)t;
191+
var nrt = t;
192+
do
193+
{
194+
nbt = nrt.GetNextBase(bt!);
195+
if (nbt is null)
196+
break;
197+
198+
nrt = nbt.Resolve();
199+
stack.Push(nbt);
200+
}
201+
while (nrt != bt);
202+
203+
if (stack.Count != 0 && stack.Pop() is GenericInstanceType gi && gi.Resolve() == bt)
204+
{
205+
foreach (var gp in bt!.GenericParameters)
206+
ctm[gp.Name] = gi.GenericArguments[gp.Position];
207+
}
208+
209+
foreach (var gb in stack.Where(b => b.IsGenericInstance).Cast<GenericInstanceType>())
210+
{
211+
var rb = gb.Resolve();
212+
foreach (var kv in ctm.Where(e => e.Value.IsGenericParameter && rb.GenericParameters.Contains(e.Value)).ToList())
213+
ctm[kv.Key] = gb.GenericArguments[rb.GenericParameters.IndexOf((GenericParameter)kv.Value)];
214+
}
215+
}
216+
217+
foreach (var gp in t.GenericParameters.Where(p => !ctm.ContainsKey(p.Name)))
218+
ctm.Add(gp.Name, gp);
219+
220+
return ctm;
221+
}
222+
161223
private static ApiLevel getApiLevel(MethodDefinition? m) => m is null ? ApiLevel.None : m.IsPrivate ? ApiLevel.Private : m.IsAssembly || m.IsFamilyAndAssembly ? ApiLevel.Internal : ApiLevel.Public;
162224

163225
private static ApiLevel getApiLevel(FieldDefinition? f) => f is null ? ApiLevel.None : f.IsPrivate ? ApiLevel.Private : f.IsAssembly || f.IsFamilyAndAssembly ? ApiLevel.Internal : ApiLevel.Public;
@@ -172,8 +234,7 @@ private static IEnumerable<MethodDefinition> getBaseCandidatesFromType(MethodDef
172234

173235
while (bt is not null)
174236
{
175-
var rbt = (bt.IsGenericInstance ? ((GenericInstanceType)bt).ElementType : bt).Resolve();
176-
237+
var rbt = bt.Resolve();
177238
if (bt.IsGenericInstance)
178239
{
179240
var gi = (GenericInstanceType)bt;
@@ -235,7 +296,7 @@ private static IEnumerable<string> encodeMethodParams(ICollection<ParameterDefin
235296
foreach (var pl in mp.Select(p => encodeTypeName(p.ParameterType)))
236297
sl = sl.SelectMany(s => pl.Select(p => s + "," + p));
237298

238-
foreach (var s in sl)
299+
foreach (string s in sl)
239300
yield return "(" + s.Substring(1) + ")";
240301
}
241302

@@ -330,11 +391,11 @@ public static RefAssemblyResolver Create(string mainAssembly, string[] refAssemb
330391

331392
private RefAssemblyResolver() { }
332393

333-
private bool isCompatibleName(AssemblyNameReference name, AssemblyNameReference cname) =>
334-
cname.Name == name.Name && cname.PublicKeyToken.SequenceEqual(name.PublicKeyToken) && cname.Version >= name.Version;
335-
336394
public AssemblyDefinition Resolve(AssemblyNameReference name)
337395
{
396+
static bool isCompatibleName(AssemblyNameReference name, AssemblyNameReference cname) =>
397+
cname.Name == name.Name && cname.PublicKeyToken.SequenceEqual(name.PublicKeyToken) && cname.Version >= name.Version;
398+
338399
if (!cache.TryGetValue(name.FullName, out var match))
339400
cache[name.FullName] = match = cache.Values.FirstOrDefault(c => isCompatibleName(name, c.Name));
340401

src/InheritDoc/InheritDocProcessor.cs

Lines changed: 43 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ private static class DocElementNames
2525
public static readonly XName Members = XName.Get("members");
2626
public static readonly XName Param = XName.Get("param");
2727
public static readonly XName TypeParam = XName.Get("typeparam");
28+
public static readonly XName TypeParamRef = XName.Get("typeparamref");
2829
public static readonly XName Overloads = XName.Get("overloads");
2930
public static readonly XName Redirect = XName.Get("redirect");
3031
public static readonly XName Returns = XName.Get("returns");
@@ -338,27 +339,54 @@ static void removeDoc(List<XNode> nodes, int pos)
338339
continue;
339340

340341
var ename = elem.Name;
342+
string? pname = (string)elem.Attribute(DocAttributeNames.Name);
341343

342344
if (ename == DocElementNames.Returns && !dm.HasReturn || ename == DocElementNames.Value && !dm.HasValue)
343345
{
344346
removeDoc(nodes, i);
345347
continue;
346348
}
347349

348-
if (ename == DocElementNames.Param || ename == DocElementNames.TypeParam)
350+
if (ename == DocElementNames.Param)
349351
{
350-
string? pname = (string)elem.Attribute(DocAttributeNames.Name);
351-
var pmap = ename == DocElementNames.Param ? dm.ParamMap : dm.TypeParamMap;
352-
353-
if (!pmap.ContainsKey(pname))
352+
if (!dm.ParamMap.ContainsKey(pname))
354353
{
355354
removeDoc(nodes, i);
356355
continue;
357356
}
358357

359-
elem.SetAttributeValue(DocAttributeNames.Name, pmap[pname]);
358+
elem.SetAttributeValue(DocAttributeNames.Name, dm.ParamMap[pname]);
360359
foreach (var pref in nodes.OfType<XElement>().DescendantNodesAndSelf().OfType<XElement>().Where(e => e.Name == (ename.LocalName + "ref") && (string)e.Attribute(DocAttributeNames.Name) == pname))
361-
pref.SetAttributeValue(DocAttributeNames.Name, pmap[pname]);
360+
pref.SetAttributeValue(DocAttributeNames.Name, dm.ParamMap[pname]);
361+
}
362+
363+
if (ename == DocElementNames.TypeParam)
364+
{
365+
if (!dm.TypeParamMap.ContainsKey(pname) || dm.TypeParamMap[pname] is not GenericParameter)
366+
{
367+
removeDoc(nodes, i);
368+
continue;
369+
}
370+
371+
elem.SetAttributeValue(DocAttributeNames.Name, dm.TypeParamMap[pname].Name);
372+
}
373+
374+
foreach (var tpr in elem.Descendants(DocElementNames.TypeParamRef).ToList())
375+
{
376+
if (!tpr.HasAttribute(DocAttributeNames.Name) || !dm.TypeParamMap.ContainsKey((string)tpr.Attribute(DocAttributeNames.Name)))
377+
{
378+
if ((tpr.PreviousNode?.IsWhiteSpace()).GetValueOrDefault())
379+
tpr.PreviousNode!.Remove();
380+
381+
tpr.Remove();
382+
continue;
383+
}
384+
385+
var tr = dm.TypeParamMap[(string)tpr.Attribute(DocAttributeNames.Name)];
386+
if (tr.IsGenericParameter)
387+
tpr.SetAttributeValue(DocAttributeNames.Name, tr.Name);
388+
else
389+
tpr.ReplaceWith(new XElement("see", new XAttribute("cref", tr.GetDocID())));
362390
}
363391

364392
// Doc inheritance rules built for compatibility with SHFB modulo the name change of the "select" attribute to "path"
@@ -504,36 +532,16 @@ private static string getTypeIDFromDocID(string docID)
504532
private class DocMatch(string cref)
505533
{
506534
private static readonly IReadOnlyDictionary<string, string> emptyMap = new Dictionary<string, string>();
535+
private static readonly IReadOnlyDictionary<string, TypeReference> emptyTypeMap = new Dictionary<string, TypeReference>();
507536

508537
public string Cref = cref;
509-
public IReadOnlyDictionary<string, string> TypeParamMap = emptyMap;
510538
public IReadOnlyDictionary<string, string> ParamMap = emptyMap;
539+
public IReadOnlyDictionary<string, TypeReference> TypeParamMap = emptyTypeMap;
511540
public bool HasReturn = false;
512541
public bool HasValue = false;
513542

514-
public DocMatch(string cref, TypeReference t, TypeReference? bt = null) : this(cref)
515-
{
516-
if (t.HasGenericParameters && (bt?.IsGenericInstance ?? true))
517-
{
518-
var tpm = new Dictionary<string, string>();
519-
520-
if (bt is not null)
521-
{
522-
var ga = ((GenericInstanceType)bt).GenericArguments;
523-
var rbt = bt.Resolve();
524-
525-
foreach (var tp in t.GenericParameters.Where(ga.Contains))
526-
tpm.Add(rbt.GenericParameters[ga.IndexOf(tp)].Name, tp.Name);
527-
}
528-
else
529-
{
530-
foreach (var tp in t.GenericParameters)
531-
tpm.Add(tp.Name, tp.Name);
532-
}
533-
534-
TypeParamMap = tpm;
535-
}
536-
}
543+
public DocMatch(string cref, TypeDefinition t, TypeReference? bt = null) : this(cref) =>
544+
TypeParamMap = t.GetTypeParamMap(bt?.Resolve()) ?? emptyTypeMap;
537545

538546
public DocMatch(string cref, MethodDefinition m, MethodDefinition? bm = null) : this(cref)
539547
{
@@ -546,14 +554,14 @@ public DocMatch(string cref, MethodDefinition m, MethodDefinition? bm = null) :
546554
ParamMap = pm;
547555
}
548556

557+
var tpm = m.DeclaringType.GetTypeParamMap(bm?.DeclaringType);
549558
if (m.HasGenericParameters)
550559
{
551-
var tpm = new Dictionary<string, string>();
560+
tpm ??= [ ];
552561
foreach (var tp in m.GenericParameters)
553-
tpm.Add(bm?.GenericParameters[tp.Position].Name ?? tp.Name, tp.Name);
554-
555-
TypeParamMap = tpm;
562+
tpm[bm?.GenericParameters[tp.Position].Name ?? tp.Name] = tp;
556563
}
564+
TypeParamMap = tpm ?? emptyTypeMap;
557565

558566
HasReturn = m.HasReturnValue();
559567
HasValue = m.IsPropertyMethod();

0 commit comments

Comments
 (0)