diff --git a/documentation/NEXT_RELEASENOTES.txt b/documentation/NEXT_RELEASENOTES.txt index 00c828d4..63f282c9 100644 --- a/documentation/NEXT_RELEASENOTES.txt +++ b/documentation/NEXT_RELEASENOTES.txt @@ -7,4 +7,5 @@ - fixed string and char const field definition - fixed ExplicitInterfaceImplementationsSection config name (fixes #141) -- fixed StackOverflowException when using cyclic inheritdoc (fixes #142) \ No newline at end of file +- fixed StackOverflowException when using cyclic inheritdoc (fixes #142) +- fixed property getter/setter access modifier not taken into account (fixes #151) \ No newline at end of file diff --git a/source/DefaultDocumentation.Api/Extensions/IEntityExtensions.cs b/source/DefaultDocumentation.Api/Extensions/IEntityExtensions.cs new file mode 100644 index 00000000..5f46c5c5 --- /dev/null +++ b/source/DefaultDocumentation.Api/Extensions/IEntityExtensions.cs @@ -0,0 +1,38 @@ +using System.Linq; +using DefaultDocumentation; + +namespace ICSharpCode.Decompiler.TypeSystem +{ + /// + /// Provides extension methods on the type. + /// + public static class IEntityExtensions + { + /// + /// Returns wether an should be part of the documentation or not based on its accessibility. + /// + /// The to check. + /// The used to generate the documentation. + /// if the entity should be part of the documentation; otherwise . + public static bool IsVisibleInDocumentation(this IEntity? entity, ISettings settings) + { + ArgumentNullException.ThrowIfNull(settings); + + return entity?.EffectiveAccessibility() switch + { + Accessibility.Public => (settings.GeneratedAccessModifiers & GeneratedAccessModifiers.Public) != 0, + Accessibility.Private => entity switch + { + IProperty property when property.IsExplicitInterfaceImplementation => property.ExplicitlyImplementedInterfaceMembers.First().DeclaringTypeDefinition.IsVisibleInDocumentation(settings), + IMethod method when method.IsExplicitInterfaceImplementation => method.ExplicitlyImplementedInterfaceMembers.First().DeclaringTypeDefinition.IsVisibleInDocumentation(settings), + _ => (settings.GeneratedAccessModifiers & GeneratedAccessModifiers.Private) != 0 + }, + Accessibility.Protected => (settings.GeneratedAccessModifiers & GeneratedAccessModifiers.Protected) != 0, + Accessibility.Internal => (settings.GeneratedAccessModifiers & GeneratedAccessModifiers.Internal) != 0, + Accessibility.ProtectedOrInternal => (settings.GeneratedAccessModifiers & GeneratedAccessModifiers.ProtectedInternal) != 0, + Accessibility.ProtectedAndInternal => (settings.GeneratedAccessModifiers & GeneratedAccessModifiers.PrivateProtected) != 0, + _ => false + }; + } + } +} diff --git a/source/DefaultDocumentation.Common/Internal/DocItemReader.cs b/source/DefaultDocumentation.Common/Internal/DocItemReader.cs index e1773cc7..bd1972f4 100644 --- a/source/DefaultDocumentation.Common/Internal/DocItemReader.cs +++ b/source/DefaultDocumentation.Common/Internal/DocItemReader.cs @@ -27,22 +27,6 @@ internal sealed class DocItemReader private DocItemReader(Settings settings) { - bool IsGenerated(IEntity? entity) => entity.EffectiveAccessibility() switch - { - Accessibility.Public => (settings.GeneratedAccessModifiers & GeneratedAccessModifiers.Public) != 0, - Accessibility.Private => entity switch - { - IProperty property when property.IsExplicitInterfaceImplementation => IsGenerated(property.ExplicitlyImplementedInterfaceMembers.First().DeclaringTypeDefinition), - IMethod method when method.IsExplicitInterfaceImplementation => IsGenerated(method.ExplicitlyImplementedInterfaceMembers.First().DeclaringTypeDefinition), - _ => (settings.GeneratedAccessModifiers & GeneratedAccessModifiers.Private) != 0 - }, - Accessibility.Protected => (settings.GeneratedAccessModifiers & GeneratedAccessModifiers.Protected) != 0, - Accessibility.Internal => (settings.GeneratedAccessModifiers & GeneratedAccessModifiers.Internal) != 0, - Accessibility.ProtectedOrInternal => (settings.GeneratedAccessModifiers & GeneratedAccessModifiers.ProtectedInternal) != 0, - Accessibility.ProtectedAndInternal => (settings.GeneratedAccessModifiers & GeneratedAccessModifiers.PrivateProtected) != 0, - _ => false - }; - _logger = settings.Logger; _decompiler = new CSharpDecompiler(settings.AssemblyFile.FullName, new DecompilerSettings { ThrowOnAssemblyResolveErrors = false }); _resolver = new CSharpResolver(_decompiler.TypeSystem); @@ -75,7 +59,7 @@ private DocItemReader(Settings settings) continue; } - if (!IsGenerated(type)) + if (!type.IsVisibleInDocumentation(settings)) { _logger.Debug($"Skipping documentation for type \"{type.FullName}\": accessibility \"{type.EffectiveAccessibility()}\" not generated"); continue; @@ -155,7 +139,7 @@ private DocItemReader(Settings settings) continue; } - if (!IsGenerated(entity)) + if (!entity.IsVisibleInDocumentation(settings)) { _logger.Debug($"Skipping documentation for member \"{entity.FullName}\": accessibility \"{entity.EffectiveAccessibility()}\" not generated"); continue; diff --git a/source/DefaultDocumentation.Markdown/Sections/DefinitionSection.cs b/source/DefaultDocumentation.Markdown/Sections/DefinitionSection.cs index 25e6979a..c65f13b4 100644 --- a/source/DefaultDocumentation.Markdown/Sections/DefinitionSection.cs +++ b/source/DefaultDocumentation.Markdown/Sections/DefinitionSection.cs @@ -89,7 +89,6 @@ public sealed class DefinitionSection : ISection { ConversionFlags = ConversionFlags.ShowAccessibility - | ConversionFlags.ShowBody | ConversionFlags.ShowModifiers | ConversionFlags.ShowParameterDefaultValues | ConversionFlags.ShowParameterList @@ -186,6 +185,40 @@ static IWriter Write(IWriter writer, Action writeAction) .Append("```"); } + + static void WritePropertyMethod(IWriter writer, IMethod? method, string name) + { + if (!method.IsVisibleInDocumentation(writer.Context.Settings)) + { + return; + } + + writer + .Append((method!.IsExplicitInterfaceImplementation ? method.ExplicitlyImplementedInterfaceMembers.FirstOrDefault() : method).Accessibility switch + { + Accessibility.Private => " private ", + Accessibility.Internal => " internal ", + Accessibility.Protected => " protected ", + Accessibility.ProtectedAndInternal => " private protected ", + Accessibility.ProtectedOrInternal => " protected internal ", + _ => " " + }) + .Append(name) + .Append(";"); + } + + static void WriteProperty(IWriter writer, IProperty property) + { + writer + .Append(property.ToString(_propertyAmbience)) + .Append(" {"); + + WritePropertyMethod(writer, property.Getter, "get"); + WritePropertyMethod(writer, property.Setter, property.Setter?.IsInitOnly ?? false ? "init" : "set"); + + writer.Append(" }"); + } + _ = writer.GetCurrentItem() switch { FieldDocItem item => Write(writer, w => @@ -210,12 +243,12 @@ static IWriter Write(IWriter writer, Action writeAction) w.Append(";"); }), - PropertyDocItem item => Write(writer, w => w.Append(item.Property.ToString(_propertyAmbience))), + PropertyDocItem item => Write(writer, w => WriteProperty(w, item.Property)), EventDocItem item => Write(writer, w => w.Append(item.Event.ToString(_eventAmbience))), ConstructorDocItem item => Write(writer, w => w.Append(item.Method.ToString(_methodAmbience)).Append(";")), OperatorDocItem item => Write(writer, w => w.Append(item.Method.ToString(_methodAmbience)).Append(";")), ExplicitInterfaceImplementationDocItem item when item.Member is IEvent => Write(writer, w => w.Append(item.Member.ToString(_eventAmbience))), - ExplicitInterfaceImplementationDocItem item when item.Member is IProperty => Write(writer, w => w.Append(item.Member.ToString(_propertyAmbience))), + ExplicitInterfaceImplementationDocItem item when item.Member is IProperty property => Write(writer, w => WriteProperty(w, property)), ExplicitInterfaceImplementationDocItem item when item.Member is IMethod => Write(writer, w => { w.Append(item.Member.ToString(_methodAmbience)); diff --git a/source/DefaultDocumentation.Test/AssemblyInfo.cs b/source/DefaultDocumentation.Test/AssemblyInfo.cs index e44283ed..9eb1e833 100644 --- a/source/DefaultDocumentation.Test/AssemblyInfo.cs +++ b/source/DefaultDocumentation.Test/AssemblyInfo.cs @@ -63,7 +63,11 @@ public sealed record ClassRecord( private static readonly int _field; - private static int Property { get; } + public static int Property { get; } + + public static int PropertyPrivateSet { get; private set; } + + public static int PropertyInternalSet { get; internal set; } private static void MethodWithParameter(int parameter) { } @@ -117,6 +121,8 @@ void IInterface.Method() public static readonly FieldDocItem ConstCharFieldDocItem = new(ClassDocItem, Get($"F:{typeof(AssemblyInfo).FullName}.{nameof(_constCharField)}"), null); public static readonly FieldDocItem FieldDocItem = new(ClassDocItem, Get($"F:{typeof(AssemblyInfo).FullName}.{nameof(_field)}"), null); public static readonly PropertyDocItem PropertyDocItem = new(ClassDocItem, Get($"P:{typeof(AssemblyInfo).FullName}.{nameof(Property)}"), null); + public static readonly PropertyDocItem PropertyPrivateSetDocItem = new(ClassDocItem, Get($"P:{typeof(AssemblyInfo).FullName}.{nameof(PropertyPrivateSet)}"), null); + public static readonly PropertyDocItem PropertyInternalSetDocItem = new(ClassDocItem, Get($"P:{typeof(AssemblyInfo).FullName}.{nameof(PropertyInternalSet)}"), null); public static readonly MethodDocItem MethodWithGenericConstrainsDocItem = new(ClassDocItem, Get($"M:{typeof(AssemblyInfo).FullName}.{nameof(MethodWithGenericConstrains)}``5"), null); public static readonly MethodDocItem MethodWithParameterDocItem = new(ClassDocItem, Get($"M:{typeof(AssemblyInfo).FullName}.{nameof(MethodWithParameter)}({typeof(int).FullName})"), null); public static readonly MethodDocItem MethodWithReturnDocItem = new(ClassDocItem, Get($"M:{typeof(AssemblyInfo).FullName}.{nameof(MoveNext)}"), null); diff --git a/source/DefaultDocumentation.Test/Markdown/AWriterTest.cs b/source/DefaultDocumentation.Test/Markdown/AWriterTest.cs index 868978a3..a7d3d1f7 100644 --- a/source/DefaultDocumentation.Test/Markdown/AWriterTest.cs +++ b/source/DefaultDocumentation.Test/Markdown/AWriterTest.cs @@ -87,6 +87,7 @@ protected AWriterTest() settings.AssemblyFile.Returns(new FileInfo("test.dll")); settings.ProjectDirectory.Returns(new DirectoryInfo(Path.GetTempPath())); settings.GeneratedPages.Returns(GetGeneratedPages()); + settings.GeneratedAccessModifiers.Returns(GetGeneratedAccessModifiers()); return settings; }); @@ -103,6 +104,8 @@ protected AWriterTest() protected virtual GeneratedPages GetGeneratedPages() => GeneratedPages.Assembly | GeneratedPages.Namespaces | GeneratedPages.Types | GeneratedPages.Members; + protected virtual GeneratedAccessModifiers GetGeneratedAccessModifiers() => GeneratedAccessModifiers.Api; + protected virtual IFileNameFactory GetFileNameFactory() => new DummyFileNameFactory(); protected virtual IUrlFactory[] GetUrlFactories() => new IUrlFactory[] diff --git a/source/DefaultDocumentation.Test/Markdown/Sections/DefinitionSectionTest.cs b/source/DefaultDocumentation.Test/Markdown/Sections/DefinitionSectionTest.cs index 3fb6f057..ee5c7b45 100644 --- a/source/DefaultDocumentation.Test/Markdown/Sections/DefinitionSectionTest.cs +++ b/source/DefaultDocumentation.Test/Markdown/Sections/DefinitionSectionTest.cs @@ -63,7 +63,28 @@ public void Write_should_write_When_FieldDocItem_and_constant_char() => Test( public void Write_should_write_When_PropertyDocItem() => Test( AssemblyInfo.PropertyDocItem, @"```csharp -private static int Property { get; } +public static int Property { get; } +```"); + + [Fact] + public void Write_should_write_When_PropertyDocItem_with_private_set() => Test( + AssemblyInfo.PropertyPrivateSetDocItem, +@"```csharp +public static int PropertyPrivateSet { get; } +```"); + + [Fact] + public void Write_should_write_When_PropertyDocItem_with_internal_set() => Test( + AssemblyInfo.PropertyInternalSetDocItem, +@"```csharp +public static int PropertyInternalSet { get; } +```"); + + [Fact] + public void Write_should_write_When_PropertyDocItem_with_init() => Test( + AssemblyInfo.RecordPropertyDocItem, +@"```csharp +public int Property { get; init; } ```"); [Fact] @@ -160,13 +181,6 @@ public void Write_should_write_When_TypeDocItem_for_record_struct() => Test( AssemblyInfo.StructRecordDocItem, @"```csharp public readonly record struct AssemblyInfo.StructRecord : System.IEquatable -```"); - - //[Fact] to uncomment once https://github.com/icsharpcode/ILSpy/issues/3159 is solved - public void Write_should_write_When_PropertyDocItem_with_init() => Test( - AssemblyInfo.RecordPropertyDocItem, -@"```csharp -public int Property { get; init; } ```"); } }