diff --git a/src/PublicApiGenerator/ApiGenerator.cs b/src/PublicApiGenerator/ApiGenerator.cs index eece2ac..6f2bbce 100644 --- a/src/PublicApiGenerator/ApiGenerator.cs +++ b/src/PublicApiGenerator/ApiGenerator.cs @@ -209,10 +209,24 @@ static CodeTypeDeclaration CreateTypeDeclaration(TypeDefinition publicType, stri // correct C#, but it's good enough for our API outline var name = publicType.Name; + var isStruct = publicType.IsValueType && !publicType.IsPrimitive && !publicType.IsEnum; + + var @readonly = isStruct && publicType.CustomAttributes.Any(a => + a.AttributeType.FullName == "System.Runtime.CompilerServices.IsReadOnlyAttribute"); + var index = name.IndexOf('`'); if (index != -1) name = name.Substring(0, index); - var declaration = new CodeTypeDeclaration(@static ? "static " + name : name) + + var declarationName = string.Empty; + if (@readonly) + declarationName += "readonly "; + if (@static) + declarationName += "static "; + + declarationName += name; + + var declaration = new CodeTypeDeclaration(declarationName) { CustomAttributes = CreateCustomAttributes(publicType, excludeAttributes), // TypeAttributes must be specified before the IsXXX as they manipulate TypeAttributes! @@ -220,7 +234,7 @@ static CodeTypeDeclaration CreateTypeDeclaration(TypeDefinition publicType, stri IsClass = publicType.IsClass, IsEnum = publicType.IsEnum, IsInterface = publicType.IsInterface, - IsStruct = publicType.IsValueType && !publicType.IsPrimitive && !publicType.IsEnum, + IsStruct = isStruct, }; if (declaration.IsInterface && publicType.BaseType != null) @@ -417,6 +431,7 @@ static string ConvertAttributeToCode(Func "System.Runtime.CompilerServices.ExtensionAttribute", "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute", "System.Runtime.CompilerServices.IteratorStateMachineAttribute", + "System.Runtime.CompilerServices.IsReadOnlyAttribute", "System.Reflection.DefaultMemberAttribute", "System.Diagnostics.DebuggableAttribute", "System.Diagnostics.DebuggerNonUserCodeAttribute", diff --git a/src/PublicApiGeneratorTests/PublicApiGeneratorTests.csproj b/src/PublicApiGeneratorTests/PublicApiGeneratorTests.csproj index 8c0b4de..654360b 100644 --- a/src/PublicApiGeneratorTests/PublicApiGeneratorTests.csproj +++ b/src/PublicApiGeneratorTests/PublicApiGeneratorTests.csproj @@ -3,6 +3,7 @@ netcoreapp2.1;net452 $(NoWarn);CS0067 + latest diff --git a/src/PublicApiGeneratorTests/Struct_readonly.cs b/src/PublicApiGeneratorTests/Struct_readonly.cs new file mode 100644 index 0000000..47c80a4 --- /dev/null +++ b/src/PublicApiGeneratorTests/Struct_readonly.cs @@ -0,0 +1,28 @@ +using System; +using PublicApiGeneratorTests.Examples; +using Xunit; + +namespace PublicApiGeneratorTests +{ + public class Struct_readonly : ApiGeneratorTestsBase + { + [Fact] + public void Should_output() + { + AssertPublicApi( +@"namespace PublicApiGeneratorTests.Examples +{ + public struct readonly ReadonlyStruct { } +}"); + } + } + + // ReSharper disable ClassNeverInstantiated.Global + namespace Examples + { + public readonly struct ReadonlyStruct + { + } + } + // ReSharper restore ClassNeverInstantiated.Global +} \ No newline at end of file