{"id":31922967,"url":"https://github.com/saucecontrol/inheritdoc","last_synced_at":"2025-10-13T23:28:41.579Z","repository":{"id":46241193,"uuid":"211411879","full_name":"saucecontrol/InheritDoc","owner":"saucecontrol","description":"Automatically replace \u003cinheritdoc /\u003e tags in .NET XML comments with inherited documentation at build time","archived":false,"fork":false,"pushed_at":"2024-02-22T00:56:43.000Z","size":1491,"stargazers_count":67,"open_issues_count":1,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-05-28T12:52:28.133Z","etag":null,"topics":["dotnet","inheritdoc","msbuild-task"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/saucecontrol.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"license","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-09-27T22:38:09.000Z","updated_at":"2024-05-31T02:11:36.074Z","dependencies_parsed_at":"2022-07-26T05:16:34.998Z","dependency_job_id":"791e2549-4c5c-4f14-bdb6-29105399ec88","html_url":"https://github.com/saucecontrol/InheritDoc","commit_stats":{"total_commits":31,"total_committers":2,"mean_commits":15.5,"dds":"0.032258064516129004","last_synced_commit":"b6933b0cad00a1aee62106ecec39ff74e36c7f12"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/saucecontrol/InheritDoc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saucecontrol%2FInheritDoc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saucecontrol%2FInheritDoc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saucecontrol%2FInheritDoc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saucecontrol%2FInheritDoc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/saucecontrol","download_url":"https://codeload.github.com/saucecontrol/InheritDoc/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saucecontrol%2FInheritDoc/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279017238,"owners_count":26086015,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-13T02:00:06.723Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dotnet","inheritdoc","msbuild-task"],"created_at":"2025-10-13T23:28:36.453Z","updated_at":"2025-10-13T23:28:41.573Z","avatar_url":"https://github.com/saucecontrol.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![NuGet](https://buildstats.info/nuget/SauceControl.InheritDoc)](https://www.nuget.org/packages/SauceControl.InheritDoc/) [![Build Status](https://dev.azure.com/saucecontrol/InheritDoc/_apis/build/status/saucecontrol.InheritDoc?branchName=master)](https://dev.azure.com/saucecontrol/InheritDoc/_build/latest?definitionId=2\u0026branchName=master) [![Test Results](https://img.shields.io/azure-devops/tests/saucecontrol/InheritDoc/2?logo=azure-devops)](https://dev.azure.com/saucecontrol/InheritDoc/_build/latest?definitionId=2\u0026branchName=master)\n\nInheritDoc\n==========\n\nThis [MSBuild Task]( https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-tasks) automatically replaces `\u003cinheritdoc /\u003e` tags in your .NET XML documentation with the actual inherited docs.\n\nHow to Use It\n-------------\n\n1) Add some `\u003cinheritdoc /\u003e` tags to your XML documentation comments.\n\n    This tool’s handling of `\u003cinheritdoc /\u003e` tags is based on the [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 `\u003cinheritdoc /\u003e` support in [Sandcastle Help File Builder]( https://ewsoftware.github.io/XMLCommentsGuide/html/86453FFB-B978-4A2A-9EB5-70E118CA8073.htm#TopLevelRules) (SHFB).\n\n2) Add the [SauceControl.InheritDoc](https://www.nuget.org/packages/SauceControl.InheritDoc) NuGet package reference to your project.\n\n    This is a development-only dependency; it will not be deployed with or referenced by your compiled app/library.\n\n3) Build your project as you normally would.\n\n    The XML docs will be post-processed automatically with each non-debug build, whether you use Visual Studio, dotnet CLI, or anything else that hosts the MSBuild engine.\n\nAdditional Features\n-------------------\n\n* Updates the contents of inherited docs to replace `param` and `typeparam` names that changed in the inheriting type or member.\n\n* Supports trimming your published XML doc files of any types or members not publicly visible in your API.\n\n* Validates your usage of `\u003cinheritdoc /\u003e` and warns you if no documentation exists or if your `cref`s or `path`s are incorrect.\n\nHow it Works\n------------\n\nThe InheritDoc task inserts itself between the `Compile` and `CopyFilesToOutputDirectory` steps in the MSBuild process.  It uses the arguments passed to the compiler to find your assembly, the XML doc file, and all referenced assemblies, and processes it to replace `\u003cinheritdoc /\u003e` tags.  The output of InheritDoc is then written to your output (bin) directory and is used for the remainder of your build process.  If you have further steps, such as building a NuGet package, the updated XML file will used in place of the original, meaning `\u003cinheritdoc /\u003e` Just Works™.\n\nThis enhances the new support for `\u003cinheritdoc /\u003e` in Roslyn (available starting in [VS 16.4](https://docs.microsoft.com/en-us/visualstudio/releases/2019/release-notes-preview#net-productivity-164P1)), making it available to all downstream consumers of your documentation.  When using tools such as [DocFX](https://dotnet.github.io/docfx/spec/triple_slash_comments_spec.html#inheritdoc), you will no longer be [subject](https://github.com/dotnet/docfx/issues/3699) to [limitations](https://github.com/dotnet/docfx/issues/1306) around `\u003cinheritdoc /\u003e` tag usage because the documentation will already have those tags replaced with the upstream docs.\n\nRequirements\n------------\n\nInheritDoc requires MSBuild 16.0 or greater, which is included with the .NET SDK or with Visual Studio 2019 or later.\n\nSome Examples\n-------------\n\nClick to expand samples:\n\n\u003cdetails\u003e\n\u003csummary\u003eBasic \u003ccode\u003e\u0026lt;inheritdoc /\u0026gt;\u003c/code\u003e Usage (Automatic Inheritance)\u003c/summary\u003e\n\nConsider the following C#\n\n```C#\n/// \u003csummary\u003eInterface IX\u003c/summary\u003e\npublic interface IX\n{\n    /// \u003csummary\u003eMethod X\u003c/summary\u003e\n    void X();\n}\n\n/// \u003cinheritdoc /\u003e\npublic interface IY : IX\n{\n    /// \u003csummary\u003eMethod Y\u003c/summary\u003e\n    void Y();\n}\n\n/// \u003csummary\u003eClass A\u003c/summary\u003e\npublic class A : IY\n{\n    void IX.X() { }\n\n    /// \u003cinheritdoc /\u003e\n    public virtual void Y() { }\n\n    /// \u003csummary\u003eMethod M\u003c/summary\u003e\n    /// \u003ctypeparam name=\"T\"\u003eTypeParam T\u003c/typeparam\u003e\n    /// \u003cparam name=\"t\"\u003eParam t\u003c/param\u003e\n    /// \u003creturns\u003e\n    /// Returns value \u003cparamref name=\"t\" /\u003e\n    /// of type \u003ctypeparamref name=\"T\" /\u003e\n    /// \u003c/returns\u003e\n    public virtual T M\u003cT\u003e(T t) =\u003e t;\n\n    /// \u003csummary\u003eMethod P\u003c/summary\u003e\n    private void P() { }\n\n    /// \u003csummary\u003eOverloaded Method O\u003c/summary\u003e\n    /// \u003cparam name=\"s\"\u003eParam s\u003c/param\u003e\n    /// \u003cparam name=\"t\"\u003eParam t\u003c/param\u003e\n    /// \u003cparam name=\"u\"\u003eParam u\u003c/param\u003e\n    public static void O(string[] s, string t, string u) { }\n\n    /// \u003cinheritdoc cref=\"O(string[], string, string)\" /\u003e\n    public static void O(string[] s) { }\n}\n\n/// \u003cinheritdoc /\u003e\npublic class B : A\n{\n    /// \u003cinheritdoc /\u003e\n    public override void Y() { }\n\n    /// \u003cinheritdoc /\u003e\n    public override TValue M\u003cTValue\u003e(TValue value) =\u003e value;\n}\n```\n\nOnce processed, the output XML documentation will look like this (results abbreviated and comments added manually to highlight features)\n\n```XML\n\u003cmember name=\"T:IX\"\u003e\n    \u003csummary\u003eInterface IX\u003c/summary\u003e\n\u003c/member\u003e\n\u003cmember name=\"M:IX.X\"\u003e\n    \u003csummary\u003eMethod X\u003c/summary\u003e\n\u003c/member\u003e\n\u003cmember name=\"T:IY\"\u003e\n    \u003csummary\u003eInterface IX\u003c/summary\u003e \u003c!-- inherited from IX --\u003e\n\u003c/member\u003e\n\u003cmember name=\"M:IY.Y\"\u003e\n    \u003csummary\u003eMethod Y\u003c/summary\u003e\n\u003c/member\u003e\n\u003cmember name=\"T:A\"\u003e\n    \u003csummary\u003eClass A\u003c/summary\u003e\n\u003c/member\u003e\n\u003cmember name=\"M:A.Y\"\u003e\n    \u003csummary\u003eMethod Y\u003c/summary\u003e \u003c!-- inherited from IY --\u003e\n\u003c/member\u003e\n\u003cmember name=\"M:A.M``1(``0)\"\u003e\n    \u003csummary\u003eMethod M\u003c/summary\u003e\n    \u003ctypeparam name=\"T\"\u003eTypeParam T\u003c/typeparam\u003e\n    \u003cparam name=\"t\"\u003eParam t\u003c/param\u003e\n    \u003creturns\u003e\n    Return value \u003cparamref name=\"t\" /\u003e\n    of type \u003ctypeparamref name=\"T\" /\u003e\n    \u003c/returns\u003e\n\u003c/member\u003e\n\u003c!-- private method A.P doc removed --\u003e\n\u003cmember name=\"M:A.O(System.String[],System.String,System.String)\"\u003e\n    \u003csummary\u003eOverloaded Method O\u003c/summary\u003e\n    \u003cparam name=\"s\"\u003eParam s\u003c/param\u003e\n    \u003cparam name=\"t\"\u003eParam t\u003c/param\u003e\n    \u003cparam name=\"u\"\u003eParam u\u003c/param\u003e\n\u003c/member\u003e\n\u003cmember name=\"M:A.O(System.String[])\"\u003e \n    \u003csummary\u003eOverloaded Method O\u003c/summary\u003e \u003c!-- inherited (by cref) from overload --\u003e\n    \u003cparam name=\"s\"\u003eParam s\u003c/param\u003e\n    \u003c!-- unused parameters automatically removed --\u003e\n\u003c/member\u003e\n\u003cmember name=\"T:B\"\u003e\n    \u003csummary\u003eClass A\u003c/summary\u003e \u003c!-- inherited from A --\u003e\n\u003c/member\u003e\n\u003cmember name=\"M:B.Y\"\u003e\n    \u003csummary\u003eMethod Y\u003c/summary\u003e \u003c!-- inherited from IY (recursively through A) --\u003e\n\u003c/member\u003e\n\u003cmember name=\"M:B.M``1(``0)\"\u003e\n    \u003csummary\u003eMethod M\u003c/summary\u003e \u003c!-- inherited from A --\u003e\n    \u003ctypeparam name=\"TValue\"\u003eTypeParam T\u003c/typeparam\u003e \u003c!-- typeparam updated to match override's name --\u003e\n    \u003cparam name=\"value\"\u003eParam t\u003c/param\u003e \u003c!-- param updated to match override's name --\u003e\n    \u003creturns\u003e\n    Returns value \u003cparamref name=\"value\" /\u003e \u003c!-- paramref and typeparamref updated as well --\u003e\n    of type \u003ctypeparamref name=\"TValue\" /\u003e\n    \u003c/returns\u003e\n\u003c/member\u003e\n\u003cmember name=\"M:A.IX#X\"\u003e \u003c!-- explicit interface implementation doc added automatically --\u003e\n    \u003csummary\u003eMethod X\u003c/summary\u003e\n\u003c/member\u003e\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eExplicit Inheritance\u003c/summary\u003e\n\nInheritDoc also supports the `path` attribute defined in the Roslyn draft design doc, which is analogous to the `select` attribute in SHFB.\n\nIn this example, we define a custom Exception class that for some reason doesn't inherit from `System.Exception` and yet we want to use its documentation anyway.\n\n```C#\npublic class ExceptionForSomeReasonNotInheritedFromSystemException\n{\n    /// \u003cinheritdoc cref=\"Exception(string)\" /\u003e\n    /// \u003cparam name=\"theErrorMessage\"\u003e\u003cinheritdoc cref=\"Exception(string)\" path=\"/param[@name='message']/node()\" /\u003e\u003c/param\u003e\n    ExceptionForSomeReasonNotInheritedFromSystemException(string theErrorMessage) { }\n}\n```\n\nOutputs:\n\n```XML\n\u003cmember name=\"M:ExceptionForSomeReasonNotInheritedFromSystemException.#ctor(System.String)\"\u003e\n    \u003csummary\u003eInitializes a new instance of the \u003csee cref=\"T:System.Exception\"\u003e\u003c/see\u003e class with a specified error message.\u003c/summary\u003e\n    \u003cparam name=\"theErrorMessage\"\u003eThe message that describes the error.\u003c/param\u003e\n\u003c/member\u003e\n```\n\nNotice the `param` element for `message` was excluded automatically because there was no matching parameter on the target constructor, however with a nested `\u003cinheritdoc /\u003e` and a custom selector, we were able to extract the contents from that `param` element into a new one with the correct name.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eNamespace Documentation\u003c/summary\u003e\n\nAlthough 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.\n\nNote that both the `[CompilerGenerated]` attribute and the class name `NamespaceDoc` are required by InheritDoc.\n\n```C#\nnamespace InheritDocTest\n{\n    /// \u003csummary\u003eNamespace InheritDocTest\u003c/summary\u003e\n    [CompilerGenerated] internal class NamespaceDoc { }\n}\n```\n\nWill output:\n\n```XML\n\u003cmember name=\"N:InheritDocTest\"\u003e\n    \u003csummary\u003eNamespace InheritDocTest\u003c/summary\u003e\n\u003c/member\u003e\n```\n\n\u003c/details\u003e\n\nConfiguration\n-------------\n\n#### Enabling/Disabling InheritDoc\n\nInheritDoc 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.\n\n```XML\n\u003cPropertyGroup\u003e\n    \u003cInheritDocEnabled\u003efalse\u003c/InheritDocEnabled\u003e\n\u003c/PropertyGroup\u003e\n```\n\nAlternatively, you can conditionally include the NuGet package only for specific configurations.\n\n```XML\n\u003cItemGroup Condition=\"'$(Configuration)'=='Dist'\"\u003e\n    \u003cPackageReference Include=\"SauceControl.InheritDoc\" Version=\"2.*\" PrivateAssets=\"all\" /\u003e\n\u003c/ItemGroup\u003e\n```\n\n*NuGet tools may include a more verbose version of the `PackageReference` tag when you add the package to your project.  The above example is all that's actually necessary.\n\n#### Configuring Doc Trimming\n\nBy default, InheritDoc will remove documentation for any types/members that are not part of the assembly's public API from the output XML.  This behavior can be configured by setting the `InheritDocTrimLevel` property to one of: `none`, `private`, or `internal`.  Docs belonging to types/members with API visibility at or below the `InheritDocTrimLevel` will be removed.  The default setting is `internal`.\n\nIf your internal types/members are available to other assemblies (by means of `InternalsVisibleToAttribute`) and those projects are not part of the same Visual Studio solution, you may wish to preserve the internal member docs by setting `InheritDocTrimLevel` to `private`.\n\n```XML\n\u003cPropertyGroup\u003e\n    \u003cInheritDocTrimLevel\u003eprivate\u003c/InheritDocTrimLevel\u003e\n\u003c/PropertyGroup\u003e\n```\n\n#### Adding Candidate Docs for Inheritance\n\nInheritDoc will automatically discover XML documentation files alongside assemblies referenced by your project.  If necessary, additional XML documentation files can be manually included with `InheritDocReference`.  For example:\n\n```XML\n\u003cItemGroup\u003e\n    \u003cInheritDocReference Include=\"\\path\\to\\somedocs.xml\" /\u003e\n    \u003cInheritDocReference Include=\"\\path\\to\\moredocs.xml\" /\u003e\n\u003c/ItemGroup\u003e\n```\n\n#### Using InheritDoc With Multi-Targeted Projects\n\nIf 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.\n\nIf you configure the XML documentation output from the project property page in Visual Studio, you may end up with something like:\n\n```XML\n\u003cPropertyGroup\u003e\n    \u003cDocumentationFile\u003eMyProject.xml\u003c/DocumentationFile\u003e \u003c!-- NOOOOOOO! --\u003e\n\u003c/PropertyGroup\u003e\n```\n\nThe above configuration will create a single `MyProject.xml` file in your project root for all target frameworks and all build configurations.  Since the dotnet build server builds multiple target framework outputs in parallel there will be a race condition for access to that file.\n\nThe simpler configuration, supported in all multi-targeting capable SDK versions, is:\n\n```XML\n\u003cPropertyGroup\u003e\n    \u003cGenerateDocumentationFile\u003etrue\u003c/GenerateDocumentationFile\u003e\n\u003c/PropertyGroup\u003e\n```\n\nThis will automatically name your XML file with the same base as the assembly name and will create it in the correct `obj` folder alongside the assembly.\n\n#### Using InheritDoc in Docker\n\nThe .NET SDK Docker images set an environment variable that instructs the NuGet client not to extract XML documentation files during package restore.  This is done in the interest of time and space savings, as explained in https://github.com/dotnet/dotnet-docker/issues/2790, however this may prevent InheritDoc from resolving documentation from NuGet package references.\n\nThe default behavior can be restored by clearing the environment variable in your own `Dockerfile`.\n\n```ini\nENV NUGET_XMLDOC_MODE=\n```\n\nMore documentation on the environment variables used by NuGet client can be found [here](https://learn.microsoft.com/en-us/nuget/reference/cli-reference/cli-ref-environment-variables).\n\n#### Disabling InheritDoc Build Warnings\n\nWarnings can be selectively disabled with the MSBuild standard `NoWarn` property.  For example:\n\n```XML\n\u003cPropertyGroup\u003e\n    \u003cNoWarn\u003e$(NoWarn);IDT002\u003c/NoWarn\u003e\n\u003c/PropertyGroup\u003e\n```\n\n#### Possible Warnings\n\n| Code | Description |\n|------|-------------|\n|IDT001| Indicates a referenced XML documentation file could not be loaded or parsed or that the file did not contain documentation in the standard schema. |\n|IDT002| Indicates incomplete XML docs for the target assembly or one of its external references. i.e. an inheritance candidate was identified but had no documentaion to inherit. |\n|IDT003| May indicate you used `\u003cinheritdoc /\u003e` on a type/member with no identifiable base. You may correct this warning by using the `cref` attribute to identify the base explicitly. |\n|IDT004| May indicate an incorrect XPath value in a `path` attribute or a duplicate/superfluous or self-referencing `\u003cinheritdoc /\u003e` tag. |\n\nTroubleshooting\n---------------\n\nWhen it runs, `InheritDocTask` will log a success message to the build output for each processed file, telling you what it did.  For example:\n\n```\nInheritDocTask replaced 55 of 55 inheritdoc tags and removed 60 non-public member docs in /path/to/MyProject.xml\n```\n\nIf 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`) or use the [MSBuild Log Viewer](https://msbuildlog.com/) and look for `InheritDoc` in the logs for clues.  Issue reports are, of course, welcome with good repro steps.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaucecontrol%2Finheritdoc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsaucecontrol%2Finheritdoc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaucecontrol%2Finheritdoc/lists"}