diff --git a/.github/workflows/BuildAndTest.yml b/.github/workflows/BuildAndTest.yml new file mode 100644 index 0000000..db86bae --- /dev/null +++ b/.github/workflows/BuildAndTest.yml @@ -0,0 +1,31 @@ +name: Build and Test + +on: + pull_request: + branches: + - '**' + push: + branches: + - 'main' + +jobs: + build-and-test: + name: Build and Test on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [windows-2022] + + steps: + - uses: actions/checkout@v4 + + - name: Setup .NET 8 SDK + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: 'Build' + run: dotnet build "./FluentBuilder Solution.sln" -c Release + + - name: 'Test' + run: dotnet test ./tests/FluentBuilderGeneratorTests/FluentBuilderGeneratorTests.csproj --no-build -c Release \ No newline at end of file diff --git a/FluentBuilder Solution.sln b/FluentBuilder Solution.sln index 08f6784..07eea4b 100644 --- a/FluentBuilder Solution.sln +++ b/FluentBuilder Solution.sln @@ -46,6 +46,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BuilderConsumerFileScopedNa EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GitHub Actions", "GitHub Actions", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" ProjectSection(SolutionItems) = preProject + .github\workflows\BuildAndTest.yml = .github\workflows\BuildAndTest.yml .github\workflows\CreateRelease.yml = .github\workflows\CreateRelease.yml EndProjectSection EndProject diff --git a/FluentBuilder Solution.sln.DotSettings b/FluentBuilder Solution.sln.DotSettings index d84748d..74a6126 100644 --- a/FluentBuilder Solution.sln.DotSettings +++ b/FluentBuilder Solution.sln.DotSettings @@ -1,3 +1,7 @@  + True + True True + True + True True \ No newline at end of file diff --git a/src-examples/BuilderConsumerFileScopedNamespace/BuilderConsumerFileScopedNamespace.csproj b/src-examples/BuilderConsumerFileScopedNamespace/BuilderConsumerFileScopedNamespace.csproj index d4efa64..285d5de 100644 --- a/src-examples/BuilderConsumerFileScopedNamespace/BuilderConsumerFileScopedNamespace.csproj +++ b/src-examples/BuilderConsumerFileScopedNamespace/BuilderConsumerFileScopedNamespace.csproj @@ -12,8 +12,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src-examples/BuilderConsumerNET45/BuilderConsumerNET45_viaNuGet.csproj b/src-examples/BuilderConsumerNET45/BuilderConsumerNET45_viaNuGet.csproj index 1c9b54f..a908642 100644 --- a/src-examples/BuilderConsumerNET45/BuilderConsumerNET45_viaNuGet.csproj +++ b/src-examples/BuilderConsumerNET45/BuilderConsumerNET45_viaNuGet.csproj @@ -6,11 +6,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src-examples/BuilderConsumerNET45/Program.cs b/src-examples/BuilderConsumerNET45/Program.cs index c72526b..e92b991 100644 --- a/src-examples/BuilderConsumerNET45/Program.cs +++ b/src-examples/BuilderConsumerNET45/Program.cs @@ -14,7 +14,7 @@ static void Main(string[] args) Console.WriteLine("t0 = " + JsonConvert.SerializeObject(t0)); var t1 = new ThingWithOnlyParameterizedConstructorsBuilder() - .WithConstructor(1,2,"xxx") + .UsingConstructor(1,2,"xxx") .Build(); Console.WriteLine("t1 = " + JsonConvert.SerializeObject(t1)); diff --git a/src-examples/BuilderConsumerNET72/BuilderConsumerNET72.csproj b/src-examples/BuilderConsumerNET72/BuilderConsumerNET72.csproj index 65f4ae0..c7eaa31 100644 --- a/src-examples/BuilderConsumerNET72/BuilderConsumerNET72.csproj +++ b/src-examples/BuilderConsumerNET72/BuilderConsumerNET72.csproj @@ -1,4 +1,4 @@ - + Exe diff --git a/src-examples/BuilderConsumerNET8_ViaNuGet/BuilderConsumerNET8_ViaNuGet.csproj b/src-examples/BuilderConsumerNET8_ViaNuGet/BuilderConsumerNET8_ViaNuGet.csproj index 7e9a32e..8017749 100644 --- a/src-examples/BuilderConsumerNET8_ViaNuGet/BuilderConsumerNET8_ViaNuGet.csproj +++ b/src-examples/BuilderConsumerNET8_ViaNuGet/BuilderConsumerNET8_ViaNuGet.csproj @@ -10,7 +10,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src-examples/BuilderConsumerNET8_ViaNuGet/ClassWithPrivateSetter.cs b/src-examples/BuilderConsumerNET8_ViaNuGet/ClassWithPrivateSetter.cs index 7202c51..046d62d 100644 --- a/src-examples/BuilderConsumerNET8_ViaNuGet/ClassWithPrivateSetter.cs +++ b/src-examples/BuilderConsumerNET8_ViaNuGet/ClassWithPrivateSetter.cs @@ -8,5 +8,11 @@ public class ClassWithPrivateSetter public int Value1 { get; private set; } public int Value2 { get; set; } + + [AutoGenerateBuilder(FluentBuilderAccessibility.PublicAndPrivate)] + private class PrivateClass + { + public int Test { get; set; } + } } } \ No newline at end of file diff --git a/src-extensions/CSharp.SourceGenerators.Extensions/CSharp.SourceGenerators.Extensions.csproj b/src-extensions/CSharp.SourceGenerators.Extensions/CSharp.SourceGenerators.Extensions.csproj index fc08acb..d8c172f 100644 --- a/src-extensions/CSharp.SourceGenerators.Extensions/CSharp.SourceGenerators.Extensions.csproj +++ b/src-extensions/CSharp.SourceGenerators.Extensions/CSharp.SourceGenerators.Extensions.csproj @@ -6,7 +6,7 @@ latest enable Stef Heyenrath - Some extensions for a ISourceGenerator which can be used to execute a SourceGenerator manually which makes unit-testing easy. + Some extensions for a ISourceGenerator and IIncrementalGenerator which can be used to execute a SourceGenerator manually which makes unit-testing easy. CSharp;SourceGenerator;SourceGenerators;Extensions;Execute;UnitTest;Test icon.png MIT @@ -27,8 +27,8 @@ - - + + \ No newline at end of file diff --git a/src-extensions/CSharp.SourceGenerators.Extensions/Compatibility/RequiredMemberAttribute.cs b/src-extensions/CSharp.SourceGenerators.Extensions/Compatibility/RequiredMemberAttribute.cs new file mode 100644 index 0000000..67c9a7b --- /dev/null +++ b/src-extensions/CSharp.SourceGenerators.Extensions/Compatibility/RequiredMemberAttribute.cs @@ -0,0 +1,13 @@ +// ReSharper disable once CheckNamespace +namespace System.Runtime.CompilerServices; + +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] +internal sealed class RequiredMemberAttribute : Attribute; + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)] +internal sealed class CompilerFeatureRequiredAttribute(string featureName) : Attribute +{ + public string FeatureName { get; } = featureName; + + public bool IsOptional { get; init; } +} \ No newline at end of file diff --git a/src-extensions/CSharp.SourceGenerators.Extensions/Models/ExecuteResult.cs b/src-extensions/CSharp.SourceGenerators.Extensions/Models/ExecuteResult.cs index 20e1617..fd7cd01 100644 --- a/src-extensions/CSharp.SourceGenerators.Extensions/Models/ExecuteResult.cs +++ b/src-extensions/CSharp.SourceGenerators.Extensions/Models/ExecuteResult.cs @@ -8,22 +8,32 @@ public class ExecuteResult /// /// The internally used GeneratorDriver is also returned here to support using https://github.com/VerifyTests/Verify.SourceGenerators. /// - public GeneratorDriver GeneratorDriver { get; set; } = null!; + public required GeneratorDriver GeneratorDriver { get; init; } /// /// A list of generated files. /// - public IReadOnlyList Files { get; set; } = new List(); + public required IReadOnlyList Files { get; init; } /// - /// A list of Errors + /// Gets the collection of diagnostics associated with the current operation or state. /// - public IReadOnlyList ErrorMessages { get; set; } = new List(); + public required IReadOnlyList Diagnostics { get; init; } /// - /// A list of Warnings + /// A list of Error messages /// - public IReadOnlyList WarningMessages { get; set; } = new List(); + public required IReadOnlyList ErrorMessages { get; init; } + + /// + /// A list of Warning messages + /// + public required IReadOnlyList WarningMessages { get; init; } + + /// + /// A list of Information messages + /// + public required IReadOnlyList InformationMessages { get; init; } /// /// Is the result valid. diff --git a/src-extensions/CSharp.SourceGenerators.Extensions/SourceGeneratorExtensions.cs b/src-extensions/CSharp.SourceGenerators.Extensions/SourceGeneratorExtensions.cs index 250fd1f..c1b1a11 100644 --- a/src-extensions/CSharp.SourceGenerators.Extensions/SourceGeneratorExtensions.cs +++ b/src-extensions/CSharp.SourceGenerators.Extensions/SourceGeneratorExtensions.cs @@ -29,7 +29,7 @@ public static ExecuteResult Execute( IReadOnlyList? additionalTextPaths = null ) { - return Execute(sourceGenerator, $"GeneratedNamespace_{Guid.NewGuid().ToString().Replace("-", "")}", sources, additionalTextPaths); + return Execute(sourceGenerator, GetRandomAssemblyName(), sources, additionalTextPaths); } /// @@ -47,6 +47,52 @@ public static ExecuteResult Execute( IReadOnlyList? additionalTextPaths = null ) { + return ExecuteInternal(() => CSharpGeneratorDriver.Create(sourceGenerator), assemblyName, sources, additionalTextPaths); + } + + /// + /// Executes and runs the specified . + /// + /// The SourceGenerator to execute. + /// Provide a list of sources which need to be analyzed and processed. + /// A list of additional files. + /// + public static ExecuteResult Execute( + this IIncrementalGenerator sourceGenerator, + IReadOnlyList sources, + IReadOnlyList? additionalTextPaths = null + ) + { + return Execute(sourceGenerator, GetRandomAssemblyName(), sources, additionalTextPaths); + } + + /// + /// Executes and runs the specified . + /// + /// The SourceGenerator to execute. + /// The assembly name. + /// Provide a list of sources which need to be analyzed and processed. + /// A list of additional files. + /// + public static ExecuteResult Execute( + this IIncrementalGenerator sourceGenerator, + string assemblyName, + IReadOnlyList sources, + IReadOnlyList? additionalTextPaths = null + ) + { + return ExecuteInternal(() => CSharpGeneratorDriver.Create(sourceGenerator), assemblyName, sources, additionalTextPaths); + } + + private static ExecuteResult ExecuteInternal( + Func driverFactory, + string assemblyName, + IReadOnlyList sources, + IReadOnlyList? additionalTextPaths = null + ) + { + var driver = driverFactory(); + var metadataReferences = AppDomain.CurrentDomain.GetAssemblies() .Where(a => !a.IsDynamic) .Select(a => MetadataReference.CreateFromFile(a.Location)) @@ -62,9 +108,7 @@ public static ExecuteResult Execute( metadataReferences, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); - var driver = CSharpGeneratorDriver - .Create(sourceGenerator) - .AddAdditionalTexts(ImmutableArray.CreateRange(additionalTexts)); + driver = driver.AddAdditionalTexts(ImmutableArray.CreateRange(additionalTexts)); var executedDriver = driver.RunGeneratorsAndUpdateCompilation( compilation, @@ -74,8 +118,10 @@ public static ExecuteResult Execute( return new ExecuteResult { GeneratorDriver = executedDriver, - WarningMessages = diagnostics.Where(d => d.Severity == DiagnosticSeverity.Warning).Select(d => d.GetMessage()).ToList(), - ErrorMessages = diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error).Select(d => d.GetMessage()).ToList(), + Diagnostics = diagnostics, + InformationMessages = diagnostics.Where(d => d.Severity == DiagnosticSeverity.Info).Select(d => d.GetMessage()).ToArray(), + WarningMessages = diagnostics.Where(d => d.Severity == DiagnosticSeverity.Warning).Select(d => d.GetMessage()).ToArray(), + ErrorMessages = diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error).Select(d => d.GetMessage()).ToArray(), Files = outputCompilation.SyntaxTrees .Where(st => !sources.Any(s => s.Path == st.FilePath)) .Select(st => new FileResult @@ -110,7 +156,7 @@ private static SyntaxTree GetSyntaxTree(SourceFile source) } else { - throw new InvalidOperationException("If AttributeToAddToClass is defined, the target must be a record or class."); + throw new InvalidOperationException("If AttributeToAddToClass is defined, the target must be a class or record."); } } else if (source.AttributeToAddToInterface is not null && TryAddExtraAttribute(syntaxTree, source.AttributeToAddToInterface.Value, out var interfaceNode)) @@ -187,4 +233,9 @@ private static bool TryParseArguments(AnyOf attributeToA attributeArgumentListSyntax = null; return false; } + + private static string GetRandomAssemblyName() + { + return $"CSharp.SourceGenerators.Extensions.Generated_{Guid.NewGuid().ToString().Replace("-", "")}"; + } } \ No newline at end of file diff --git a/src/FluentBuilderGenerator/AnalyzerReleases.Shipped.md b/src/FluentBuilderGenerator/AnalyzerReleases.Shipped.md new file mode 100644 index 0000000..2080dcd --- /dev/null +++ b/src/FluentBuilderGenerator/AnalyzerReleases.Shipped.md @@ -0,0 +1,3 @@ +; Shipped analyzer releases +; https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + diff --git a/src/FluentBuilderGenerator/AnalyzerReleases.Unshipped.md b/src/FluentBuilderGenerator/AnalyzerReleases.Unshipped.md new file mode 100644 index 0000000..418950c --- /dev/null +++ b/src/FluentBuilderGenerator/AnalyzerReleases.Unshipped.md @@ -0,0 +1,10 @@ +; Unshipped analyzer release +; https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +FBGERR0001 | FluentBuilderGenerator | Error | DiagnosticDescriptors +FBGINF0001 | FluentBuilderGenerator | Info | DiagnosticDescriptors +FBGINF0002 | FluentBuilderGenerator | Info | DiagnosticDescriptors \ No newline at end of file diff --git a/src/FluentBuilderGenerator/Compatibility/IsExternalInit.cs b/src/FluentBuilderGenerator/Compatibility/IsExternalInit.cs index ec49e6d..12b2b60 100644 --- a/src/FluentBuilderGenerator/Compatibility/IsExternalInit.cs +++ b/src/FluentBuilderGenerator/Compatibility/IsExternalInit.cs @@ -2,4 +2,5 @@ // ReSharper disable once CheckNamespace namespace System.Runtime.CompilerServices; +// ReSharper disable once RedundantTypeDeclarationBody internal class IsExternalInit { } \ No newline at end of file diff --git a/src/FluentBuilderGenerator/Compatibility/NullableAttributes.cs b/src/FluentBuilderGenerator/Compatibility/NullableAttributes.cs index a5fbb0e..066cbcb 100644 --- a/src/FluentBuilderGenerator/Compatibility/NullableAttributes.cs +++ b/src/FluentBuilderGenerator/Compatibility/NullableAttributes.cs @@ -4,7 +4,7 @@ namespace System.Diagnostics.CodeAnalysis; /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. -[AttributeUsage(AttributeTargets.Parameter, Inherited = false)] +[AttributeUsage(AttributeTargets.Parameter)] internal sealed class NotNullWhenAttribute : Attribute { /// Initializes the attribute with the specified return value condition. diff --git a/src/FluentBuilderGenerator/Constants/DiagnosticDescriptors.cs b/src/FluentBuilderGenerator/Constants/DiagnosticDescriptors.cs new file mode 100644 index 0000000..42afe16 --- /dev/null +++ b/src/FluentBuilderGenerator/Constants/DiagnosticDescriptors.cs @@ -0,0 +1,34 @@ +using Microsoft.CodeAnalysis; + +namespace FluentBuilderGenerator.Constants; + +internal static class DiagnosticDescriptors +{ + internal static readonly DiagnosticDescriptor Error = new( + "FBGERR0001", + "Source generator exception", + "An exception occurred during source generation: {0}", + nameof(FluentBuilderGenerator), + DiagnosticSeverity.Error, + true, + "An unhandled exception occurred while generating source code." + ); + + internal static readonly DiagnosticDescriptor ClassOrRecordModifierShouldBeInternalOrPublic = new( + "FBGINF0001", + "Information", + "Class or Record modifier should be 'public' or 'internal'", + nameof(FluentBuilderGenerator), + DiagnosticSeverity.Info, + true + ); + + internal static readonly DiagnosticDescriptor CustomBuilderClassModifierShouldBePartialAndInternalOrPublic = new( + "FBGINF0002", + "Information", + "Custom builder class should be 'partial' and 'public' or 'internal'", + nameof(FluentBuilderGenerator), + DiagnosticSeverity.Info, + true + ); +} \ No newline at end of file diff --git a/src/FluentBuilderGenerator/Extensions/ExceptionExtensions.cs b/src/FluentBuilderGenerator/Extensions/ExceptionExtensions.cs new file mode 100644 index 0000000..ce7edec --- /dev/null +++ b/src/FluentBuilderGenerator/Extensions/ExceptionExtensions.cs @@ -0,0 +1,12 @@ +using FluentBuilderGenerator.Constants; +using Microsoft.CodeAnalysis; + +namespace FluentBuilderGenerator.Extensions; + +internal static class ExceptionExtensions +{ + internal static Diagnostic ToDiagnostic(this Exception exception) + { + return Diagnostic.Create(DiagnosticDescriptors.Error, Location.None, exception); + } +} \ No newline at end of file diff --git a/src/FluentBuilderGenerator/Extensions/PropertySymbolExtensions.cs b/src/FluentBuilderGenerator/Extensions/PropertySymbolExtensions.cs index fe7fc89..77f11ed 100644 --- a/src/FluentBuilderGenerator/Extensions/PropertySymbolExtensions.cs +++ b/src/FluentBuilderGenerator/Extensions/PropertySymbolExtensions.cs @@ -37,7 +37,7 @@ internal static bool TryGetIDictionaryElementTypes(this IPropertyOrParameterSymb if (type == FluentTypeKind.IDictionary && property.Type is INamedTypeSymbol namedTypeSymbol) { - if (namedTypeSymbol.IsGenericType && namedTypeSymbol.TypeArguments.Length == 2) + if (namedTypeSymbol is { IsGenericType: true, TypeArguments.Length: 2 }) { if (namedTypeSymbol.TypeArguments[0] is INamedTypeSymbol key && namedTypeSymbol.TypeArguments[1] is INamedTypeSymbol value) { @@ -47,7 +47,7 @@ internal static bool TryGetIDictionaryElementTypes(this IPropertyOrParameterSymb } } - tuple = default; + tuple = null; return false; } diff --git a/src/FluentBuilderGenerator/Extensions/SymbolExtensions.cs b/src/FluentBuilderGenerator/Extensions/SymbolExtensions.cs index a9a6630..12f6634 100644 --- a/src/FluentBuilderGenerator/Extensions/SymbolExtensions.cs +++ b/src/FluentBuilderGenerator/Extensions/SymbolExtensions.cs @@ -7,10 +7,10 @@ namespace FluentBuilderGenerator.Extensions; internal static class SymbolExtensions { private static readonly string[] ExcludedAttributes = - { + [ InternalClassNames.AsyncStateMachineAttribute , InternalClassNames.NullableAttribute - }; + ]; public static string GetDeterministicHashCodeAsString(this ISymbol symbol) { diff --git a/src/FluentBuilderGenerator/Extensions/SyntaxNodeExtensions.cs b/src/FluentBuilderGenerator/Extensions/SyntaxNodeExtensions.cs index adda0a9..75d5e7a 100644 --- a/src/FluentBuilderGenerator/Extensions/SyntaxNodeExtensions.cs +++ b/src/FluentBuilderGenerator/Extensions/SyntaxNodeExtensions.cs @@ -17,14 +17,13 @@ public static string GetNamespace(this SyntaxNode syntaxNode) { // If we don't have a namespace at all we'll return an empty string // This accounts for the "default namespace" case - string nameSpace = string.Empty; + var nameSpace = string.Empty; // Get the containing syntax node for the type declaration // (could be a nested type, for example) - SyntaxNode? potentialNamespaceParent = syntaxNode.Parent; + var potentialNamespaceParent = syntaxNode.Parent; - // Keep moving "out" of nested classes etc until we get to a namespace - // or until we run out of parents + // Keep moving "out" of nested classes until we get to a namespace or until we run out of parents while (potentialNamespaceParent != null && potentialNamespaceParent is not NamespaceDeclarationSyntax && potentialNamespaceParent is not FileScopedNamespaceDeclarationSyntax) diff --git a/src/FluentBuilderGenerator/Extensions/TypeDeclarationSyntaxExtensions.cs b/src/FluentBuilderGenerator/Extensions/TypeDeclarationSyntaxExtensions.cs index 680575a..3e9dd3b 100644 --- a/src/FluentBuilderGenerator/Extensions/TypeDeclarationSyntaxExtensions.cs +++ b/src/FluentBuilderGenerator/Extensions/TypeDeclarationSyntaxExtensions.cs @@ -8,9 +8,9 @@ namespace FluentBuilderGenerator.Extensions; /// internal static class TypeDeclarationSyntaxExtensions { - const char NESTED_CLASS_DELIMITER = '+'; - const char NAMESPACE_CLASS_DELIMITER = '.'; - const char TYPEPARAMETER_CLASS_DELIMITER = '`'; + private const char NestedClassDelimiter = '+'; + private const char NamespaceClassDelimiter = '.'; + private const char TypeparameterClassDelimiter = '`'; public static string GetMetadataName(this TypeDeclarationSyntax source) { @@ -31,14 +31,14 @@ public static string GetMetadataName(this TypeDeclarationSyntax source) var result = new StringBuilder(); for (var item = namespaces.First; item is not null; item = item.Next) { - result.Append(item.Value.Name).Append(NAMESPACE_CLASS_DELIMITER); + result.Append(item.Value.Name).Append(NamespaceClassDelimiter); } for (var item = types.First; item is not null; item = item.Next) { var type = item.Value; AppendName(result, type); - result.Append(NESTED_CLASS_DELIMITER); + result.Append(NestedClassDelimiter); } AppendName(result, source); @@ -51,7 +51,7 @@ private static void AppendName(StringBuilder builder, TypeDeclarationSyntax type var typeArguments = type.TypeParameterList?.ChildNodes().Count(node => node is TypeParameterSyntax) ?? 0; if (typeArguments != 0) { - builder.Append(TYPEPARAMETER_CLASS_DELIMITER).Append(typeArguments); + builder.Append(TypeparameterClassDelimiter).Append(typeArguments); } } } \ No newline at end of file diff --git a/src/FluentBuilderGenerator/FileGenerators/ExtraFilesGenerator.cs b/src/FluentBuilderGenerator/FileGenerators/ExtraFilesGenerator.cs index 2ae9143..f5f6931 100644 --- a/src/FluentBuilderGenerator/FileGenerators/ExtraFilesGenerator.cs +++ b/src/FluentBuilderGenerator/FileGenerators/ExtraFilesGenerator.cs @@ -3,163 +3,157 @@ using FluentBuilderGenerator.Extensions; using FluentBuilderGenerator.Models; using FluentBuilderGenerator.Types; -using FluentBuilderGenerator.Wrappers; namespace FluentBuilderGenerator.FileGenerators; -internal class ExtraFilesGenerator(IGeneratorExecutionContextWrapper context) : IFileGenerator +internal class ExtraFilesGenerator : IFileGenerator { private const string Name = "FluentBuilder.Extra.g.cs"; - private readonly bool _supportsNullable = context.SupportsNullable; - private readonly bool _supportsGenericAttributes = context.SupportsGenericAttributes; + private readonly bool _supportsNullable; + private readonly bool _supportsGenericAttributes; + + public ExtraFilesGenerator(bool supportsNullable, bool supportsGenericAttributes) + { + _supportsNullable = supportsNullable; + _supportsGenericAttributes = supportsGenericAttributes; + } public FileData GenerateFile() { - var autoGenerateBuilderAttribute = - $$""" - [AttributeUsage(AttributeTargets.Class)] - internal sealed class AutoGenerateBuilderAttribute : Attribute - { - public Type{{_supportsNullable.IIf("?")}} Type { get; } - public bool HandleBaseClasses { get; } - public FluentBuilderAccessibility Accessibility { get; } - public FluentBuilderMethods Methods { get; } - - public AutoGenerateBuilderAttribute() : this(null, true, FluentBuilderAccessibility.Public, FluentBuilderMethods.WithOnly) - { - } - - public AutoGenerateBuilderAttribute(bool handleBaseClasses) : this(null, handleBaseClasses, FluentBuilderAccessibility.Public, FluentBuilderMethods.WithOnly) - { - } - - public AutoGenerateBuilderAttribute(FluentBuilderAccessibility accessibility) : this(null, true, accessibility, FluentBuilderMethods.WithOnly) - { - } - - public AutoGenerateBuilderAttribute(FluentBuilderMethods methods) : this(null, true, FluentBuilderAccessibility.Public, methods) - { - } - - public AutoGenerateBuilderAttribute(bool handleBaseClasses, FluentBuilderAccessibility accessibility) : this(null, handleBaseClasses, accessibility, FluentBuilderMethods.WithOnly) - { - } - - public AutoGenerateBuilderAttribute(bool handleBaseClasses, FluentBuilderMethods methods) : this(null, handleBaseClasses, FluentBuilderAccessibility.Public, methods) - { - } - - public AutoGenerateBuilderAttribute(Type{{_supportsNullable.IIf("?")}} type) : this(type, true, FluentBuilderAccessibility.Public, FluentBuilderMethods.WithOnly) - { - } - - public AutoGenerateBuilderAttribute(Type{{_supportsNullable.IIf("?")}} type, bool handleBaseClasses) : this(type, handleBaseClasses, FluentBuilderAccessibility.Public, FluentBuilderMethods.WithOnly) - { - } - - public AutoGenerateBuilderAttribute(Type{{_supportsNullable.IIf("?")}} type, FluentBuilderAccessibility accessibility) : this(type, true, accessibility, FluentBuilderMethods.WithOnly) - { - } - - public AutoGenerateBuilderAttribute(Type{{_supportsNullable.IIf("?")}} type, bool handleBaseClasses, FluentBuilderAccessibility accessibility) : this(type, handleBaseClasses, accessibility, FluentBuilderMethods.WithOnly) - { - } - - public AutoGenerateBuilderAttribute(Type{{_supportsNullable.IIf("?")}} type, bool handleBaseClasses, FluentBuilderMethods methods) : this(type, handleBaseClasses, FluentBuilderAccessibility.Public, methods) - { - } - - public AutoGenerateBuilderAttribute(Type{{_supportsNullable.IIf("?")}} type, bool handleBaseClasses, FluentBuilderAccessibility accessibility, FluentBuilderMethods methods) - { - Type = type; - HandleBaseClasses = handleBaseClasses; - Accessibility = accessibility; - Methods = methods; - } - } - """; - - const string autoGenerateBuilderAttributeGeneric = - """ - [AttributeUsage(AttributeTargets.Class)] - internal sealed class AutoGenerateBuilderAttribute : Attribute where T : class - { - public Type Type { get; } - public bool HandleBaseClasses { get; } - public FluentBuilderAccessibility Accessibility { get; } - public FluentBuilderMethods Methods { get; } - - public AutoGenerateBuilderAttribute() : this(true, FluentBuilderAccessibility.Public, FluentBuilderMethods.WithOnly) - { - } - - public AutoGenerateBuilderAttribute(bool handleBaseClasses) : this(handleBaseClasses, FluentBuilderAccessibility.Public, FluentBuilderMethods.WithOnly) - { - } - - public AutoGenerateBuilderAttribute(FluentBuilderAccessibility accessibility) : this(true, accessibility, FluentBuilderMethods.WithOnly) - { - } - - public AutoGenerateBuilderAttribute(bool handleBaseClasses, FluentBuilderAccessibility accessibility) : this(handleBaseClasses, accessibility, FluentBuilderMethods.WithOnly) - { - } - - public AutoGenerateBuilderAttribute(bool handleBaseClasses, FluentBuilderMethods methods) : this(handleBaseClasses, FluentBuilderAccessibility.Public, methods) - { - } - - public AutoGenerateBuilderAttribute(bool handleBaseClasses, FluentBuilderAccessibility accessibility, FluentBuilderMethods methods) - { - Type = typeof(T); - HandleBaseClasses = handleBaseClasses; - Accessibility = accessibility; - Methods = methods; - } - } - """; - - var text = - $$""" - {{Header.Text}} - - {{_supportsNullable.IIf("#nullable enable")}} - using System; - - namespace FluentBuilder - { - {{autoGenerateBuilderAttribute}} - - {{_supportsGenericAttributes.IIf(autoGenerateBuilderAttributeGeneric)}} - - [AttributeUsage(AttributeTargets.Property)] - internal sealed class FluentBuilderIgnoreAttribute : Attribute - { - } - - [Flags] - internal enum FluentBuilderAccessibility - { - Public = 0, - PublicAndPrivate = 1 - } - - [Flags] - internal enum FluentBuilderMethods - { - WithOnly = 0, - WithAndWithout = 1 - } - } - {{_supportsNullable.IIf("#nullable restore")}} - """; + var autoGenerateBuilderAttribute = $@"[AttributeUsage(AttributeTargets.Class)] + internal sealed class AutoGenerateBuilderAttribute : Attribute + {{ + public Type{_supportsNullable.IIf("?")} Type {{ get; }} + public bool HandleBaseClasses {{ get; }} + public FluentBuilderAccessibility Accessibility {{ get; }} + public FluentBuilderMethods Methods {{ get; }} + + public AutoGenerateBuilderAttribute() : this(null, true, FluentBuilderAccessibility.Public, FluentBuilderMethods.WithOnly) + {{ + }} + + public AutoGenerateBuilderAttribute(bool handleBaseClasses) : this(null, handleBaseClasses, FluentBuilderAccessibility.Public, FluentBuilderMethods.WithOnly) + {{ + }} + + public AutoGenerateBuilderAttribute(FluentBuilderAccessibility accessibility) : this(null, true, accessibility, FluentBuilderMethods.WithOnly) + {{ + }} + + public AutoGenerateBuilderAttribute(FluentBuilderMethods methods) : this(null, true, FluentBuilderAccessibility.Public, methods) + {{ + }} + + public AutoGenerateBuilderAttribute(bool handleBaseClasses, FluentBuilderAccessibility accessibility) : this(null, handleBaseClasses, accessibility, FluentBuilderMethods.WithOnly) + {{ + }} + + public AutoGenerateBuilderAttribute(bool handleBaseClasses, FluentBuilderMethods methods) : this(null, handleBaseClasses, FluentBuilderAccessibility.Public, methods) + {{ + }} + + public AutoGenerateBuilderAttribute(Type{_supportsNullable.IIf("?")} type) : this(type, true, FluentBuilderAccessibility.Public, FluentBuilderMethods.WithOnly) + {{ + }} + + public AutoGenerateBuilderAttribute(Type{_supportsNullable.IIf("?")} type, bool handleBaseClasses) : this(type, handleBaseClasses, FluentBuilderAccessibility.Public, FluentBuilderMethods.WithOnly) + {{ + }} + + public AutoGenerateBuilderAttribute(Type{_supportsNullable.IIf("?")} type, FluentBuilderAccessibility accessibility) : this(type, true, accessibility, FluentBuilderMethods.WithOnly) + {{ + }} + + public AutoGenerateBuilderAttribute(Type{_supportsNullable.IIf("?")} type, bool handleBaseClasses, FluentBuilderAccessibility accessibility) : this(type, handleBaseClasses, accessibility, FluentBuilderMethods.WithOnly) + {{ + }} + + public AutoGenerateBuilderAttribute(Type{_supportsNullable.IIf("?")} type, bool handleBaseClasses, FluentBuilderMethods methods) : this(type, handleBaseClasses, FluentBuilderAccessibility.Public, methods) + {{ + }} + + public AutoGenerateBuilderAttribute(Type{_supportsNullable.IIf("?")} type, bool handleBaseClasses, FluentBuilderAccessibility accessibility, FluentBuilderMethods methods) + {{ + Type = type; + HandleBaseClasses = handleBaseClasses; + Accessibility = accessibility; + Methods = methods; + }} + }}"; + + var autoGenerateBuilderAttributeGeneric = @"[AttributeUsage(AttributeTargets.Class)] + internal sealed class AutoGenerateBuilderAttribute : Attribute where T : class + { + public Type Type { get; } + public bool HandleBaseClasses { get; } + public FluentBuilderAccessibility Accessibility { get; } + public FluentBuilderMethods Methods { get; } + + public AutoGenerateBuilderAttribute() : this(true, FluentBuilderAccessibility.Public, FluentBuilderMethods.WithOnly) + { + } + + public AutoGenerateBuilderAttribute(bool handleBaseClasses) : this(handleBaseClasses, FluentBuilderAccessibility.Public, FluentBuilderMethods.WithOnly) + { + } + + public AutoGenerateBuilderAttribute(FluentBuilderAccessibility accessibility) : this(true, accessibility, FluentBuilderMethods.WithOnly) + { + } + + public AutoGenerateBuilderAttribute(bool handleBaseClasses, FluentBuilderAccessibility accessibility) : this(handleBaseClasses, accessibility, FluentBuilderMethods.WithOnly) + { + } + + public AutoGenerateBuilderAttribute(bool handleBaseClasses, FluentBuilderMethods methods) : this(handleBaseClasses, FluentBuilderAccessibility.Public, methods) + { + } + + public AutoGenerateBuilderAttribute(bool handleBaseClasses, FluentBuilderAccessibility accessibility, FluentBuilderMethods methods) + { + Type = typeof(T); + HandleBaseClasses = handleBaseClasses; + Accessibility = accessibility; + Methods = methods; + } + }"; return new FileData ( FileDataType.Attribute, Name, - text + $@"{Header.Text} + +{_supportsNullable.IIf("#nullable enable")} +using System; + +namespace FluentBuilder +{{ + {autoGenerateBuilderAttribute} + + {_supportsGenericAttributes.IIf(autoGenerateBuilderAttributeGeneric)} + + [AttributeUsage(AttributeTargets.Property)] + internal sealed class FluentBuilderIgnoreAttribute : Attribute + {{ + }} + + [Flags] + internal enum FluentBuilderAccessibility + {{ + Public = 0, + PublicAndPrivate = 1 + }} + + [Flags] + internal enum FluentBuilderMethods + {{ + WithOnly = 0, + WithAndWithout = 1 + }} +}} +{_supportsNullable.IIf("#nullable disable")}" ); } } \ No newline at end of file diff --git a/src/FluentBuilderGenerator/FileGenerators/FluentBuilderClassesGenerator.IDictionary.cs b/src/FluentBuilderGenerator/FileGenerators/FluentBuilderClassesGenerator.IDictionary.cs index 30cf837..ecc57b6 100644 --- a/src/FluentBuilderGenerator/FileGenerators/FluentBuilderClassesGenerator.IDictionary.cs +++ b/src/FluentBuilderGenerator/FileGenerators/FluentBuilderClassesGenerator.IDictionary.cs @@ -30,9 +30,9 @@ private StringBuilder GenerateWithIDictionaryBuilderActionMethod( var cast = property.Type.TypeKind == TypeKind.Interface ? "" : $"({property.Type}) "; return new StringBuilder() - .AppendLine($" public {className} With{property.Name}(Action<{_context.AssemblyName}.FluentBuilder.{dictionaryBuilderName}> action, bool useObjectInitializer = true) => With{property.Name}(() =>") + .AppendLine($" public {className} With{property.Name}(Action<{_compilationHelper.AssemblyName}.FluentBuilder.{dictionaryBuilderName}> action, bool useObjectInitializer = true) => With{property.Name}(() =>") .AppendLine(" {") - .AppendLine($" var builder = new {_context.AssemblyName}.FluentBuilder.{dictionaryBuilderName}();") + .AppendLine($" var builder = new {_compilationHelper.AssemblyName}.FluentBuilder.{dictionaryBuilderName}();") .AppendLine(" action(builder);") .AppendLine($" return {cast}builder.Build(useObjectInitializer);") .AppendLine(" });"); diff --git a/src/FluentBuilderGenerator/FileGenerators/FluentBuilderClassesGenerator.IEnumerable.cs b/src/FluentBuilderGenerator/FileGenerators/FluentBuilderClassesGenerator.IEnumerable.cs index b28566f..6884382 100644 --- a/src/FluentBuilderGenerator/FileGenerators/FluentBuilderClassesGenerator.IEnumerable.cs +++ b/src/FluentBuilderGenerator/FileGenerators/FluentBuilderClassesGenerator.IEnumerable.cs @@ -20,11 +20,11 @@ private string CreateIEnumerableBuilderCode(ClassSymbol classSymbol) // //------------------------------------------------------------------------------ -{(_context.SupportsNullable ? "#nullable enable" : string.Empty)} +{(_supportsNullable ? "#nullable enable" : string.Empty)} using System; using System.Collections; using System.Collections.Generic; -using {_context.AssemblyName}.FluentBuilder; +using {_compilationHelper.AssemblyName}.FluentBuilder; using {classSymbol.NamedTypeSymbol.ContainingNamespace}; namespace {classSymbol.BuilderNamespace} @@ -37,7 +37,7 @@ public partial class {classSymbol.BuilderClassName} : Builder<{t}> {GenerateBuildMethodForIEnumerableBuilder(classSymbol)} }} }} -{(_context.SupportsNullable ? "#nullable disable" : string.Empty)}"; +{(_supportsNullable ? "#nullable disable" : string.Empty)}"; } private static StringBuilder GenerateAddMethodsForIEnumerableBuilder(ClassSymbol classSymbol) diff --git a/src/FluentBuilderGenerator/FileGenerators/FluentBuilderClassesGenerator.cs b/src/FluentBuilderGenerator/FileGenerators/FluentBuilderClassesGenerator.cs index c605bb4..57258d1 100644 --- a/src/FluentBuilderGenerator/FileGenerators/FluentBuilderClassesGenerator.cs +++ b/src/FluentBuilderGenerator/FileGenerators/FluentBuilderClassesGenerator.cs @@ -1,51 +1,51 @@ // This source code is based on https://justsimplycode.com/2020/12/06/auto-generate-builders-using-source-generator-in-net-5 -using System.Text; using FluentBuilderGenerator.Extensions; using FluentBuilderGenerator.Helpers; using FluentBuilderGenerator.Interfaces; using FluentBuilderGenerator.Models; -using FluentBuilderGenerator.SyntaxReceiver; using FluentBuilderGenerator.Types; using FluentBuilderGenerator.Wrappers; using Microsoft.CodeAnalysis; +using System.Text; namespace FluentBuilderGenerator.FileGenerators; internal partial class FluentBuilderClassesGenerator : IFilesGenerator { private static readonly string[] FluentBuilderIgnoreAttributeClassNames = - { + [ "FluentBuilder.FluentBuilderIgnoreAttribute", "FluentBuilderIgnoreAttribute", "FluentBuilder.FluentBuilderIgnore", "FluentBuilderIgnore" - }; + ]; private static readonly string[] SystemUsings = - { + [ "System", "System.Collections", "System.Collections.Generic", "System.Reflection" - }; + ]; private static readonly FileDataType[] ExtraBuilders = - { + [ FileDataType.ArrayBuilder, FileDataType.ICollectionBuilder, FileDataType.IEnumerableBuilder, FileDataType.IListBuilder, FileDataType.IReadOnlyCollectionBuilder, FileDataType.IReadOnlyListBuilder - }; - - private readonly IGeneratorExecutionContextWrapper _context; - private readonly IAutoGenerateBuilderSyntaxReceiver _receiver; + ]; + private readonly IList _items; + private readonly CompilationHelper _compilationHelper; + private readonly bool _supportsNullable; - public FluentBuilderClassesGenerator(IGeneratorExecutionContextWrapper context, IAutoGenerateBuilderSyntaxReceiver receiver) + public FluentBuilderClassesGenerator(IList items, CompilationHelper compilationHelper, bool supportsNullable) { - _context = context; - _receiver = receiver; + _items = items; + _compilationHelper = compilationHelper; + _supportsNullable = supportsNullable; } public IReadOnlyList GenerateFiles() @@ -90,38 +90,42 @@ private string CreateClassBuilderCode(FluentData fluentData, ClassSymbol classSy var propertiesCode = GenerateWithPropertyCode(fluentData, classSymbol, allClassSymbols); var usings = SystemUsings.ToList(); - usings.Add($"{_context.AssemblyName}.FluentBuilder"); + usings.Add($"{_compilationHelper.AssemblyName}.FluentBuilder"); usings.Add($"{classSymbol.NamedTypeSymbol.ContainingNamespace}"); usings.AddRange(propertiesCode.ExtraUsings); var usingsAsStrings = string.Join("\r\n", usings.Distinct().Select(u => $"using {u};")); - return - $$""" - {{Header.Text}} - - {{(_context.SupportsNullable ? "#nullable enable" : string.Empty)}} - {{usingsAsStrings}} - - namespace {{classSymbol.BuilderNamespace}} - { - public static partial class {{classSymbol.BuilderClassName.ToSafeClassName()}}Extensions - { - {{fluentData.ClassModifier}} static {{classSymbol.BuilderClassName}} AsBuilder{{classSymbol.NamedTypeSymbol.GetTypeArguments()}}(this {{classSymbol.NamedTypeSymbol}} instance){{classSymbol.NamedTypeSymbol.GetWhereStatement()}} - { - return new {{classSymbol.BuilderClassName}}().UsingInstance(instance); - } - } - - {{fluentData.ClassModifier}} partial class {{classSymbol.BuilderClassName}} : Builder<{{classSymbol.NamedTypeSymbol}}>{{classSymbol.NamedTypeSymbol.GetWhereStatement()}} - { - {{propertiesCode.StringBuilder}} - {{constructorCode.StringBuilder}} - {{GenerateSeveralMethods(fluentData, classSymbol)}} - } - } - {{(_context.SupportsNullable ? "#nullable disable" : string.Empty)}} - """; + return $@"//------------------------------------------------------------------------------ +// +// This code was generated by https://github.com/StefH/FluentBuilder version {System.Reflection.Assembly.GetExecutingAssembly().GetName().Version} +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +{(_supportsNullable ? "#nullable enable" : string.Empty)} +{usingsAsStrings} + +namespace {classSymbol.BuilderNamespace} +{{ + public static partial class {classSymbol.BuilderClassName.ToSafeClassName()}Extensions + {{ + {fluentData.ClassModifier} static {classSymbol.BuilderClassName} AsBuilder{classSymbol.NamedTypeSymbol.GetTypeArguments()}(this {classSymbol.NamedTypeSymbol} instance){classSymbol.NamedTypeSymbol.GetWhereStatement()} + {{ + return new {classSymbol.BuilderClassName}().UsingInstance(instance); + }} + }} + + {fluentData.ClassModifier} partial class {classSymbol.BuilderClassName} : Builder<{classSymbol.NamedTypeSymbol}>{classSymbol.NamedTypeSymbol.GetWhereStatement()} + {{ +{propertiesCode.StringBuilder} +{constructorCode.StringBuilder} +{GenerateSeveralMethods(fluentData, classSymbol)} + }} +}} +{(_supportsNullable ? "#nullable disable" : string.Empty)}"; } private static (StringBuilder StringBuilder, IReadOnlyList ExtraUsings) GenerateUsingConstructorCode( @@ -363,7 +367,7 @@ private StringBuilder GenerateWithIEnumerableBuilderActionMethod( else { // Normal - fullBuilderName = $"{_context.AssemblyName}.FluentBuilder.{kind}Builder{(typeSymbol == null ? string.Empty : "<" + typeSymbol.GenerateFullTypeName() + ">")}"; + fullBuilderName = $"{_compilationHelper.AssemblyName}.FluentBuilder.{kind}Builder{(typeSymbol == null ? string.Empty : "<" + typeSymbol.GenerateFullTypeName() + ">")}"; } // If the property.Type is an interface or array, no cast is needed. Else cast the interface to the real type. @@ -554,9 +558,9 @@ private static void BuildPrivateSetMethod(StringBuilder output, string className private IReadOnlyList<(ClassSymbol ClassSymbol, FluentData FluentData)> GetClassSymbols() { var classSymbols = new List<(ClassSymbol ClassSymbol, FluentData FluentData)>(); - foreach (var fluentDataItem in _receiver.CandidateFluentDataItems) + foreach (var fluentDataItem in _items) { - if (_context.TryGetNamedTypeSymbolByFullMetadataName(fluentDataItem, out var classSymbol)) + if (_compilationHelper.TryGetNamedTypeSymbolByFullMetadataName(fluentDataItem, out var classSymbol)) { classSymbols.Add((classSymbol, fluentDataItem)); } diff --git a/src/FluentBuilderGenerator/FluentBuilderGenerator.csproj b/src/FluentBuilderGenerator/FluentBuilderGenerator.csproj index baa87b5..36abfa4 100644 --- a/src/FluentBuilderGenerator/FluentBuilderGenerator.csproj +++ b/src/FluentBuilderGenerator/FluentBuilderGenerator.csproj @@ -1,7 +1,7 @@ - + - 0.11.0 + 0.12.0 netstandard2.0 latest true @@ -28,6 +28,7 @@ true enable Debug;Release;DebugAttach + true @@ -52,11 +53,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/FluentBuilderGenerator/FluentBuilderSourceGenerator.cs b/src/FluentBuilderGenerator/FluentBuilderSourceGenerator.cs index 0153112..cc2173b 100644 --- a/src/FluentBuilderGenerator/FluentBuilderSourceGenerator.cs +++ b/src/FluentBuilderGenerator/FluentBuilderSourceGenerator.cs @@ -1,19 +1,22 @@ // This source code is based on https://justsimplycode.com/2020/12/06/auto-generate-builders-using-source-generator-in-net-5 using System.Text; +using FluentBuilderGenerator.Extensions; using FluentBuilderGenerator.FileGenerators; +using FluentBuilderGenerator.Models; using FluentBuilderGenerator.SyntaxReceiver; using FluentBuilderGenerator.Types; using FluentBuilderGenerator.Wrappers; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Text; namespace FluentBuilderGenerator; -[Generator] -internal class FluentBuilderSourceGenerator : ISourceGenerator +[Generator(LanguageNames.CSharp)] +internal class FluentBuilderSourceGenerator : IIncrementalGenerator { - public void Initialize(GeneratorInitializationContext context) + public void Initialize(IncrementalGeneratorInitializationContext context) { #if DEBUGATTACH if (!System.Diagnostics.Debugger.IsAttached) @@ -21,68 +24,114 @@ public void Initialize(GeneratorInitializationContext context) System.Diagnostics.Debugger.Launch(); } #endif - context.RegisterForSyntaxNotifications(() => new AutoGenerateBuilderSyntaxReceiver()); - } - public void Execute(GeneratorExecutionContext context) - { - var contextWrapper = new GeneratorExecutionContextWrapper(context); + var languageDataProvider = context.ParseOptionsProvider.Select(static (options, _) => + { + if (options is not CSharpParseOptions csParseOptions) + { + throw new NotSupportedException($"Only {LanguageNames.CSharp} is supported."); + } + + return new LanguageData + { + SupportsNullable = csParseOptions.LanguageVersion >= LanguageVersion.CSharp8, + SupportsGenericAttributes = csParseOptions.LanguageVersion >= LanguageVersion.CSharp11 + }; + }); + + var compilationHelperProvider = context.CompilationProvider.Select(static (compilation, _) => new CompilationHelper(compilation)); - try + var combinedProvider = languageDataProvider.Combine(compilationHelperProvider); + + context.RegisterSourceOutput(combinedProvider, static (SourceProductionContext spc, (LanguageData LanguageData, CompilationHelper CompilationHelper) x) => { - InjectGeneratedClasses(contextWrapper); + var assemblyName = x.CompilationHelper.AssemblyName; + var supportsNullable = x.LanguageData.SupportsNullable; + + var generators = new IFileGenerator[] + { + new BaseBuilderGenerator(assemblyName, x.LanguageData.SupportsNullable), + + new ExtraFilesGenerator(supportsNullable, x.LanguageData.SupportsGenericAttributes), - if (context.SyntaxContextReceiver is not AutoGenerateBuilderSyntaxReceiver receiver) + new IDictionaryBuilderGenerator(assemblyName, supportsNullable), + + new IEnumerableBuilderGenerator(assemblyName, FileDataType.ArrayBuilder, supportsNullable), + new IEnumerableBuilderGenerator(assemblyName, FileDataType.ICollectionBuilder, supportsNullable), + new IEnumerableBuilderGenerator(assemblyName, FileDataType.IEnumerableBuilder, supportsNullable), + new IEnumerableBuilderGenerator(assemblyName, FileDataType.IListBuilder, supportsNullable), + new IEnumerableBuilderGenerator(assemblyName, FileDataType.IReadOnlyCollectionBuilder, supportsNullable), + new IEnumerableBuilderGenerator(assemblyName, FileDataType.IReadOnlyListBuilder, supportsNullable), + }; + + foreach (var generator in generators) { - return; + var fileData = generator.GenerateFile(); + spc.AddSource(fileData.FileName, SourceText.From(fileData.Text, Encoding.UTF8)); } + }); - InjectFluentBuilderClasses(contextWrapper, receiver); - } - catch (Exception exception) + var diagnostics = new List(); + + var fluentBuilderClassesProvider = context.SyntaxProvider + .CreateSyntaxProvider( + (syntaxNode, ct) => ShouldHandle(syntaxNode, ct, diagnostics), + (generatorSyntaxContext, ct) => Transform(generatorSyntaxContext, ct, diagnostics) + ) + .Collect(); + + var combined2 = fluentBuilderClassesProvider.Combine(combinedProvider).Select((x, _) => new { - GenerateError(contextWrapper, exception); - } - } - - private static void GenerateError(IGeneratorExecutionContextWrapper context, Exception exception) - { - var message = $"/*\r\n{nameof(FluentBuilderSourceGenerator)}\r\n\r\n[Exception]\r\n{exception}\r\n\r\n[StackTrace]\r\n{exception.StackTrace}*/"; - context.AddSource("Error.g.cs", SourceText.From(message, Encoding.UTF8)); - } + Items = x.Left, + LanguageData = x.Right.Left, + CompilationHelper = x.Right.Right + }); - private static void InjectGeneratedClasses(IGeneratorExecutionContextWrapper context) - { - var generators = new IFileGenerator[] + context.RegisterSourceOutput(combined2, (sourceProductionContext, data) => { - new BaseBuilderGenerator(context.AssemblyName, context.SupportsNullable), + foreach (var diagnostic in diagnostics) + { + sourceProductionContext.ReportDiagnostic(diagnostic); + } - new ExtraFilesGenerator(context), + var generator = new FluentBuilderClassesGenerator(data.Items, data.CompilationHelper, data.LanguageData.SupportsNullable); - new IDictionaryBuilderGenerator(context.AssemblyName, context.SupportsNullable), + try + { + foreach (var fileData in generator.GenerateFiles()) + { + sourceProductionContext.AddSource(fileData.FileName, SourceText.From(fileData.Text, Encoding.UTF8)); + } + } + catch (Exception exception) + { + var message = $"/*\r\n{nameof(FluentBuilderSourceGenerator)}\r\n\r\n[Exception]\r\n{exception}\r\n\r\n[StackTrace]\r\n{exception.StackTrace}*/"; + sourceProductionContext.AddSource("Error.g.cs", SourceText.From(message, Encoding.UTF8)); - new IEnumerableBuilderGenerator(context.AssemblyName, FileDataType.ArrayBuilder, context.SupportsNullable), - new IEnumerableBuilderGenerator(context.AssemblyName, FileDataType.ICollectionBuilder, context.SupportsNullable), - new IEnumerableBuilderGenerator(context.AssemblyName, FileDataType.IEnumerableBuilder, context.SupportsNullable), - new IEnumerableBuilderGenerator(context.AssemblyName, FileDataType.IListBuilder, context.SupportsNullable), - new IEnumerableBuilderGenerator(context.AssemblyName, FileDataType.IReadOnlyCollectionBuilder, context.SupportsNullable), - new IEnumerableBuilderGenerator(context.AssemblyName, FileDataType.IReadOnlyListBuilder, context.SupportsNullable), - }; + sourceProductionContext.ReportDiagnostic(exception.ToDiagnostic()); + } + }); + } - foreach (var generator in generators) + private static bool ShouldHandle(SyntaxNode syntaxNode, CancellationToken _, List diagnostics) + { + var result = AutoGenerateBuilderSyntaxReceiver.CheckSyntaxNode(syntaxNode, out var diagnostic); + if (diagnostic != null) { - var data = generator.GenerateFile(); - context.AddSource(data.FileName, SourceText.From(data.Text, Encoding.UTF8)); + diagnostics.Add(diagnostic); } + + return result; } - private static void InjectFluentBuilderClasses(IGeneratorExecutionContextWrapper context, IAutoGenerateBuilderSyntaxReceiver receiver) + private static FluentData Transform(GeneratorSyntaxContext gsc, CancellationToken _, List diagnostics) { - var generator = new FluentBuilderClassesGenerator(context, receiver); - - foreach (var data in generator.GenerateFiles()) + var result = AutoGenerateBuilderSyntaxReceiver.HandleSyntaxNode(gsc.Node, gsc.SemanticModel, out var diagnostic); + if (diagnostic != null) { - context.AddSource(data.FileName, SourceText.From(data.Text, Encoding.UTF8)); + diagnostics.Add(diagnostic); } + + return result; } } \ No newline at end of file diff --git a/src/FluentBuilderGenerator/Helpers/DefaultValueHelper.cs b/src/FluentBuilderGenerator/Helpers/DefaultValueHelper.cs index 8f73587..94b5ffb 100644 --- a/src/FluentBuilderGenerator/Helpers/DefaultValueHelper.cs +++ b/src/FluentBuilderGenerator/Helpers/DefaultValueHelper.cs @@ -8,7 +8,7 @@ namespace FluentBuilderGenerator.Helpers; internal static class DefaultValueHelper { - private static readonly SyntaxKind[] ExcludedSyntaxKinds = { SyntaxKind.SuppressNullableWarningExpression }; + private static readonly SyntaxKind[] ExcludedSyntaxKinds = [SyntaxKind.SuppressNullableWarningExpression]; /// /// Check if the has a value set, in that case try to get that value and return it, and return the usings. @@ -26,9 +26,9 @@ internal static (string DefaultValue, IReadOnlyList? ExtraUsings) GetDef if (propertyDeclarationSyntax?.Initializer != null && !ExcludedSyntaxKinds.Contains(propertyDeclarationSyntax.Initializer.Value.Kind())) { - var thisUsings = rootSyntaxNode.FindDescendantNodes().Select(ud => ud.Name.ToString()); + var thisUsings = rootSyntaxNode.FindDescendantNodes().Select(ud => ud.Name?.ToString()).OfType(); - var ancestorUsings = rootSyntaxNode.GetAncestorsUsings().Select(ud => ud.Name.ToString()); + var ancestorUsings = rootSyntaxNode.GetAncestorsUsings().Select(ud => ud.Name?.ToString()).OfType(); var extraUsings = thisUsings.Union(ancestorUsings).Distinct().ToList(); @@ -108,7 +108,7 @@ private static string GetNewConstructor(ITypeSymbol typeSymbol) var body = namedTypeSymbol.DelegateInvokeMethod.ReturnsVoid ? "{ }" // It's an Action - : GetDefault(namedTypeSymbol.DelegateInvokeMethod.ReturnType); // It's an Func + : GetDefault(namedTypeSymbol.DelegateInvokeMethod.ReturnType); // It's a Func return $"new {typeSymbol}(({string.Join(", ", delegateParameters)}) => {body})"; } diff --git a/src/FluentBuilderGenerator/Interfaces/IPropertyOrParameterSymbol.cs b/src/FluentBuilderGenerator/Interfaces/IPropertyOrParameterSymbol.cs index 62a7dea..19f2902 100644 --- a/src/FluentBuilderGenerator/Interfaces/IPropertyOrParameterSymbol.cs +++ b/src/FluentBuilderGenerator/Interfaces/IPropertyOrParameterSymbol.cs @@ -14,11 +14,4 @@ internal interface IPropertyOrParameterSymbol ITypeSymbol Type { get; } bool ExcludeFromIsSetLogic { get; } -} - -internal record PropertyOrParameterSymbol(ISymbol Symbol, ITypeSymbol Type, bool ExcludeFromIsSetLogic) : IPropertyOrParameterSymbol -{ - public PropertyType PropertyType => Symbol is IPropertySymbol ? PropertyType.Property : PropertyType.Parameter; - - public string Name => Symbol.Name; } \ No newline at end of file diff --git a/src/FluentBuilderGenerator/Models/LanguageData.cs b/src/FluentBuilderGenerator/Models/LanguageData.cs new file mode 100644 index 0000000..9733bf1 --- /dev/null +++ b/src/FluentBuilderGenerator/Models/LanguageData.cs @@ -0,0 +1,7 @@ +namespace FluentBuilderGenerator.Models; + +internal struct LanguageData +{ + public bool SupportsNullable { get; set; } + public bool SupportsGenericAttributes { get; set; } +} \ No newline at end of file diff --git a/src/FluentBuilderGenerator/Models/PropertyOrParameterSymbol.cs b/src/FluentBuilderGenerator/Models/PropertyOrParameterSymbol.cs new file mode 100644 index 0000000..17f1f93 --- /dev/null +++ b/src/FluentBuilderGenerator/Models/PropertyOrParameterSymbol.cs @@ -0,0 +1,12 @@ +using FluentBuilderGenerator.Interfaces; +using FluentBuilderGenerator.Types; +using Microsoft.CodeAnalysis; + +namespace FluentBuilderGenerator.Models; + +internal record PropertyOrParameterSymbol(ISymbol Symbol, ITypeSymbol Type, bool ExcludeFromIsSetLogic) : IPropertyOrParameterSymbol +{ + public PropertyType PropertyType => Symbol is IPropertySymbol ? PropertyType.Property : PropertyType.Parameter; + + public string Name => Symbol.Name; +} \ No newline at end of file diff --git a/src/FluentBuilderGenerator/SyntaxReceiver/AttributeArgumentListParser.cs b/src/FluentBuilderGenerator/SyntaxReceiver/AttributeArgumentListParser.cs index c3c6ca7..17b5e3a 100644 --- a/src/FluentBuilderGenerator/SyntaxReceiver/AttributeArgumentListParser.cs +++ b/src/FluentBuilderGenerator/SyntaxReceiver/AttributeArgumentListParser.cs @@ -76,14 +76,13 @@ public static FluentBuilderAttributeArguments Parse(AttributeSyntax? attributeSy private static bool TryParseAsBoolean(ExpressionSyntax expressionSyntax, out bool value) { - value = default; - if (expressionSyntax is LiteralExpressionSyntax literalExpressionSyntax) { value = literalExpressionSyntax.Kind() == SyntaxKind.TrueLiteralExpression; return true; } + value = false; return false; } diff --git a/src/FluentBuilderGenerator/SyntaxReceiver/AutoGenerateBuilderSyntaxReceiver.cs b/src/FluentBuilderGenerator/SyntaxReceiver/AutoGenerateBuilderSyntaxReceiver.cs index 4ec415c..2f686d2 100644 --- a/src/FluentBuilderGenerator/SyntaxReceiver/AutoGenerateBuilderSyntaxReceiver.cs +++ b/src/FluentBuilderGenerator/SyntaxReceiver/AutoGenerateBuilderSyntaxReceiver.cs @@ -1,6 +1,7 @@ // This source code is based on https://justsimplycode.com/2020/12/06/auto-generate-builders-using-source-generator-in-net-5 using System.Diagnostics.CodeAnalysis; +using FluentBuilderGenerator.Constants; using FluentBuilderGenerator.Extensions; using FluentBuilderGenerator.Models; using FluentBuilderGenerator.Types; @@ -9,64 +10,85 @@ namespace FluentBuilderGenerator.SyntaxReceiver; -internal class AutoGenerateBuilderSyntaxReceiver : IAutoGenerateBuilderSyntaxReceiver +internal static class AutoGenerateBuilderSyntaxReceiver { private const string ModifierPartial = "partial"; private const string ModifierPublic = "public"; private const string ModifierInternal = "internal"; - public IList CandidateFluentDataItems { get; } = new List(); - - public void OnVisitSyntaxNode(GeneratorSyntaxContext context) + public static bool CheckSyntaxNode(SyntaxNode syntaxNode, out Diagnostic? diagnostic) { - var syntaxNode = context.Node; - var semanticModel = context.SemanticModel; - - switch (syntaxNode) + TypeDeclarationSyntax typeDeclarationSyntax; + if (syntaxNode is ClassDeclarationSyntax classDeclarationSyntax) { - case ClassDeclarationSyntax classDeclarationSyntax when TryGet(classDeclarationSyntax, semanticModel, out var classData): - CandidateFluentDataItems.Add(classData); - break; - - case RecordDeclarationSyntax recordDeclarationSyntax when TryGet(recordDeclarationSyntax, semanticModel, out var recordData): - CandidateFluentDataItems.Add(recordData); - break; + typeDeclarationSyntax = classDeclarationSyntax; + } + else if (syntaxNode is RecordDeclarationSyntax recordDeclarationSyntax) + { + typeDeclarationSyntax = recordDeclarationSyntax; + } + else + { + diagnostic = null; + return false; } - } - - private static bool TryGet(TypeDeclarationSyntax classDeclarationSyntax, SemanticModel semanticModel, out FluentData data) - { - data = default; - var attributeList = classDeclarationSyntax.AttributeLists + var attributeList = typeDeclarationSyntax.AttributeLists .FirstOrDefault(x => x.Attributes.Any(AttributeArgumentListParser.IsMatch)); if (attributeList is null) { - Console.WriteLine("ClassDeclarationSyntax should have the correct attribute."); + diagnostic = null; return false; } - if (!TryGetClassModifier(classDeclarationSyntax, out var classModifier)) + if (!TryGetModifier(typeDeclarationSyntax, out _)) + { + diagnostic = Diagnostic.Create(DiagnosticDescriptors.ClassOrRecordModifierShouldBeInternalOrPublic, typeDeclarationSyntax.GetLocation()); + return false; + } + + diagnostic = null; + return true; + } + + public static FluentData HandleSyntaxNode(SyntaxNode syntaxNode, SemanticModel semanticModel, out Diagnostic? diagnostic) + { + return syntaxNode switch + { + ClassDeclarationSyntax classDeclarationSyntax when TryGet(classDeclarationSyntax, semanticModel, out var data, out diagnostic) => data, + RecordDeclarationSyntax recordDeclarationSyntax when TryGet(recordDeclarationSyntax, semanticModel, out var data, out diagnostic) => data, + _ => throw new InvalidOperationException("Only classes or records are supported."), + }; + } + + private static bool TryGet(TypeDeclarationSyntax typeDeclarationSyntax, SemanticModel semanticModel, out FluentData data, out Diagnostic? diagnostic) + { + data = default; + + var attributeList = typeDeclarationSyntax.AttributeLists + .FirstOrDefault(x => x.Attributes.Any(AttributeArgumentListParser.IsMatch))!; + + if (!TryGetModifier(typeDeclarationSyntax, out var modifier)) { - Console.WriteLine("Class modifier should be 'public' or 'internal'."); + diagnostic = Diagnostic.Create(DiagnosticDescriptors.ClassOrRecordModifierShouldBeInternalOrPublic, typeDeclarationSyntax.GetLocation()); return false; } var usings = new List(); - var ns = classDeclarationSyntax.GetNamespace(); + var ns = typeDeclarationSyntax.GetNamespace(); if (!string.IsNullOrEmpty(ns)) { usings.Add(ns); } - if (classDeclarationSyntax.TryGetParentSyntax(out CompilationUnitSyntax? cc)) + if (typeDeclarationSyntax.TryGetParentSyntax(out CompilationUnitSyntax? cc)) { usings.AddRange(cc.Usings.Select(@using => @using.Name!.ToString())); } // https://github.com/StefH/FluentBuilder/issues/36 - usings.AddRange(classDeclarationSyntax.GetAncestorsUsings().Select(@using => @using.Name!.ToString())); + usings.AddRange(typeDeclarationSyntax.GetAncestorsUsings().Select(@using => @using.Name!.ToString())); usings = usings.Distinct().ToList(); @@ -74,18 +96,18 @@ private static bool TryGet(TypeDeclarationSyntax classDeclarationSyntax, Semanti if (fluentBuilderAttributeArguments.RawTypeName != null) // The class which needs to be processed by the CustomBuilder is provided as type { - if (!AreBuilderClassModifiersValid(classDeclarationSyntax)) + if (!AreBuilderClassModifiersValid(typeDeclarationSyntax)) { - Console.WriteLine("Custom builder class should be 'partial' and 'public' or 'internal'."); + diagnostic = Diagnostic.Create(DiagnosticDescriptors.CustomBuilderClassModifierShouldBePartialAndInternalOrPublic, typeDeclarationSyntax.GetLocation()); return false; } data = new FluentData { Namespace = ns, - ClassModifier = classModifier, - ShortBuilderClassName = $"{classDeclarationSyntax.Identifier}", - FullBuilderClassName = CreateFullBuilderClassName(ns, classDeclarationSyntax), + ClassModifier = modifier, + ShortBuilderClassName = $"{typeDeclarationSyntax.Identifier}", + FullBuilderClassName = CreateFullBuilderClassName(ns, typeDeclarationSyntax), FullRawTypeName = fluentBuilderAttributeArguments.RawTypeName, ShortTypeName = ConvertTypeName(fluentBuilderAttributeArguments.RawTypeName).Split('.').Last(), MetadataName = ConvertTypeName(fluentBuilderAttributeArguments.RawTypeName), @@ -96,17 +118,18 @@ private static bool TryGet(TypeDeclarationSyntax classDeclarationSyntax, Semanti Methods = fluentBuilderAttributeArguments.Methods }; + diagnostic = null; return true; } - var fullType = GetFullType(ns, classDeclarationSyntax, false); // FluentBuilderGeneratorTests.DTO.UserT - var fullBuilderType = GetFullType(ns, classDeclarationSyntax, true); - var metadataName = classDeclarationSyntax.GetMetadataName(); + var fullType = GetFullType(ns, typeDeclarationSyntax, false); // FluentBuilderGeneratorTests.DTO.UserT + var fullBuilderType = GetFullType(ns, typeDeclarationSyntax, true); + var metadataName = typeDeclarationSyntax.GetMetadataName(); data = new FluentData { Namespace = ns, - ClassModifier = classModifier, + ClassModifier = modifier, ShortBuilderClassName = $"{fullBuilderType.Split('.').Last()}", FullBuilderClassName = fullBuilderType, FullRawTypeName = fullType, @@ -119,6 +142,7 @@ private static bool TryGet(TypeDeclarationSyntax classDeclarationSyntax, Semanti Methods = fluentBuilderAttributeArguments.Methods }; + diagnostic = null; return true; } @@ -128,7 +152,7 @@ private static bool AreBuilderClassModifiersValid(MemberDeclarationSyntax classD return modifiers.Contains(ModifierPartial) && (modifiers.Contains(ModifierPublic) || modifiers.Contains(ModifierInternal)); } - private static bool TryGetClassModifier(MemberDeclarationSyntax classDeclarationSyntax, [NotNullWhen(true)] out string? modifier) + private static bool TryGetModifier(MemberDeclarationSyntax classDeclarationSyntax, [NotNullWhen(true)] out string? modifier) { var modifiers = classDeclarationSyntax.Modifiers.Select(m => m.ToString()).Distinct().ToArray(); if (modifiers.Contains(ModifierPublic)) @@ -143,7 +167,7 @@ private static bool TryGetClassModifier(MemberDeclarationSyntax classDeclaration return true; } - modifier = default; + modifier = null; return false; } diff --git a/src/FluentBuilderGenerator/SyntaxReceiver/IAutoGenerateBuilderSyntaxReceiver.cs b/src/FluentBuilderGenerator/SyntaxReceiver/IAutoGenerateBuilderSyntaxReceiver.cs deleted file mode 100644 index 732493b..0000000 --- a/src/FluentBuilderGenerator/SyntaxReceiver/IAutoGenerateBuilderSyntaxReceiver.cs +++ /dev/null @@ -1,9 +0,0 @@ -using FluentBuilderGenerator.Models; -using Microsoft.CodeAnalysis; - -namespace FluentBuilderGenerator.SyntaxReceiver; - -internal interface IAutoGenerateBuilderSyntaxReceiver : ISyntaxContextReceiver -{ - public IList CandidateFluentDataItems { get; } -} \ No newline at end of file diff --git a/src/FluentBuilderGenerator/Wrappers/GeneratorExecutionContextWrapper.cs b/src/FluentBuilderGenerator/Wrappers/CompilationHelper.cs similarity index 52% rename from src/FluentBuilderGenerator/Wrappers/GeneratorExecutionContextWrapper.cs rename to src/FluentBuilderGenerator/Wrappers/CompilationHelper.cs index b3a93ee..29f80b4 100644 --- a/src/FluentBuilderGenerator/Wrappers/GeneratorExecutionContextWrapper.cs +++ b/src/FluentBuilderGenerator/Wrappers/CompilationHelper.cs @@ -1,79 +1,52 @@ -using System.Diagnostics.CodeAnalysis; -using FluentBuilderGenerator.Models; -using FluentBuilderGenerator.Types; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Text; - -namespace FluentBuilderGenerator.Wrappers; - -internal class GeneratorExecutionContextWrapper : IGeneratorExecutionContextWrapper -{ - private readonly GeneratorExecutionContext _context; - - public GeneratorExecutionContextWrapper(GeneratorExecutionContext context) - { - _context = context; - - if (context.ParseOptions is not CSharpParseOptions csharpParseOptions) - { - throw new NotSupportedException("Only C# is supported."); - } - - // https://github.com/reactiveui/refit/blob/main/InterfaceStubGenerator.Core/InterfaceStubGenerator.cs - SupportsNullable = csharpParseOptions.LanguageVersion >= LanguageVersion.CSharp8; - SupportsGenericAttributes = csharpParseOptions.LanguageVersion >= LanguageVersion.CSharp11; - NullableEnabled = context.Compilation.Options.NullableContextOptions == NullableContextOptions.Enable; - } - - public string AssemblyName => _context.Compilation.AssemblyName ?? "FluentBuilder"; - - public bool SupportsNullable { get; } - - public bool NullableEnabled { get; } - - public bool SupportsGenericAttributes { get; } - - public void AddSource(string hintName, SourceText sourceText) => _context.AddSource(hintName, sourceText); - - public bool TryGetNamedTypeSymbolByFullMetadataName(FluentData fluentDataItem, [NotNullWhen(true)] out ClassSymbol? classSymbol) - { - classSymbol = null; - - // The GetTypeByMetadataName method returns null if no type matches the full name or if 2 or more types (in different assemblies) match the full name. - var symbol = _context.Compilation.GetTypeByMetadataName(fluentDataItem.MetadataName); - if (symbol is not null) - { - classSymbol = new ClassSymbol - { - Type = FileDataType.Builder, - FluentData = fluentDataItem, - //BuilderNamespace = fluentDataItem.Namespace, - //BuilderClassName = fluentDataItem.ShortBuilderClassName, - //FullBuilderClassName = fluentDataItem.FullBuilderClassName, - NamedTypeSymbol = symbol - }; - return true; - } - - foreach (var @using in fluentDataItem.Usings) - { - symbol = _context.Compilation.GetTypeByMetadataName($"{@using}.{fluentDataItem.MetadataName}"); - if (symbol is not null) - { - classSymbol = new ClassSymbol - { - Type = FileDataType.Builder, - FluentData = fluentDataItem, - //BuilderNamespace = fluentDataItem.Namespace, - //BuilderClassName = fluentDataItem.ShortBuilderClassName, - //FullBuilderClassName = fluentDataItem.FullBuilderClassName, - NamedTypeSymbol = symbol - }; - return true; - } - } - - return false; - } +using System.Diagnostics.CodeAnalysis; +using FluentBuilderGenerator.Models; +using FluentBuilderGenerator.Types; +using Microsoft.CodeAnalysis; + +namespace FluentBuilderGenerator.Wrappers; + +internal class CompilationHelper(Compilation compilation) +{ + public string AssemblyName { get; } = compilation.AssemblyName ?? "FluentBuilder"; + + public bool TryGetNamedTypeSymbolByFullMetadataName(FluentData fluentDataItem, [NotNullWhen(true)] out ClassSymbol? classSymbol) + { + classSymbol = null; + + // The GetTypeByMetadataName method returns null if no type matches the full name or if 2 or more types (in different assemblies) match the full name. + var symbol = compilation.GetTypeByMetadataName(fluentDataItem.MetadataName); + if (symbol is not null) + { + classSymbol = new ClassSymbol + { + Type = FileDataType.Builder, + FluentData = fluentDataItem, + //BuilderNamespace = fluentDataItem.Namespace, + //BuilderClassName = fluentDataItem.ShortBuilderClassName, + //FullBuilderClassName = fluentDataItem.FullBuilderClassName, + NamedTypeSymbol = symbol + }; + return true; + } + + foreach (var @using in fluentDataItem.Usings) + { + symbol = compilation.GetTypeByMetadataName($"{@using}.{fluentDataItem.MetadataName}"); + if (symbol is not null) + { + classSymbol = new ClassSymbol + { + Type = FileDataType.Builder, + FluentData = fluentDataItem, + //BuilderNamespace = fluentDataItem.Namespace, + //BuilderClassName = fluentDataItem.ShortBuilderClassName, + //FullBuilderClassName = fluentDataItem.FullBuilderClassName, + NamedTypeSymbol = symbol + }; + return true; + } + } + + return false; + } } \ No newline at end of file diff --git a/src/FluentBuilderGenerator/Wrappers/IGeneratorExecutionContextWrapper.cs b/src/FluentBuilderGenerator/Wrappers/IGeneratorExecutionContextWrapper.cs deleted file mode 100644 index ddf82e7..0000000 --- a/src/FluentBuilderGenerator/Wrappers/IGeneratorExecutionContextWrapper.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using FluentBuilderGenerator.Models; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Text; - -namespace FluentBuilderGenerator.Wrappers; - -internal interface IGeneratorExecutionContextWrapper -{ - /// - public string AssemblyName { get; } - - public bool SupportsNullable { get; } - - public bool NullableEnabled { get; } - - public bool SupportsGenericAttributes { get; } - - /// - public void AddSource(string hintName, SourceText sourceText); - - bool TryGetNamedTypeSymbolByFullMetadataName(FluentData fluentDataItem, [NotNullWhen(true)] out ClassSymbol? classSymbol); -} \ No newline at end of file diff --git a/tests/FluentBuilderGeneratorTests/DTO/AbcTest.OtherNamespace.ClassOnOtherNamespaceBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/AbcTest.OtherNamespace.ClassOnOtherNamespaceBuilder.g.cs index 9aec061..b771389 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/AbcTest.OtherNamespace.ClassOnOtherNamespaceBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/AbcTest.OtherNamespace.ClassOnOtherNamespaceBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/AbcTest.OtherNamespace.ClassOnOtherNamespace_IListBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/AbcTest.OtherNamespace.ClassOnOtherNamespace_IListBuilder.g.cs index a555ce8..ef7aea3 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/AbcTest.OtherNamespace.ClassOnOtherNamespace_IListBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/AbcTest.OtherNamespace.ClassOnOtherNamespace_IListBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.AddressBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.AddressBuilder.g.cs index 4fdc483..f4830fe 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.AddressBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.AddressBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.Address_ArrayBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.Address_ArrayBuilder.g.cs index 0239cba..27a4646 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.Address_ArrayBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.Address_ArrayBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.Address_ICollectionBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.Address_ICollectionBuilder.g.cs index 89e254e..6a3cd65 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.Address_ICollectionBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.Address_ICollectionBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.Address_IEnumerableBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.Address_IEnumerableBuilder.g.cs index 98cb2c5..66b4b07 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.Address_IEnumerableBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.Address_IEnumerableBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.Address_IListBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.Address_IListBuilder.g.cs index ad072c4..08f5f32 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.Address_IListBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.Address_IListBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.Address_IReadOnlyCollectionBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.Address_IReadOnlyCollectionBuilder.g.cs index 786f164..0c07284 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.Address_IReadOnlyCollectionBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.Address_IReadOnlyCollectionBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.Address_IReadOnlyListBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.Address_IReadOnlyListBuilder.g.cs index ac05e71..38d1df8 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.Address_IReadOnlyListBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/BuilderGeneratorTests.DTO.Address_IReadOnlyListBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/ClassWithPrivateBuilderClass.cs b/tests/FluentBuilderGeneratorTests/DTO/ClassWithPrivateBuilderClass.cs new file mode 100644 index 0000000..e70afcc --- /dev/null +++ b/tests/FluentBuilderGeneratorTests/DTO/ClassWithPrivateBuilderClass.cs @@ -0,0 +1,12 @@ +using FluentBuilder; + +namespace FluentBuilderGeneratorTests.DTO; + +public class ClassWithPrivateBuilderClass +{ + [AutoGenerateBuilder(FluentBuilderAccessibility.PublicAndPrivate)] + private class PrivateClass + { + public int Test { get; set; } + } +} \ No newline at end of file diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.ArrayBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.ArrayBuilder.g.cs index 0948134..d9d3afd 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.ArrayBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.ArrayBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.BaseBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.BaseBuilder.g.cs index 6e3ee3c..dc5009b 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.BaseBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.BaseBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.Extra.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.Extra.g.cs index 5c06069..4c9ee5e 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.Extra.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.Extra.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -129,4 +129,4 @@ internal enum FluentBuilderMethods WithAndWithout = 1 } } -#nullable restore \ No newline at end of file +#nullable disable \ No newline at end of file diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.ICollectionBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.ICollectionBuilder.g.cs index 7c7e7d0..30fd5a9 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.ICollectionBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.ICollectionBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.IDictionaryBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.IDictionaryBuilder.g.cs index b2ef15c..f70deaa 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.IDictionaryBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.IDictionaryBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.IEnumerableBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.IEnumerableBuilder.g.cs index 46a1bce..b3adba9 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.IEnumerableBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.IEnumerableBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.IListBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.IListBuilder.g.cs index ff8b9cc..fef14f7 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.IListBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.IListBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.IReadOnlyCollectionBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.IReadOnlyCollectionBuilder.g.cs index 89dd673..b0097ea 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.IReadOnlyCollectionBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.IReadOnlyCollectionBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.IReadOnlyListBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.IReadOnlyListBuilder.g.cs index 686fff7..bb1717b 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.IReadOnlyListBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilder.IReadOnlyListBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.AddressBuilder_T_.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.AddressBuilder_T_.g.cs index cad11dc..f239bc9 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.AddressBuilder_T_.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.AddressBuilder_T_.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.AddressTTBuilder_T1_T2_.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.AddressTTBuilder_T1_T2_.g.cs index 1bf5b6b..1c447bc 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.AddressTTBuilder_T1_T2_.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.AddressTTBuilder_T1_T2_.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ClassWithFuncAndActionBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ClassWithFuncAndActionBuilder.g.cs index 77fc406..47b073d 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ClassWithFuncAndActionBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ClassWithFuncAndActionBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ClassWithInitPropertiesBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ClassWithInitPropertiesBuilder.g.cs index d0062c0..e35c965 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ClassWithInitPropertiesBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ClassWithInitPropertiesBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ClassWithPrimaryConstructorBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ClassWithPrimaryConstructorBuilder.g.cs index 1bd03f4..63b33a8 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ClassWithPrimaryConstructorBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ClassWithPrimaryConstructorBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ClassWithPrivateSetter1Builder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ClassWithPrivateSetter1Builder.g.cs index 9233fc2..7b66466 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ClassWithPrivateSetter1Builder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ClassWithPrivateSetter1Builder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ClassWithPrivateSetter2Builder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ClassWithPrivateSetter2Builder.g.cs index 454b978..7f6d049 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ClassWithPrivateSetter2Builder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ClassWithPrivateSetter2Builder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ClassWithPropertyValueSetBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ClassWithPropertyValueSetBuilder.g.cs index cf36e8a..fd87873 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ClassWithPropertyValueSetBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ClassWithPropertyValueSetBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.InternalClassBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.InternalClassBuilder.g.cs index 7c8158b..2635b25 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.InternalClassBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.InternalClassBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.MyDummyClassBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.MyDummyClassBuilder.g.cs index 0247e04..928adf6 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.MyDummyClassBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.MyDummyClassBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.MyInternalClassBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.MyInternalClassBuilder.g.cs index e777e50..d8d31d1 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.MyInternalClassBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.MyInternalClassBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.RecordWithPrimaryConstructorBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.RecordWithPrimaryConstructorBuilder.g.cs index 2ced2b2..17f3ac3 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.RecordWithPrimaryConstructorBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.RecordWithPrimaryConstructorBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.TestBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.TestBuilder.g.cs index 455ac51..ba78426 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.TestBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.TestBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ThingBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ThingBuilder.g.cs index 633710f..f7c7738 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ThingBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.ThingBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.UserBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.UserBuilder.g.cs index f9fc878..dfb8723 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.UserBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.UserBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.UserTBuilder_T_.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.UserTBuilder_T_.g.cs index 0949f30..a79e4ad 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.UserTBuilder_T_.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.UserTBuilder_T_.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.UserTWithAddressAndConstructorBuilder_T_.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.UserTWithAddressAndConstructorBuilder_T_.g.cs index 2d85ade..f65b4ee 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.UserTWithAddressAndConstructorBuilder_T_.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.UserTWithAddressAndConstructorBuilder_T_.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.UserTWithAddressTBuilder_T_.g.cs b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.UserTWithAddressTBuilder_T_.g.cs index 005dd74..4edc14b 100644 --- a/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.UserTWithAddressTBuilder_T_.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO/FluentBuilderGeneratorTests.DTO.UserTWithAddressTBuilder_T_.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO2/FluentBuilderGeneratorTests.DTO.Option_IListBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO2/FluentBuilderGeneratorTests.DTO.Option_IListBuilder.g.cs index d6acb27..845c4f0 100644 --- a/tests/FluentBuilderGeneratorTests/DTO2/FluentBuilderGeneratorTests.DTO.Option_IListBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO2/FluentBuilderGeneratorTests.DTO.Option_IListBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/DTO2/FluentBuilderGeneratorTests.DTO2.MyOptionBuilder.g.cs b/tests/FluentBuilderGeneratorTests/DTO2/FluentBuilderGeneratorTests.DTO2.MyOptionBuilder.g.cs index 676181f..d64e9ff 100644 --- a/tests/FluentBuilderGeneratorTests/DTO2/FluentBuilderGeneratorTests.DTO2.MyOptionBuilder.g.cs +++ b/tests/FluentBuilderGeneratorTests/DTO2/FluentBuilderGeneratorTests.DTO2.MyOptionBuilder.g.cs @@ -1,6 +1,6 @@ //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/FileGenerator/FluentBuilderClassesGeneratorTests.cs b/tests/FluentBuilderGeneratorTests/FileGenerator/FluentBuilderClassesGeneratorTests.cs deleted file mode 100644 index 3905d11..0000000 --- a/tests/FluentBuilderGeneratorTests/FileGenerator/FluentBuilderClassesGeneratorTests.cs +++ /dev/null @@ -1,161 +0,0 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using System.IO; -using System.Linq; -using AwesomeAssertions; -using FluentBuilderGenerator.FileGenerators; -using FluentBuilderGenerator.Models; -using FluentBuilderGenerator.SyntaxReceiver; -using FluentBuilderGenerator.Types; -using FluentBuilderGenerator.Wrappers; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Moq; -using Xunit; - -namespace FluentBuilderGeneratorTests.FileGenerator; - -public class FluentBuilderClassesGeneratorTests -{ - private const bool Write = true; - - private readonly Mock _contextMock; - private readonly Mock _syntaxReceiverMock; - - private readonly FluentBuilderClassesGenerator _sut; - - public FluentBuilderClassesGeneratorTests() - { - _contextMock = new Mock(); - _contextMock.SetupGet(c => c.AssemblyName).Returns("FluentBuilderGeneratorTests"); - _contextMock.SetupGet(c => c.SupportsNullable).Returns(true); - _contextMock.SetupGet(c => c.NullableEnabled).Returns(true); - - _syntaxReceiverMock = new Mock(); - - _sut = new FluentBuilderClassesGenerator(_contextMock.Object, _syntaxReceiverMock.Object); - } - - [Fact] - public void GenerateFiles_WhenNoClassesFoundByReceiver_Should_NotGenerateFiles() - { - // Arrange - _syntaxReceiverMock.SetupGet(r => r.CandidateFluentDataItems).Returns(new List()); - - // Act - var result = _sut.GenerateFiles(); - - // Assert - result.Should().BeEmpty(); - - // Verify - _syntaxReceiverMock.Verify(r => r.CandidateFluentDataItems, Times.Once()); - _syntaxReceiverMock.VerifyNoOtherCalls(); - - _contextMock.VerifyNoOtherCalls(); - } - - [Fact(Skip = "needs fix")] - public void GenerateFiles_WhenOneClassIsFoundByReceiver_Should_GenerateOneFile() - { - // Arrange : SyntaxReceiverMock - var syntaxTree = CSharpSyntaxTree.ParseText(File.ReadAllText("./DTO/User.cs")); - var root = syntaxTree.GetRoot(); - var @namespace = root.ChildNodes().OfType().Single(); - var @class = @namespace.ChildNodes().OfType().Single(); - var properties = @class.ChildNodes().OfType().ToList(); - - var fluentData = new FluentData - { - BuilderType = BuilderType.Generated, - Namespace = "FluentBuilderGeneratorTests.DTO", - ShortBuilderClassName = "UserBuilder", - FullBuilderClassName = "FluentBuilderGeneratorTests.DTO.UserBuilder", - FullRawTypeName = "FluentBuilderGeneratorTests.DTO.User", - ShortTypeName = "User", - MetadataName = "FluentBuilderGeneratorTests.DTO.User", - Usings = new List() - }; - _syntaxReceiverMock.SetupGet(r => r.CandidateFluentDataItems).Returns(new[] { fluentData }); - - // Arrange : ContextMock - var namespaceSymbolMock = new Mock(); - namespaceSymbolMock.Setup(n => n.ToString()).Returns(@namespace.Name.ToString()); - - var namedTypeSymbolMock = new Mock(); - namedTypeSymbolMock.SetupGet(n => n.Name).Returns("User"); - namedTypeSymbolMock.SetupGet(n => n.TypeArguments).Returns(ImmutableArray.Create()); - - var membersMock = properties.Select(p => - { - var setMethodMock = new Mock(); - - var typeSymbol = new Mock(); - typeSymbol.SetupGet(t => t.Name).Returns(p.Type.ToString()); - typeSymbol.Setup(t => t.ToString()).Returns(p.Type.ToString()); - typeSymbol.SetupGet(ts => ts.AllInterfaces).Returns(ImmutableArray.Create()); - - var propertySymbolMock = new Mock(); - propertySymbolMock.SetupGet(pt => pt.CanBeReferencedByName).Returns(true); - propertySymbolMock.SetupGet(pt => pt.Name).Returns(p.Identifier.ValueText); - propertySymbolMock.SetupGet(pt => pt.SetMethod).Returns(setMethodMock.Object); - propertySymbolMock.SetupGet(pt => pt.Type).Returns(typeSymbol.Object); - - return propertySymbolMock; - }) - .Select(p => (ISymbol)p.Object) - .ToImmutableArray(); - - var originalDefinitionMock = new Mock(); - originalDefinitionMock.Setup(o => o.ToString()).Returns("FluentBuilderGeneratorTests.DTO.User"); - - var classSymbolMock = new Mock(); - classSymbolMock.SetupGet(n => n.ContainingNamespace).Returns(namespaceSymbolMock.Object); - classSymbolMock.SetupGet(n => n.Name).Returns("User"); - classSymbolMock.SetupGet(n => n.OriginalDefinition).Returns(originalDefinitionMock.Object); - classSymbolMock.Setup(n => n.GetMembers()).Returns(membersMock); - classSymbolMock.SetupGet(n => n.TypeArguments).Returns(ImmutableArray.Create()); - - var classSymbolConstructorMock = new Mock(); - classSymbolConstructorMock.SetupGet(c => c.DeclaredAccessibility).Returns(Accessibility.Public); - classSymbolConstructorMock.SetupGet(c => c.Parameters).Returns(ImmutableArray.Empty); - classSymbolMock.SetupGet(n => n.Constructors).Returns(ImmutableArray.Create(new[] { classSymbolConstructorMock.Object })); - - var classSymbol = new ClassSymbol - { - Type = FileDataType.Builder, - FluentData = fluentData, - //BuilderNamespace = fluentData.Namespace, - //BuilderClassName = fluentData.ShortBuilderClassName, - //FullBuilderClassName = fluentData.FullBuilderClassName, - NamedTypeSymbol = classSymbolMock.Object - }; - _contextMock.Setup(c => c.TryGetNamedTypeSymbolByFullMetadataName(It.IsAny(), out classSymbol)).Returns(true); - - // Act - var result = _sut.GenerateFiles().ToList(); - - // Assert - result.Should().HaveCount(1); - result[0].FileName.Should().Be("FluentBuilderGeneratorTests.DTO.UserBuilder.g.cs"); - - var generated = result[0].Text; - generated.Should().NotBeEmpty(); - - if (Write) File.WriteAllText("../../../DTO/FluentBuilderGeneratorTests.DTO.UserBuilder.g.cs", generated); - - var generatedCode = CSharpSyntaxTree.ParseText(generated); - var expectedCode = CSharpSyntaxTree.ParseText(File.ReadAllText("../../../DTO/FluentBuilderGeneratorTests.DTO.UserBuilder.g.cs")); - generatedCode.Should().BeEquivalentTo(expectedCode); - - // Verify - _syntaxReceiverMock.Verify(r => r.CandidateFluentDataItems, Times.Once()); - _syntaxReceiverMock.VerifyNoOtherCalls(); - - _contextMock.Verify(c => c.TryGetNamedTypeSymbolByFullMetadataName(It.IsAny(), out It.Ref.IsAny), Times.Once()); - _contextMock.Verify(c => c.AssemblyName, Times.Once()); - _contextMock.Verify(c => c.SupportsNullable, Times.AtLeast(1)); - _contextMock.VerifyNoOtherCalls(); - } -} \ No newline at end of file diff --git a/tests/FluentBuilderGeneratorTests/FluentBuilderSourceGeneratorTests.GenerateFiles_ClassWithFluentBuilderIgnore_Should_GenerateCorrectFiles.verified.txt b/tests/FluentBuilderGeneratorTests/FluentBuilderSourceGeneratorTests.GenerateFiles_ClassWithFluentBuilderIgnore_Should_GenerateCorrectFiles.verified.txt index 9c83a6c..8fa5c48 100644 --- a/tests/FluentBuilderGeneratorTests/FluentBuilderSourceGeneratorTests.GenerateFiles_ClassWithFluentBuilderIgnore_Should_GenerateCorrectFiles.verified.txt +++ b/tests/FluentBuilderGeneratorTests/FluentBuilderSourceGeneratorTests.GenerateFiles_ClassWithFluentBuilderIgnore_Should_GenerateCorrectFiles.verified.txt @@ -3,7 +3,7 @@ Source: //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/FluentBuilderSourceGeneratorTests.GenerateFiles_ForAClassWithoutAPublicConstructor_Should_Create_ErrorFile.verified.txt b/tests/FluentBuilderGeneratorTests/FluentBuilderSourceGeneratorTests.GenerateFiles_ForAClassWithoutAPublicConstructor_Should_Create_ErrorFile.verified.txt deleted file mode 100644 index 23aeec9..0000000 --- a/tests/FluentBuilderGeneratorTests/FluentBuilderSourceGeneratorTests.GenerateFiles_ForAClassWithoutAPublicConstructor_Should_Create_ErrorFile.verified.txt +++ /dev/null @@ -1,29 +0,0 @@ -{ - HintName: Error.g.cs, - Source: -/* -FluentBuilderSourceGenerator - -[Exception] -System.NotSupportedException: Unable to generate a FluentBuilder for the class 'System.AppDomain' because no public constructor is defined. - at FluentBuilderGenerator.FileGenerators.FluentBuilderClassesGenerator.CreateClassBuilderCode(FluentData fluentData, ClassSymbol classSymbol, List`1 allClassSymbols) in {SolutionDirectory}src\FluentBuilderGenerator\FileGenerators\FluentBuilderClassesGenerator.cs:line 85 - at FluentBuilderGenerator.FileGenerators.FluentBuilderClassesGenerator.<>c__DisplayClass6_0.b__1(ValueTuple`2 classSymbol) in {SolutionDirectory}src\FluentBuilderGenerator\FileGenerators\FluentBuilderClassesGenerator.cs:line 56 - at System.Linq.Enumerable.SelectListIterator`2.MoveNext() - at System.Collections.Generic.HashSet`1.UnionWith(IEnumerable`1 other) - at System.Linq.Enumerable.UnionIterator`1.FillSet() - at System.Linq.Enumerable.UnionIterator`1.ToList() - at FluentBuilderGenerator.FileGenerators.FluentBuilderClassesGenerator.GenerateFiles() in {SolutionDirectory}src\FluentBuilderGenerator\FileGenerators\FluentBuilderClassesGenerator.cs:line 74 - at FluentBuilderGenerator.FluentBuilderSourceGenerator.InjectFluentBuilderClasses(IGeneratorExecutionContextWrapper context, IAutoGenerateBuilderSyntaxReceiver receiver) in {SolutionDirectory}src\FluentBuilderGenerator\FluentBuilderSourceGenerator.cs:line 83 - at FluentBuilderGenerator.FluentBuilderSourceGenerator.Execute(GeneratorExecutionContext context) in {SolutionDirectory}src\FluentBuilderGenerator\FluentBuilderSourceGenerator.cs:line 40 - -[StackTrace] - at FluentBuilderGenerator.FileGenerators.FluentBuilderClassesGenerator.CreateClassBuilderCode(FluentData fluentData, ClassSymbol classSymbol, List`1 allClassSymbols) in {SolutionDirectory}src\FluentBuilderGenerator\FileGenerators\FluentBuilderClassesGenerator.cs:line 85 - at FluentBuilderGenerator.FileGenerators.FluentBuilderClassesGenerator.<>c__DisplayClass6_0.b__1(ValueTuple`2 classSymbol) in {SolutionDirectory}src\FluentBuilderGenerator\FileGenerators\FluentBuilderClassesGenerator.cs:line 56 - at System.Linq.Enumerable.SelectListIterator`2.MoveNext() - at System.Collections.Generic.HashSet`1.UnionWith(IEnumerable`1 other) - at System.Linq.Enumerable.UnionIterator`1.FillSet() - at System.Linq.Enumerable.UnionIterator`1.ToList() - at FluentBuilderGenerator.FileGenerators.FluentBuilderClassesGenerator.GenerateFiles() in {SolutionDirectory}src\FluentBuilderGenerator\FileGenerators\FluentBuilderClassesGenerator.cs:line 74 - at FluentBuilderGenerator.FluentBuilderSourceGenerator.InjectFluentBuilderClasses(IGeneratorExecutionContextWrapper context, IAutoGenerateBuilderSyntaxReceiver receiver) in {SolutionDirectory}src\FluentBuilderGenerator\FluentBuilderSourceGenerator.cs:line 83 - at FluentBuilderGenerator.FluentBuilderSourceGenerator.Execute(GeneratorExecutionContext context) in {SolutionDirectory}src\FluentBuilderGenerator\FluentBuilderSourceGenerator.cs:line 40*/ -} \ No newline at end of file diff --git a/tests/FluentBuilderGeneratorTests/FluentBuilderSourceGeneratorTests.GenerateFiles_ForSimpleClass_MethodsIsWithAndWithout_Should_GenerateCorrectFiles.verified.txt b/tests/FluentBuilderGeneratorTests/FluentBuilderSourceGeneratorTests.GenerateFiles_ForSimpleClass_MethodsIsWithAndWithout_Should_GenerateCorrectFiles.verified.txt index 9c83a6c..8fa5c48 100644 --- a/tests/FluentBuilderGeneratorTests/FluentBuilderSourceGeneratorTests.GenerateFiles_ForSimpleClass_MethodsIsWithAndWithout_Should_GenerateCorrectFiles.verified.txt +++ b/tests/FluentBuilderGeneratorTests/FluentBuilderSourceGeneratorTests.GenerateFiles_ForSimpleClass_MethodsIsWithAndWithout_Should_GenerateCorrectFiles.verified.txt @@ -3,7 +3,7 @@ Source: //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/FluentBuilderSourceGeneratorTests.GenerateFiles_ForSimpleClass_MethodsIsWithOnly_Should_GenerateCorrectFiles.verified.txt b/tests/FluentBuilderGeneratorTests/FluentBuilderSourceGeneratorTests.GenerateFiles_ForSimpleClass_MethodsIsWithOnly_Should_GenerateCorrectFiles.verified.txt index 9c83a6c..8fa5c48 100644 --- a/tests/FluentBuilderGeneratorTests/FluentBuilderSourceGeneratorTests.GenerateFiles_ForSimpleClass_MethodsIsWithOnly_Should_GenerateCorrectFiles.verified.txt +++ b/tests/FluentBuilderGeneratorTests/FluentBuilderSourceGeneratorTests.GenerateFiles_ForSimpleClass_MethodsIsWithOnly_Should_GenerateCorrectFiles.verified.txt @@ -3,7 +3,7 @@ Source: //------------------------------------------------------------------------------ // -// This code was generated by https://github.com/StefH/FluentBuilder version 0.11.0.0 +// This code was generated by https://github.com/StefH/FluentBuilder version 0.12.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/tests/FluentBuilderGeneratorTests/FluentBuilderSourceGeneratorTests.cs b/tests/FluentBuilderGeneratorTests/FluentBuilderSourceGeneratorTests.cs index 95c5207..39af48e 100644 --- a/tests/FluentBuilderGeneratorTests/FluentBuilderSourceGeneratorTests.cs +++ b/tests/FluentBuilderGeneratorTests/FluentBuilderSourceGeneratorTests.cs @@ -7,6 +7,7 @@ using CSharp.SourceGenerators.Extensions.Models; using FluentBuilderGenerator; using FluentBuilderGeneratorTests.DTO; +using Microsoft.CodeAnalysis; using VerifyTests; using VerifyXunit; using Xunit; @@ -32,7 +33,27 @@ public FluentBuilderSourceGeneratorTests() public static void ModuleInitializer() => VerifySourceGenerators.Enable(); [Fact] - public Task GenerateFiles_ForAClassWithoutAPublicConstructor_Should_Create_ErrorFile() + public void GenerateFiles_ForClassWithPrivateBuilderClass_ShouldReturnDiagnostics() + { + // Arrange + var path = "./DTO/ClassWithPrivateBuilderClass.cs"; + var sourceFile = new SourceFile + { + Path = path, + Text = File.ReadAllText(path) + }; + IIncrementalGenerator sut = new FluentBuilderSourceGenerator(); + + // Act + var result = sut.Execute(Namespace, [sourceFile]); + + // Assert + result.InformationMessages.Should().HaveCount(1); + result.InformationMessages[0].Should().Be("Class or Record modifier should be 'public' or 'internal'"); + } + + [Fact] + public void GenerateFiles_ForAClassWithoutAPublicConstructor_Should_Create_ErrorFile() { // Arrange var path = "./DTO2/MyAppDomainBuilder.cs"; @@ -48,15 +69,12 @@ public Task GenerateFiles_ForAClassWithoutAPublicConstructor_Should_Create_Error }; // Act - var result = _sut.Execute(Namespace, new[] { sourceFile }); + var result = _sut.Execute(Namespace, [sourceFile]); // Assert result.Files.Should().HaveCount(NumFiles); result.Files[NumFiles - 1].Path.Should().EndWith("Error.g.cs"); - - // Verify - var errorResult = result.GeneratorDriver.GetRunResult().Results.First().GeneratedSources.First(s => s.HintName.Contains("Error.g.cs")); - return Verifier.Verify(errorResult); + result.Files[NumFiles - 1].Text.Should().NotBeEmpty(); } [Fact] @@ -72,7 +90,7 @@ public void GenerateFiles_ForAClassWithPublicParameterlessConstructors_Should_Ge }; // Act - var result = _sut.Execute(Namespace, new[] { sourceFile }); + var result = _sut.Execute(Namespace, [sourceFile]); // Assert result.Valid.Should().BeTrue(); @@ -103,7 +121,7 @@ public void GenerateFiles_ForAClassWithOnlyParameterizedConstructors_Should_Gene }; // Act - var result = _sut.Execute(Namespace, new[] { sourceFile }); + var result = _sut.Execute(Namespace, [sourceFile]); // Assert result.Valid.Should().BeTrue(); @@ -178,7 +196,7 @@ public void GenerateFiles_For2Classes_Should_GenerateCorrectFiles() }; // Act - var result = _sut.Execute(Namespace, new[] { sourceFileUser, sourceFileBuilder }); + var result = _sut.Execute(Namespace, [sourceFileUser, sourceFileBuilder]); // Assert result.Valid.Should().BeTrue(); @@ -281,7 +299,7 @@ public void GenerateFiles_ClassWithPrivateSetter_And_AccessibilityPublicAndPriva }; // Act - var result = _sut.Execute(Namespace, new[] { sourceFile }); + var result = _sut.Execute(Namespace, [sourceFile]); // Assert result.Valid.Should().BeTrue(); @@ -318,7 +336,7 @@ public void GenerateFiles_ClassWithPrivateSetter_And_AccessibilityPublic_Should_ }; // Act - var result = _sut.Execute(Namespace, new[] { sourceFile }); + var result = _sut.Execute(Namespace, [sourceFile]); // Assert result.Valid.Should().BeTrue(); @@ -352,7 +370,7 @@ public Task GenerateFiles_ClassWithFluentBuilderIgnore_Should_GenerateCorrectFil }; // Act - var result = _sut.Execute(Namespace, new[] { sourceFile }); + var result = _sut.Execute(Namespace, [sourceFile]); // Assert result.Valid.Should().BeTrue(); @@ -376,7 +394,7 @@ public Task GenerateFiles_ForSimpleClass_MethodsIsWithOnly_Should_GenerateCorrec }; // Act - var result = _sut.Execute(Namespace, new[] { sourceFile }); + var result = _sut.Execute(Namespace, [sourceFile]); // Assert result.Valid.Should().BeTrue(); @@ -404,7 +422,7 @@ public Task GenerateFiles_ForSimpleClass_MethodsIsWithAndWithout_Should_Generate }; // Act - var result = _sut.Execute(Namespace, new[] { sourceFile }); + var result = _sut.Execute(Namespace, [sourceFile]); // Assert result.Valid.Should().BeTrue(); @@ -452,7 +470,7 @@ public void GenerateFiles_ForClassWithArrayAndDictionaryProperty_Should_Generate }; // Act - var result = _sut.Execute(Namespace, new[] { sourceFile }); + var result = _sut.Execute(Namespace, [sourceFile]); // Assert result.Valid.Should().BeTrue(); @@ -484,7 +502,7 @@ public void GenerateFiles_For1GenericClass_Should_GenerateCorrectFiles() }; // Act - var result = _sut.Execute(Namespace, new[] { sourceFile }); + var result = _sut.Execute(Namespace, [sourceFile]); // Assert result.Valid.Should().BeTrue(); @@ -514,7 +532,7 @@ public void GenerateFiles_For1GenericClassTT_Should_GenerateCorrectFiles() }; // Act - var result = _sut.Execute(Namespace, new[] { sourceFile }); + var result = _sut.Execute(Namespace, [sourceFile]); // Assert result.Valid.Should().BeTrue(); @@ -554,7 +572,7 @@ public void GenerateFiles_For2GenericClasses_Should_GenerateCorrectFiles() }; // Act - var result = _sut.Execute(Namespace, new[] { sourceFile1, sourceFile2 }); + var result = _sut.Execute(Namespace, [sourceFile1, sourceFile2]); // Assert result.Valid.Should().BeTrue(); @@ -600,7 +618,7 @@ public void GenerateFiles_For2GenericClasses_WithDefaultConstructor_Should_Gener }; // Act - var result = _sut.Execute(Namespace, new[] { sourceFile1, sourceFile2 }); + var result = _sut.Execute(Namespace, [sourceFile1, sourceFile2]); // Assert result.Valid.Should().BeTrue(); @@ -630,7 +648,7 @@ public void GenerateFiles_ClassWithReferenceToItself_Should_GenerateCorrectFiles }; // Act - var result = _sut.Execute(Namespace, new[] { sourceFile }); + var result = _sut.Execute(Namespace, [sourceFile]); // Assert result.Valid.Should().BeTrue(); @@ -660,7 +678,7 @@ public void GenerateFiles_ClassWithFuncAndAction_Should_GenerateCorrectFiles() }; // Act - var result = _sut.Execute(Namespace, new[] { sourceFile }); + var result = _sut.Execute(Namespace, [sourceFile]); // Assert result.Valid.Should().BeTrue(); @@ -690,7 +708,7 @@ public void GenerateFiles_ClassWithPropertyWhichHasAValue_Should_GenerateCorrect }; // Act - var result = _sut.Execute(Namespace, new[] { sourceFile }); + var result = _sut.Execute(Namespace, [sourceFile]); // Assert result.Valid.Should().BeTrue(); @@ -701,9 +719,6 @@ public void GenerateFiles_ClassWithPropertyWhichHasAValue_Should_GenerateCorrect if (Write) File.WriteAllText($"../../../DTO/{builderFileName}", builder.Text); builder.Text.Should().Be(File.ReadAllText($"../../../DTO/{builderFileName}")); - - //var b = new ClassWithCultureInfoBuilder(); - //var c = b.Build(); } [Fact] @@ -769,7 +784,7 @@ public void GenerateFiles_For2ClassesWithListBuilder_Should_GenerateCorrectFiles }; // Act - var result = _sut.Execute(Namespace, new[] { sourceFileTest, sourceFileOther }); + var result = _sut.Execute(Namespace, [sourceFileTest, sourceFileOther]); // Assert result.Valid.Should().BeTrue(); @@ -797,8 +812,7 @@ public void GenerateFiles_ClassWithInitProperties_Should_GenerateCorrectFiles() Text = File.ReadAllText(path), AttributeToAddToClass = new ExtraAttribute { - Name = "FluentBuilder.AutoGenerateBuilder", - //ArgumentList = new[] { "FluentBuilderAccessibility.PublicAndPrivate" } + Name = "FluentBuilder.AutoGenerateBuilder" } };