Skip to content

Commit 328f330

Browse files
committed
v1.1.0
1 parent b513259 commit 328f330

File tree

10 files changed

+73
-41
lines changed

10 files changed

+73
-41
lines changed

build/Common.props

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
<PackageTags>Inherit, XML, Documentation, Comments, MSBuild</PackageTags>
2020
<PackageReleaseNotes>See $(RepositoryUrl)/releases for release-specific notes.</PackageReleaseNotes>
2121

22-
<LangVersion>8</LangVersion>
22+
<LangVersion>9</LangVersion>
2323
<Features>strict</Features>
2424
<Nullable>annotations</Nullable>
2525
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@@ -37,6 +37,7 @@
3737

3838
<AssemblyInfoFile>$(IntDir)_AssemblyInfo.cs</AssemblyInfoFile>
3939
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)$(Owners).snk</AssemblyOriginatorKeyFile>
40+
<PublicKey>0024000004800000940000000602000000240000525341310004000001000100e155a9524829be97329a8ccbeec33e5967353179a5d267fc141df370d38ef98d3f21c0852173b0968b96245bbe3c60f0053b2d61c6b326b26572cdeabf14c7cf29421e09d0031017d89f5dce69ab90b8d0d962dc86efbf5eb4afb55bc043810039cfc93d1dcbd511addbcdaabda6cd70270c568aa5d6a26dd5e5edd22de035d0</PublicKey>
4041
</PropertyGroup>
4142

4243
<PropertyGroup Condition="'$(Configuration)'!='Debug'">
@@ -52,11 +53,8 @@
5253

5354
<ItemGroup Condition="'$(Configuration)'=='Dist' Or '$(Configuration)'=='Coverage'">
5455
<None Include="$(MSBuildThisFileDirectory)$(Owners).png" Pack="true" PackagePath="package" />
56+
<None Include="$(RepositoryRoot)license" Pack="true" PackagePath="package" />
5557
<SourceRoot Include="$(RepositoryRoot)" />
5658
</ItemGroup>
5759

58-
<ItemGroup Condition="'$(TargetFrameworkIdentifier)'=='.NETFramework'">
59-
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="all" />
60-
</ItemGroup>
61-
6260
</Project>

readme.md

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
InheritDoc
44
==========
55

6-
This [MSBuild Task]( https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-tasks) automatically replaces `<inheritdoc />` tags in your .NET XML documentation with the actual inherited docs. By integrating with MSBuild, this tool has access to the exact arguments passed to the compiler -- including all assembly references -- making it both simpler and more capable than other documentation post-processing tools. As it processes `<inheritdoc />` elements, it is able to more accurately resolve base types whether they come from the target framework, referenced NuGet packages, or project references. This means it can be more clever about mapping documentation from base types and members to yours. For example, it can identify when you change the name of a method parameter from the base type’s definition and update the documentation accordingly. It can also remove documentation for non-public types/members to reduce the size of your published XML docs.
6+
This [MSBuild Task]( https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-tasks) automatically replaces `<inheritdoc />` tags in your .NET XML documentation with the actual inherited docs. By integrating with MSBuild, this tool has access to the exact arguments passed to the compiler -- including all assembly references -- making it both simpler and more capable than other documentation post-processing tools. As it processes `<inheritdoc />` elements, it is able to accurately resolve base types whether they come from the target framework, referenced NuGet packages, or project references. This allows intelligent mapping of documentation from base types and members to yours. For example, it can identify when you change the name of a method parameter from the base type’s definition and update the documentation accordingly. It can also remove documentation for non-public types/members to reduce the size of your published XML docs.
77

88
How to Use It
99
-------------
1010

1111
1) Add some `<inheritdoc />` tags to your XML documentation comments.
1212

13-
This tool’s handling of `<inheritdoc />` tags is based on the draft [design document]( https://github.com/dotnet/csharplang/blob/812e220fe2b964d17f353cb684aa341418618b6e/proposals/inheritdoc.md) used for the new prototype Roslyn support, which is in turn based on the `<inheritdoc />` support in [Sandcastle Help File Builder]( https://ewsoftware.github.io/XMLCommentsGuide/html/86453FFB-B978-4A2A-9EB5-70E118CA8073.htm#TopLevelRules) (SHFB).
13+
This tool’s handling of `<inheritdoc />` tags is based on the draft [design document]( https://github.com/dotnet/csharplang/blob/812e220fe2b964d17f353cb684aa341418618b6e/proposals/inheritdoc.md) used for Roslyn's support in Visual Studio, which is in turn based on the `<inheritdoc />` support in [Sandcastle Help File Builder]( https://ewsoftware.github.io/XMLCommentsGuide/html/86453FFB-B978-4A2A-9EB5-70E118CA8073.htm#TopLevelRules) (SHFB).
1414

1515
2) Add the [SauceControl.InheritDoc](https://www.nuget.org/packages/SauceControl.InheritDoc) NuGet package reference to your project.
1616

@@ -30,6 +30,11 @@ This enhances the new support for `<inheritdoc />` in Roslyn (available starting
3030
Some Examples
3131
-------------
3232

33+
Click to expand samples:
34+
35+
<details>
36+
<summary>Basic <code>&lt;inheritdoc /&gt;</code> Usage (Automatic Inheritance)</summary>
37+
3338
Consider the following C#
3439

3540
```C#
@@ -150,8 +155,10 @@ Once processed, the output XML documentation will look like this (results abbrev
150155
</member>
151156
```
152157

153-
Advanced Examples
154-
-----------------
158+
</details>
159+
160+
<details>
161+
<summary>Namespace Documentation</summary>
155162

156163
Although the .NET compilers [don't allow](https://github.com/dotnet/csharplang/issues/315) adding namespace documentation comments, some tools (including SHFB) have a [convention](https://stackoverflow.com/a/52381674/4926931) for declaring them in code. InheritDoc follows this convention.
157164

@@ -173,6 +180,10 @@ Will output:
173180
</member>
174181
```
175182

183+
</details>
184+
185+
<details>
186+
<summary>Explicit Inheritance</summary>
176187

177188
InheritDoc also supports the `path` attribute defined in the Roslyn draft design doc, which is analogous to the `select` attribute in SHFB.
178189

@@ -198,12 +209,14 @@ Outputs:
198209

199210
Notice the `param` element for `message` was excluded automatically because there was no matching parameter on the target constructor, however with a nested `<inheritdoc />` and a custom selector, we were able to extract the contents from that `param` element into a new one with the correct name.
200211

212+
</details>
213+
201214
Configuration
202215
-------------
203216

204217
#### Enabling/Disabling InheritDoc
205218

206-
InheritDoc is enabled by default for all normal builds. It can be disabled by setting the `InheritDocEnabled` MSBuild property in your project. This may be used if you wish to skip the overhead of running InheritDoc on debug builds, for example.
219+
InheritDoc is enabled by default for all non-debug builds. It can be enabled or disabled explicitly by setting the `InheritDocEnabled` MSBuild property in your project.
207220

208221
```XML
209222
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
@@ -263,6 +276,30 @@ Warnings can be selectively disabled with the MSBuild standard `NoWarn` property
263276
|IDT003| May indicate you used `<inheritdoc />` on a type/member with no identifiable base. You may correct this warning by using the `cref` attribute to identify the base explicitly. |
264277
|IDT004| May indicate an incorrect XPath value in a `path` attribute or a duplicate/superfluous or self-referencing `<inheritdoc />` tag. |
265278

279+
#### Using InheritDoc With Multi-Targeted Projects
280+
281+
If you are multi-targeting using the new(er) SDK-style projects and the `TargetFrameworks` property, you must ensure that you are not generating multiple XML documentation outputs to the same file path.
282+
283+
If you configure the XML documentation output from the project property page in Visual Studio, you may end up with something like:
284+
285+
```XML
286+
<PropertyGroup>
287+
<DocumentationFile>MyProject.xml</DocumentationFile> <!-- NOOOOOOO! -->
288+
</PropertyGroup>
289+
```
290+
291+
The above configuration will create a single `MyProject.xml` file in your project root for all target frameworks and all build configurations. Worse, since MSBuild builds multiple target framework outputs in parallel, you may create a race condition for access to that file.
292+
293+
The simpler configuration, supported in all .NET Core SDK versions, is:
294+
295+
```XML
296+
<PropertyGroup>
297+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
298+
</PropertyGroup>
299+
```
300+
301+
This will automatically name your XML file the same as the assembly name and will create it in the correct `obj` folder alongside the assembly.
302+
266303
Known Issues
267304
------------
268305

@@ -288,4 +325,10 @@ If this displeases you, you may register your discontent by commenting on the [a
288325
Troubleshooting
289326
---------------
290327

291-
When it runs, `InheritDocTask` will log a success message to the build output, telling you what it did. If you don't see the message, it didn't run for some reason. Check the detailed output from MSBuild (e.g. `dotnet build -v detailed`) and look for `InheritDoc` in the logs for clues. Issue reports are, of course, welcome with good repro steps.
328+
When it runs, `InheritDocTask` will log a success message to the build output for each processed file, telling you what it did. For example:
329+
330+
```
331+
InheritDocTask replaced 55 of 55 inheritdoc tags and removed 60 non-public member docs in /path/to/MyProject.xml
332+
```
333+
334+
If you don't see the message(s), it didn't run for some reason. Check the detailed output from MSBuild (e.g. `dotnet build -v detailed`) and look for `InheritDoc` in the logs for clues. Issue reports are, of course, welcome with good repro steps.

src/Directory.Build.props

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,8 @@
1919
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="all" />
2020
</ItemGroup>
2121

22+
<ItemGroup Condition="'$(Configuration)'!='Dist'">
23+
<InternalsVisibleTo Include="$(MSBuildProjectName).Test" />
24+
</ItemGroup>
25+
2226
</Project>

src/Directory.Build.targets

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,7 @@
11
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
22

3-
<Target Name="AddInternalsVisibleToAttributes" BeforeTargets="GetAssemblyAttributes">
4-
<PropertyGroup>
5-
<SnkPublicKeyHash>0024000004800000940000000602000000240000525341310004000001000100e155a9524829be97329a8ccbeec33e5967353179a5d267fc141df370d38ef98d3f21c0852173b0968b96245bbe3c60f0053b2d61c6b326b26572cdeabf14c7cf29421e09d0031017d89f5dce69ab90b8d0d962dc86efbf5eb4afb55bc043810039cfc93d1dcbd511addbcdaabda6cd70270c568aa5d6a26dd5e5edd22de035d0</SnkPublicKeyHash>
6-
</PropertyGroup>
7-
8-
<ItemGroup>
9-
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
10-
<_Parameter1>$(MSBuildProjectName).Test,PublicKey=$(SnkPublicKeyHash)</_Parameter1>
11-
</AssemblyAttribute>
12-
</ItemGroup>
13-
</Target>
14-
153
<!-- Work around https://github.com/Microsoft/msbuild/issues/3412 by writing the non-string assembly attributes manually -->
16-
<Target Name="AddNonStringAssemblyInfoAttributes" AfterTargets="CoreGenerateAssemblyInfo" Outputs="$(AssemblyInfoFile)">
17-
4+
<Target Name="_AddNonStringAssemblyInfoAttributes" AfterTargets="CoreGenerateAssemblyInfo" Outputs="$(AssemblyInfoFile)">
185
<ItemGroup>
196
<AssemblyInfoLines Include="[assembly:System.CLSCompliant(true)]" />
207
<AssemblyInfoLines Include="[assembly:System.Runtime.InteropServices.ComVisible(false)]" />

src/InheritDoc/CecilExtensions.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ public static IEnumerable<TypeReference> GetBaseCandidates(this TypeDefinition t
105105
{
106106
var it = t;
107107

108-
while (t.BaseType != null && !ignoredBaseTypes.Contains(t.BaseType.FullName))
108+
while (t.BaseType is not null && !ignoredBaseTypes.Contains(t.BaseType.FullName))
109109
{
110110
yield return t.BaseType;
111111
t = t.BaseType.Resolve();
@@ -114,7 +114,7 @@ public static IEnumerable<TypeReference> GetBaseCandidates(this TypeDefinition t
114114
foreach (var i in it.Interfaces)
115115
yield return i.InterfaceType;
116116

117-
if (t.BaseType != null && ignoredBaseTypes.Contains(t.BaseType.FullName))
117+
if (t.BaseType is not null && ignoredBaseTypes.Contains(t.BaseType.FullName))
118118
yield return t.BaseType;
119119
}
120120

@@ -140,7 +140,7 @@ private static IEnumerable<MethodDefinition> getBaseCandidatesFromType(MethodDef
140140
{
141141
var genMap = new Dictionary<TypeReference, TypeReference>();
142142

143-
while (bt != null)
143+
while (bt is not null)
144144
{
145145
var rbt = (bt.IsGenericInstance ? ((GenericInstanceType)bt).ElementType : bt).Resolve();
146146

@@ -247,7 +247,7 @@ private static string encodeTypeName(TypeReference tr)
247247

248248
internal sealed class RefAssemblyResolver : IAssemblyResolver
249249
{
250-
private readonly Dictionary<string, AssemblyDefinition> cache = new Dictionary<string, AssemblyDefinition>(StringComparer.Ordinal);
250+
private readonly Dictionary<string, AssemblyDefinition> cache = new(StringComparer.Ordinal);
251251

252252
public static RefAssemblyResolver Create(string mainAssembly, string[] refAssemblies)
253253
{

src/InheritDoc/InheritDoc.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
<ItemGroup>
2222
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="15.9.20" PrivateAssets="all" />
23-
<PackageReference Include="Mono.Cecil" Version="0.11.2" PrivateAssets="all" />
23+
<PackageReference Include="Mono.Cecil" Version="0.11.3" PrivateAssets="all" />
2424
</ItemGroup>
2525

2626
</Project>

src/InheritDoc/InheritDocProcessor.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ static XDocument loadDoc(string path)
101101
beforeCount = docMembers.Descendants(DocElementNames.InheritDoc).Count(dm => !dm.Ancestors(DocElementNames.Member).Any(m => m.HasAttribute(DocAttributeNames._trimmed)));
102102

103103
var mem = default(XElement);
104-
while ((mem = docMembers.Elements(DocElementNames.Member).FirstOrDefault(m => isInheritDocCandidate(m))) != null)
104+
while ((mem = docMembers.Elements(DocElementNames.Member).FirstOrDefault(m => isInheritDocCandidate(m))) is not null)
105105
replaceInheritDoc(docPath, mem, docMap, docMembers, refDocs, logger);
106106

107107
foreach (var md in docMembers.Elements(DocElementNames.Member).Where(m => m.HasAttribute(DocAttributeNames._visited)))
@@ -214,7 +214,7 @@ private static IDictionary<string, IEnumerable<DocMatch>> generateDocMap(IList<T
214214
var crefs = methDocs.Descendants(DocElementNames.InheritDoc).Select(i => (string)i.Attribute(DocAttributeNames.Cref)).Where(c => !string.IsNullOrWhiteSpace(c)).ToHashSet();
215215
var dml = new List<DocMatch>();
216216

217-
var bases = om != null ? (new[] { om }).Concat(m.GetBaseCandidates()) : m.GetBaseCandidates();
217+
var bases = om is not null ? (new[] { om }).Concat(m.GetBaseCandidates()) : m.GetBaseCandidates();
218218
foreach (var bm in bases)
219219
{
220220
string cref = bm.GetDocID();
@@ -266,7 +266,7 @@ private static void replaceInheritDoc(string file, XElement mem, IDictionary<str
266266
var dm = dml?.FirstOrDefault(d => d.Cref == cref) ?? new DocMatch(cref!);
267267

268268
var doc = findDocsByID(refDocs.Root, cref!).FirstOrDefault();
269-
if (doc != null)
269+
if (doc is not null)
270270
{
271271
inheritDocs(file, memID, inh, doc, dm, logger);
272272
continue;
@@ -319,7 +319,7 @@ static void removeDoc(List<XNode> nodes, int pos)
319319
var nodes = (docBase?.XPathSelectNodes(xpath) ?? XElement.EmptySequence).ToList();
320320
for (int i = nodes.Count - 1; i >= 0; --i)
321321
{
322-
if (!(nodes[i] is XElement elem))
322+
if (nodes[i] is not XElement elem)
323323
continue;
324324

325325
var ename = elem.Name;
@@ -355,7 +355,7 @@ static void removeDoc(List<XNode> nodes, int pos)
355355
mpath += "[" + string.Join(" and ", matchAttributes.Select(a => "@" + a.Name + "='" + SecurityElement.Escape(a.Value) + "'")) + "]";
356356

357357
var pmatch = inh.Parent.XPathSelectElement(mpath);
358-
if (ename == DocElementNames.Overloads || (pmatch != null && (inheritSkipIfExists.Contains(ename) || matchAttributes.Any())))
358+
if (ename == DocElementNames.Overloads || (pmatch is not null && (inheritSkipIfExists.Contains(ename) || matchAttributes.Any())))
359359
removeDoc(nodes, i);
360360
}
361361

@@ -499,7 +499,7 @@ public DocMatch(string cref, TypeReference t, TypeReference? bt = null) : this(c
499499
{
500500
var tpm = new Dictionary<string, string>();
501501

502-
if (bt != null)
502+
if (bt is not null)
503503
{
504504
var ga = ((GenericInstanceType)bt).GenericArguments;
505505
var rbt = bt.Resolve();

src/InheritDoc/Util.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
internal static class Util
1212
{
13-
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> e) => new HashSet<T>(e);
13+
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> e) => new(e);
1414

1515
public static IEnumerable<T> SelectManyRecursive<T>(this IEnumerable<T> e, Func<T, IEnumerable<T>> selector) =>
1616
e.Any() ? e.Concat(e.SelectMany(selector).SelectManyRecursive(selector)) : e;
@@ -22,9 +22,8 @@ public static IEnumerable<T> SelectManyRecursive<T>(this IEnumerable<T> e, Func<
2222
public static int SourceColumn(this XElement e) => e is IXmlLineInfo li && li.HasLineInfo() ? li.LinePosition : 0;
2323

2424
public static bool IsWhiteSpace(this XNode n) =>
25-
n.NodeType == XmlNodeType.Whitespace ||
26-
n.NodeType == XmlNodeType.SignificantWhitespace ||
27-
(n.NodeType == XmlNodeType.Text && string.IsNullOrWhiteSpace(((XText)n).Value));
25+
(n.NodeType is XmlNodeType.Whitespace or XmlNodeType.SignificantWhitespace) ||
26+
(n.NodeType is XmlNodeType.Text && string.IsNullOrWhiteSpace(((XText)n).Value));
2827

2928
public static IEnumerable<XNode> XPathSelectNodes(this XElement e, string xpath) =>
3029
(e.XPathEvaluate(xpath) as IEnumerable)?.Cast<XNode>() ?? Enumerable.Empty<XNode>();

tests/Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
</PropertyGroup>
1717

1818
<ItemGroup Condition="'$(Configuration)'=='Coverage'">
19-
<PackageReference Include="coverlet.msbuild" Version="2.8.0" PrivateAssets="all" />
19+
<PackageReference Include="coverlet.msbuild" Version="2.9.0" PrivateAssets="all" />
2020
</ItemGroup>
2121

2222
</Project>

tests/InheritDoc.Test/InheritDoc.Test.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<PackageReference Include="MSTest.TestAdapter" Version="2.1.0" />
1212
<PackageReference Include="MSTest.TestFramework" Version="2.1.0" />
1313
<PackageDownload Include="Microsoft.NETCore.App.Ref" Version="[3.1.0]" />
14+
<PackageDownload Include="Microsoft.NETFramework.ReferenceAssemblies.net48" Version="[1.0.0]" />
1415
</ItemGroup>
1516

1617
<ItemGroup>

0 commit comments

Comments
 (0)