From 2b1f8af5a333554a55aafe9a8031d5cc1a7e9814 Mon Sep 17 00:00:00 2001 From: daver32 <38791383+daver32@users.noreply.github.com> Date: Sun, 15 Aug 2021 14:41:36 +0200 Subject: [PATCH] added support for records --- .../RecordInterfaceGenerationTests.cs | 54 +++++++++++++++++++ InterfaceGenerator/AutoInterfaceGenerator.cs | 17 +++--- InterfaceGenerator/SyntaxReceiver.cs | 14 +++-- 3 files changed, 75 insertions(+), 10 deletions(-) create mode 100644 InterfaceGenerator.Tests/RecordInterfaceGenerationTests.cs diff --git a/InterfaceGenerator.Tests/RecordInterfaceGenerationTests.cs b/InterfaceGenerator.Tests/RecordInterfaceGenerationTests.cs new file mode 100644 index 0000000..d300119 --- /dev/null +++ b/InterfaceGenerator.Tests/RecordInterfaceGenerationTests.cs @@ -0,0 +1,54 @@ +using System.Runtime.CompilerServices; +using FluentAssertions; +using Xunit; + +namespace InterfaceGenerator.Tests +{ + public class RecordInterfaceGenerationTests + { + private readonly ITestRecord _sut; + + public RecordInterfaceGenerationTests() + { + _sut = new TestRecord(420); + } + + [Fact] + public void RecordProperty_IsGenerated() + { + var prop = typeof(ITestRecord) + .GetProperty(nameof(TestRecord.RecordProperty)); + + prop.Should().NotBeNull(); + + prop.GetMethod.Should().NotBeNull(); + prop.SetMethod.Should().NotBeNull(); + prop.SetMethod.ReturnParameter.GetRequiredCustomModifiers().Should().Contain(typeof(IsExternalInit)); + + _sut.RecordProperty.Should().Be(420); + } + + [Fact] + public void RecordMethod_IsGenerated() + { + var method = typeof(ITestRecord).GetMethod( + nameof(TestRecord.RecordMethod)); + + method.Should().NotBeNull(); + method.ReturnType.Should().Be(typeof(void)); + + var parameters = method.GetParameters(); + parameters.Should().BeEmpty(); + + _sut.RecordMethod(); + } + } + + [GenerateAutoInterface] + internal record TestRecord(int RecordProperty) : ITestRecord + { + public void RecordMethod() + { + } + } +} \ No newline at end of file diff --git a/InterfaceGenerator/AutoInterfaceGenerator.cs b/InterfaceGenerator/AutoInterfaceGenerator.cs index f3ddd0d..056602a 100644 --- a/InterfaceGenerator/AutoInterfaceGenerator.cs +++ b/InterfaceGenerator/AutoInterfaceGenerator.cs @@ -236,7 +236,6 @@ namespace InterfaceGenerator if (propertySymbol.IsIndexer) { writer.Write("{0} this[", propertySymbol.Type); - //writer.WriteJoin(", ", propertySymbol.Parameters, (w, param) => { w.Write("{0} {1}", param.Type, param.Name); }); writer.WriteJoin(", ", propertySymbol.Parameters, WriteMethodParam); writer.Write("] "); } @@ -273,6 +272,12 @@ namespace InterfaceGenerator { return; } + + if (methodSymbol.IsImplicitlyDeclared) + { + // omit methods that are auto generated by the compiler (eg. record's methods) + return; + } WriteMemberDocs(writer, methodSymbol); @@ -386,14 +391,14 @@ namespace InterfaceGenerator private static IEnumerable GetImplTypeSymbols(Compilation compilation, SyntaxReceiver receiver) { - return receiver.CandidateClasses.Select(candidate => GetClassSymbol(compilation, candidate)); + return receiver.CandidateTypes.Select(candidate => GetTypeSymbol(compilation, candidate)); } - private static INamedTypeSymbol GetClassSymbol(Compilation compilation, ClassDeclarationSyntax @class) + private static INamedTypeSymbol GetTypeSymbol(Compilation compilation, TypeDeclarationSyntax type) { - var model = compilation.GetSemanticModel(@class.SyntaxTree); - var classSymbol = ModelExtensions.GetDeclaredSymbol(model, @class)!; - return (INamedTypeSymbol)classSymbol; + var model = compilation.GetSemanticModel(type.SyntaxTree); + var typeSymbol = ModelExtensions.GetDeclaredSymbol(model, type)!; + return (INamedTypeSymbol)typeSymbol; } private static Compilation GetCompilation(GeneratorExecutionContext context) diff --git a/InterfaceGenerator/SyntaxReceiver.cs b/InterfaceGenerator/SyntaxReceiver.cs index 7ca7f04..570b42b 100644 --- a/InterfaceGenerator/SyntaxReceiver.cs +++ b/InterfaceGenerator/SyntaxReceiver.cs @@ -6,15 +6,21 @@ namespace InterfaceGenerator { internal class SyntaxReceiver : ISyntaxReceiver { - public IList CandidateClasses { get; } = new List(); + public IList CandidateTypes { get; } = new List(); public void OnVisitSyntaxNode(SyntaxNode syntaxNode) { - if (syntaxNode is ClassDeclarationSyntax classDeclarationSyntax && - classDeclarationSyntax.AttributeLists.Count > 0) + if (syntaxNode is TypeDeclarationSyntax typeDeclarationSyntax && + IsClassOrRecord(typeDeclarationSyntax) && + typeDeclarationSyntax.AttributeLists.Count > 0) { - CandidateClasses.Add(classDeclarationSyntax); + CandidateTypes.Add(typeDeclarationSyntax); } } + + private static bool IsClassOrRecord(TypeDeclarationSyntax typeDeclarationSyntax) + { + return typeDeclarationSyntax is ClassDeclarationSyntax || typeDeclarationSyntax is RecordDeclarationSyntax; + } } } \ No newline at end of file