saucecontrol / inheritdoc Goto Github PK
View Code? Open in Web Editor NEWAutomatically replace <inheritdoc /> tags in .NET XML comments with inherited documentation at build time
License: MIT License
Automatically replace <inheritdoc /> tags in .NET XML comments with inherited documentation at build time
License: MIT License
Hello,
I've encountered an issue with the version 1.3.0 of this package. When I create a new project and reference the package before the first build, the build fails when copying the (not yet existing) XML file.
MSB3030: Could not copy the file 'obj\Debug\net6.0\ClassLibrary1.xml' because it was not found.
Here the code of the ClassLibrary1.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<InheritDocEnabled>True</InheritDocEnabled>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SauceControl.InheritDoc" Version="1.3.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
Of course I can workaround this by checking the existence of this file before setting InheritDocEnabled
. But ideally the package can handle this by itself. Or did I miss any option?
Thanks
Context: NodaTime and NodaTime.Serialization libraries
.NET SDK: 8.0
I've managed to get the main NodaTime build to work with InheritDoc across all its target frameworks (netstandard2.0, net6.0, net8.0) but I'm struggling with NodaTime.Serialization libraries, e.g. NodaTime.Serialization.SystemTextJson which targets netstandard2.0 and netcoreapp3.1.
(I'm aware that netcoreapp3.1 is out of support; updating that target is a separate matter...)
I've reproduced the issue with the code below (inline and in a zip file). It's just a class library targeting netstandard2.0, with an abstract class derived from System.Text.Json.Serialization.JsonConverter<>
. On my Windows box this builds correctly:
InheritDocTask replaced 1 of 1 inheritdoc tags and removed 0 non-public member docs in [...]\InheritDocDemo\bin\Release\netstandard2.0\InheritDocDemo.xml
When I run docker build --progress=plain .
I get the following warning (I've wrapped it for readability):
obj/Release/netstandard2.0/InheritDocDemo.xml(10,10): InheritDocTask
warning IDT002: No matching documentation could be found for: M:InheritDocDemo.LibraryClass.CanConvert(System.Type),
which attempts to inherit from: M:System.Text.Json.Serialization.JsonConverter`1.CanConvert(System.Type) [/source/InheritDocDemo.csproj]
Using a target framework of net8.0, the build works fine in both Windows and Docker. For the main NodaTime build, it made sense that I needed to add extra SDKs in order to get appropriate XML files for the BCL - but in this case the only thing we're inheriting is the System.Text.Json library, which presumably comes with its own XML files. (If it would help to rewrite this example using Newtonsoft.Json, I could do that - it has the same issue.)
Files:
LibraryClass.cs:
using System;
using System.Text.Json.Serialization;
namespace InheritDocDemo;
/// <summary>Summary text</summary>
public abstract class LibraryClass : JsonConverter<string>
{
/// <inheritdoc />
public override bool CanConvert(Type type) => false;
}
InheritDocDemo.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<InheritDocEnabled>true</InheritDocEnabled>
<LangVersion>latest</LangVersion>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<InheritDocEnabled>true</InheritDocEnabled>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Text.Json" Version="6.0.9" />
<PackageReference Include="SauceControl.InheritDoc" Version="2.0.1" PrivateAssets="all" />
</ItemGroup>
</Project>
Dockerfile:
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /source
COPY Dockerfile *.csproj *.cs .
RUN dotnet build -c Release
Consider the following case, using the new record-syntax from C# 9.0:
public class A
{
/// <summary>
/// Int comment
/// </summary>
public int Int { get; set; }
}
/// <summary>
/// B documentation
/// </summary>
/// <param name="Int"><inheritdoc cref="A.Int"/></param>
public record B(int Int);
With the current version (1.3.0) of InheritDoc
, this results in the following warnings and does not follow the inheritdoc
:
Ideally, InheritDoc
should be able to resolve the inheritdoc to 'Int comment
' without any warnings.
Greetings.
First of all thanks for your work in this library. It's exactly what I've been looking for, and it's working very well in my preliminary tests.
I've only noticed an issue with enum
values. They don't seem to work, unless their cref
target is referenced by chance by another property, in which case it works well.
I've dived into your code to understand what I was doing wrong.
It seems to me that because you rely on Methods
to collect cref
references, you miss enum
values which are Fields
, and they don't end up in refDocs
when you check if refCref.Contains(name)
.
Is my understanding correct? If so, is there a plan to add support for enum
values?
Or maybe a workaround you can suggest? I can only think of a dummy class with dummy properties, but it's a bit messy and manual.
Could you avoid the above issue by simply removing the check for refCref.Contains(name)
? Will this break something? It would let all members
be available for replacement, in the next steps.
Hi,
I'm looking to use your project to trim down the generated XML docs to just public symbols but I'm having trouble fitting it into my existing build.
My build sets CopyDocumentationFileToOutputDirectory to false and adds a new target that embeds the XML file (@(DocFileItem)) as an assembly resource, but I can't figure out how to do this with InheritDoc in the mix.
InheritDoc requires CopyDocumentationFileToOutputDirectory to be true (which it then sets to false and does the copy itself I believe) so I've been trying to modify DocFileItem/FinalDocFile to point to the intermediate build directory without much luck.
Obviously my embed target needs to run before the assembly is generated but I think InheritDoc is maybe set to run after that? Which would explain why changing the paths doesn't work.
Any thoughts on how I can achieve this? Many thanks
Hello, first off thank you for this project. I really dont understand why there is no Tool that uses the same base as Intellisense currently does to provide the functionality of inheritdoc so I'm really glad for this project.
I think I found a bug in generating the Id for Explicit Property Implementation. I implemented a class that implements IDictionary
and explicitly implementent some stuff like the Property Keys
. When using this class your Tool reports the error: IDT003: Cref not present and no base could be found for: P:ImplementsIDicitionary.System#Collections#Generic#IDictionary{System#String,System#String}#Keys.
I debugged this in your Testproject and found out that in docMap
(InheritDocProcessor
in Line 253) there is a existing Key with a minor Difference: P:ImplementsIDicitionary.System#Collections#Generic#IDictionary{System#String@System#String}#Keys
As you can see the ,
between TypeArguments is replaced with @
wich I guess comes from encodeMemberName
(CecilExtensions
in line 196) thats used in some GetDocId
-Methods. Since I don't want to break anything (and you probably know the usecases of the replacement better than I do) I would prefer your help on this.
For Testing you can insert the following code in DocTests.cs
:
/// <inheritdoc cref="IDictionary{TKey, TValue}"/>
public class ImplementsIDicitionary : IDictionary<string, string>
{
/// <inheritdoc />
ICollection<string> IDictionary<string, string>.Keys => Keys;
/// <inheritdoc />
void IDictionary<string, string>.Add(string key, string value)
{
throw new NotImplementedException();
}
#region UntestedMembers
/// <inheritdoc />
ICollection<string> IDictionary<string, string>.Values => Values;
/// <inheritdoc />
public int Count => throw new NotImplementedException();
/// <inheritdoc />
public string this[string key] { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
/// <inheritdoc />
public bool IsReadOnly => throw new NotImplementedException();
/// <inheritdoc />
public void Add(KeyValuePair<string, string> item)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public void Clear()
{
throw new NotImplementedException();
}
/// <inheritdoc />
public bool Contains(KeyValuePair<string, string> item)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public bool ContainsKey(string key)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
throw new NotImplementedException();
}
/// <inheritdoc />
public bool Remove(string key)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public bool Remove(KeyValuePair<string, string> item)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public bool TryGetValue(string key, out string value)
{
throw new NotImplementedException();
}
/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
#endregion
}
and the following TestMethods to InheritDocTests.cs
:
[TestMethod]
public void ExlicitProperty()
{
var ele = getDocElement("P:ImplementsIDicitionary.System#Collections#Generic#IDictionary{System#String,System#String}#Keys", "summary");
Assert.IsNotNull(ele);
}
[TestMethod]
public void ExlicitMethod()
{
var ele = getDocElement("M:ImplementsIDicitionary.System#Collections#Generic#IDictionary{System#String,System#String}#Add(System.String,System.String)", "summary");
Assert.IsNotNull(ele);
}
Hello,
I have the following class structure (which I simplified):
/// <summary>
/// Specific type description
/// </summary>
public sealed class EntityAddBroadcast : pb::IMessage<EntityAddBroadcast> {
private static readonly pb::MessageParser<EntityAddBroadcast> _parser = new pb::MessageParser<EntityAddBroadcast>(() => new EntityAddBroadcast());
private pb::UnknownFieldSet _unknownFields;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public static pb::MessageParser<EntityAddBroadcast> Parser { get { return _parser; } }
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public static pbr::MessageDescriptor Descriptor {
get { return global::Protobuf.Gen.Descriptor.MessageTypes[16]; }
}
}
/// <summary>
/// Interface for a Protocol Buffers message, supporting
/// basic operations required for serialization.
/// </summary>
public interface IMessage
{
/// <summary>
/// Merges the data from the specified coded input stream with the current message.
/// </summary>
/// <remarks>See the user guide for precise merge semantics.</remarks>
/// <param name="input"></param>
void MergeFrom(CodedInputStream input);
/// <summary>
/// Descriptor for this message. All instances are expected to return the same descriptor,
/// and for generated types this will be an explicitly-implemented member, returning the
/// same value as the static property declared on the type.
/// </summary>
MessageDescriptor Descriptor { get; }
}
/// <summary>
/// Generic interface for a Protocol Buffers message,
/// where the type parameter is expected to be the same type as
/// the implementation class.
/// </summary>
/// <typeparam name="T">The message type.</typeparam>
public interface IMessage<T> : IMessage, IEquatable<T>, IDeepCloneable<T> where T : IMessage<T>
{
/// <summary>
/// Merges the given message into this one.
/// </summary>
/// <remarks>See the user guide for precise merge semantics.</remarks>
/// <param name="message">The message to merge with this one. Must not be null.</param>
void MergeFrom(T message);
}
I get on the Description
property of EntityAddBroadcast
a:
[IDT002] No matching documentation could be found for: P:Protobuf.Gen.EntityAddBroadcast.pb::IMessage#Descriptor, which attempts to inherit from: P:IMessage.Descriptor
(I have removed the namespaces from the error because I think they are not relevant).
What is causing this "not found" problem? I did not put an explicit there, but it still tries to inherit. Maybe it is the layer of IMessage<T> : IMessage
that is making it fail?
Any solutions that do not involve changing the protobuf generated files?
Hello it's me again ๐
sorry to bother you once again but I have a small enhancement regarding localization. My comments are German, so when I use inheritdoc, it is mixed between German and Englisch. Microsoft does provide some localized Commentfiles for Intellisense.
As you can see in the Link, the files are placed in a Folder with the specific languagetag, so for me it's de
. My Idea would be to look for a folder matching some version of the languageTag of the currently used Culture and check there for the xml-File. If there is no such File you can just do it like before.
For this I would replace the lambda in this line with a Method like this:
static string TryGetXmlFileForAssembly(string path)
{
string directoryPath = Path.GetDirectoryName(path)!;
var currentCulture = Thread.CurrentThread.CurrentUICulture; // or Thread.CurrentThread.CurrentCulture?
string culturePath = Path.Combine(directoryPath, currentCulture.Name, Path.GetFileNameWithoutExtension(path) + ".xml");
if(File.Exists(culturePath))
{
return culturePath;
}
culturePath = Path.Combine(directoryPath, currentCulture.TwoLetterISOLanguageName, Path.GetFileNameWithoutExtension(path) + ".xml");
if (File.Exists(culturePath)){
return culturePath;
}
return Path.ChangeExtension(path, ".xml");
}
I actually have no idea what the "namespaces.xml"
-files from the next line are for so you may have to modify my Suggestion^^
I also wonder if Intellisense does work this way for non CLR-Assemblies so localization would work with Nuget-References as well. I tested it with the Nuget of the CLR-Refs but Intellisense did ignore the localized files I manually added in the nugetcache.
The current build integration lets the _InheritDocPostProcess
task run regardless of whether the C# compiler ran.
In case of an incremental build where the task's output is up to date with regard to its inputs, post processing should be omitted in order to minimize build time. This can save quite a lot of time for large projects.
I've got a modified .targets
file working which sets the CopyDocumentationFileToOutputDirectory
property to false
and uses both the DocFileItem
and FinalDocFile
items to get this right.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.