From 12b395fd15e7aea572545aec609816d1fdeea652 Mon Sep 17 00:00:00 2001 From: Elias Holzer Date: Sat, 5 Mar 2022 21:15:20 +0100 Subject: [PATCH 1/6] Switches to official source generator --- CONTRIBUTING.md | 6 +- src/Directory.Build.props | 7 +- src/Directory.Build.targets | 23 ++- .../GenerateImmutableAttribute.cs | 2 - ...leObjectGraph.Generation.Attributes.csproj | 1 - .../CodeGenTests.cs | 95 ++++++------ ...mutableObjectGraph.Generation.Tests.csproj | 61 +------- .../CodeGen.cs | 23 ++- .../CodeGenerator.cs | 136 +++++++++++++++--- .../ImmutableObjectGraph.Generation.csproj | 56 +++++--- .../ImmutableObjectGraph.Generation.nuspec | 12 +- .../Readme.txt | 10 +- .../ImmutableObjectGraph.Generation.targets | 6 - src/ImmutableObjectGraph.sln | 5 +- .../ImmutableObjectGraph.csproj | 1 - src/RoslynDemo/RoslynDemo.csproj | 18 +-- src/global.json | 5 - 17 files changed, 240 insertions(+), 227 deletions(-) delete mode 100644 src/ImmutableObjectGraph.Generation/build/ImmutableObjectGraph.Generation.targets delete mode 100644 src/global.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3a1524f..81dc8b5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,20 +7,20 @@ This project is actively developed using the following software. It is highly recommended that anyone contributing to this library use the same software. -1. [Visual Studio 2017][VS]. +1. [Visual Studio 2022][VS]. All other dependencies are acquired via NuGet. ## Building Everything in the repo may be built via building the solution file -either from Visual Studio 2017 or the command line: +either from Visual Studio 2022 or the command line: msbuild /restore src\ImmutableObjectGraph.sln /t:pack ## Testing -The Visual Studio 2017 Test Explorer will list and execute all tests. +The Visual Studio 2022 Test Explorer will list and execute all tests. ## Pull requests diff --git a/src/Directory.Build.props b/src/Directory.Build.props index e42c586..6c4ed28 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -12,7 +12,7 @@ Andrew Arnott Andrew Arnott https://github.com/aarnott/immutableobjectgraph - https://raw.githubusercontent.com/AArnott/ImmutableObjectGraph/$GitCommitIdShort$/LICENSE.txt + MS-PL Copyright © Andrew Arnott immutable @@ -23,9 +23,4 @@ - - - https://raw.githubusercontent.com/aarnott/ImmutableObjectGraph/$(GitCommitIdShort)/LICENSE.txt - - diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index 1286826..aebcec3 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -1,22 +1,17 @@ - 0.6.1 + 4.1.0 - - - - - - - - + + + + + + - - + + - - - diff --git a/src/ImmutableObjectGraph.Generation.Attributes/GenerateImmutableAttribute.cs b/src/ImmutableObjectGraph.Generation.Attributes/GenerateImmutableAttribute.cs index d651913..de814b2 100644 --- a/src/ImmutableObjectGraph.Generation.Attributes/GenerateImmutableAttribute.cs +++ b/src/ImmutableObjectGraph.Generation.Attributes/GenerateImmutableAttribute.cs @@ -2,11 +2,9 @@ { using System; using System.Diagnostics; - using global::CodeGeneration.Roslyn; [AttributeUsage(AttributeTargets.Class)] [Conditional("CodeGeneration")] - [CodeGenerationAttribute("ImmutableObjectGraph.Generation.CodeGenerator, ImmutableObjectGraph.Generation, Version=" + ThisAssembly.AssemblyVersion + ", Culture=neutral, PublicKeyToken=" + ThisAssembly.PublicKeyToken)] public class GenerateImmutableAttribute : Attribute { public GenerateImmutableAttribute() diff --git a/src/ImmutableObjectGraph.Generation.Attributes/ImmutableObjectGraph.Generation.Attributes.csproj b/src/ImmutableObjectGraph.Generation.Attributes/ImmutableObjectGraph.Generation.Attributes.csproj index 315dbfc..6f58f42 100644 --- a/src/ImmutableObjectGraph.Generation.Attributes/ImmutableObjectGraph.Generation.Attributes.csproj +++ b/src/ImmutableObjectGraph.Generation.Attributes/ImmutableObjectGraph.Generation.Attributes.csproj @@ -10,6 +10,5 @@ - diff --git a/src/ImmutableObjectGraph.Generation.Tests/CodeGenTests.cs b/src/ImmutableObjectGraph.Generation.Tests/CodeGenTests.cs index afb53bf..ff135bd 100644 --- a/src/ImmutableObjectGraph.Generation.Tests/CodeGenTests.cs +++ b/src/ImmutableObjectGraph.Generation.Tests/CodeGenTests.cs @@ -9,8 +9,6 @@ using System.Text; using System.Threading; using System.Threading.Tasks; - using CodeGeneration.Roslyn.Engine; - using global::CodeGeneration.Roslyn; using ImmutableObjectGraph.Generation.Roslyn; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -38,7 +36,6 @@ public CodeGenTests(ITestOutputHelper logger) .WithCompilationOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)) .AddMetadataReferences(GetNetStandard20References()) .AddMetadataReference(MetadataReference.CreateFromFile(typeof(GenerateImmutableAttribute).Assembly.Location)) - .AddMetadataReference(MetadataReference.CreateFromFile(typeof(CodeGenerationAttributeAttribute).Assembly.Location)) .AddMetadataReference(MetadataReference.CreateFromFile(typeof(Optional).Assembly.Location)) .AddMetadataReference(MetadataReference.CreateFromFile(typeof(ImmutableArray).Assembly.Location)); var inputDocument = project.AddDocument("input.cs", string.Empty); @@ -265,40 +262,49 @@ protected async Task GenerateAsync(SourceText inputSource) { var solution = this.solution.WithDocumentText(this.inputDocumentId, inputSource); var inputDocument = solution.GetDocument(this.inputDocumentId); - var generatorDiagnostics = new List(); - var progress = new SynchronousProgress(generatorDiagnostics.Add); var inputCompilation = (CSharpCompilation)await inputDocument.Project.GetCompilationAsync(); - var inputSyntaxTree = await inputDocument.GetSyntaxTreeAsync(); - var outputSyntaxTree = await DocumentTransform.TransformAsync(inputCompilation, inputSyntaxTree, null, Assembly.Load, progress); - var outputDocument = inputDocument.Project - .AddDocument("output.cs", outputSyntaxTree.GetRoot()); - - // Make sure the result compiles without errors or warnings. - var compilation = await outputDocument.Project.GetCompilationAsync(); - var compilationDiagnostics = compilation.GetDiagnostics(); + var driver = CSharpGeneratorDriver.Create(new CodeGenerator()); + var runResult = driver.RunGenerators(inputCompilation).GetRunResult(); + var generatorDiagnostics = runResult.Diagnostics; + var project = inputDocument.Project; + + var i = 0; + var syntaxTrees = ImmutableArray.CreateBuilder(); + foreach (var t in runResult.GeneratedTrees) + { + var document = project.AddDocument($"output{i++}.cs", t.GetRoot()); - SourceText outputDocumentText = await outputDocument.GetTextAsync(); - this.logger.WriteLine("{0}", outputDocumentText); + SourceText outputDocumentText = await document.GetTextAsync(); + this.logger.WriteLine("{0}", outputDocumentText); - // Verify all line endings are consistent (otherwise VS can bug the heck out of the user if they have the generated file open). - string firstLineEnding = null; - foreach (var line in outputDocumentText.Lines) - { - string actualNewLine = line.Text.GetSubText(TextSpan.FromBounds(line.End, line.EndIncludingLineBreak)).ToString(); - if (firstLineEnding == null) - { - firstLineEnding = actualNewLine; - } - else if (actualNewLine != firstLineEnding && actualNewLine.Length > 0) + // Verify all line endings are consistent (otherwise VS can bug the heck out of the user if they have the generated file open). + string firstLineEnding = null; + foreach (var line in outputDocumentText.Lines) { - string expected = EscapeLineEndingCharacters(firstLineEnding); - string actual = EscapeLineEndingCharacters(actualNewLine); - Assert.True(false, $"Expected line ending characters '{expected}' but found '{actual}' on line {line.LineNumber + 1}.\nContent: {line}"); + string actualNewLine = line.Text.GetSubText(TextSpan.FromBounds(line.End, line.EndIncludingLineBreak)).ToString(); + if (firstLineEnding == null) + { + firstLineEnding = actualNewLine; + } + else if (actualNewLine != firstLineEnding && actualNewLine.Length > 0) + { + string expected = EscapeLineEndingCharacters(firstLineEnding); + string actual = EscapeLineEndingCharacters(actualNewLine); + Assert.True(false, $"Expected line ending characters '{expected}' but found '{actual}' on line {line.LineNumber + 1}.\nContent: {line}"); + } } + + var syntaxTree = await document.GetSyntaxTreeAsync(); + syntaxTrees.Add(syntaxTree); + + project = document.Project; } - var semanticModel = await outputDocument.GetSemanticModelAsync(); - var result = new GenerationResult(outputDocument, semanticModel, generatorDiagnostics, compilationDiagnostics); + // Make sure the result compiles without errors or warnings. + var compilation = await project.GetCompilationAsync(); + var compilationDiagnostics = compilation.GetDiagnostics(); + + var result = new GenerationResult(compilation, syntaxTrees.ToImmutable(), generatorDiagnostics, compilationDiagnostics); foreach (var diagnostic in generatorDiagnostics) { @@ -338,32 +344,39 @@ private static string EscapeLineEndingCharacters(string whitespace) private static IEnumerable GetNetStandard20References() { - string nugetPackageRoot = Environment.GetEnvironmentVariable("NUGET_PACKAGES") ?? Environment.ExpandEnvironmentVariables(@"%USERPROFILE%\.nuget\packages"); - string netstandardRoot = Path.Combine(nugetPackageRoot, @"netstandard.library\2.0.3\build\netstandard2.0\ref"); - foreach (string assembly in Directory.GetFiles(netstandardRoot, "*.dll")) + var nugetPackageRoot = Environment.GetEnvironmentVariable("NUGET_PACKAGES") ?? Environment.ExpandEnvironmentVariables(@"%USERPROFILE%\.nuget\packages"); + foreach (var dir in Directory.GetDirectories(Path.Combine(nugetPackageRoot, "netstandard.library"), "2.*")) { - yield return MetadataReference.CreateFromFile(assembly); + var netstandardRoot = Path.Combine(dir, @"build\netstandard2.0\ref"); + foreach (string assembly in Directory.GetFiles(netstandardRoot, "*.dll")) + { + yield return MetadataReference.CreateFromFile(assembly); + } + break; } } protected class GenerationResult { public GenerationResult( - Document document, - SemanticModel semanticModel, + Compilation compilation, + ImmutableArray syntaxTrees, IReadOnlyList generatorDiagnostics, IReadOnlyList compilationDiagnostics) { - this.Document = document; - this.SemanticModel = semanticModel; - this.Declarations = CSharpDeclarationComputer.GetDeclarationsInSpan(semanticModel, TextSpan.FromBounds(0, semanticModel.SyntaxTree.Length), true, CancellationToken.None); + this.Compilation = compilation; + this.SyntaxTrees = syntaxTrees; + this.SemanticModels = syntaxTrees.Select(s => compilation.GetSemanticModel(s)).ToImmutableArray(); + this.Declarations = SemanticModels.SelectMany(semanticModel => CSharpDeclarationComputer.GetDeclarationsInSpan(semanticModel, TextSpan.FromBounds(0, semanticModel.SyntaxTree.Length), true, CancellationToken.None)).ToImmutableArray(); this.GeneratorDiagnostics = generatorDiagnostics; this.CompilationDiagnostics = compilationDiagnostics; } - public Document Document { get; private set; } + public Compilation Compilation { get; } + + public ImmutableArray SemanticModels { get; private set; } - public SemanticModel SemanticModel { get; private set; } + public ImmutableArray SyntaxTrees { get; } internal ImmutableArray Declarations { get; private set; } diff --git a/src/ImmutableObjectGraph.Generation.Tests/ImmutableObjectGraph.Generation.Tests.csproj b/src/ImmutableObjectGraph.Generation.Tests/ImmutableObjectGraph.Generation.Tests.csproj index 6d9cd56..bdc463f 100644 --- a/src/ImmutableObjectGraph.Generation.Tests/ImmutableObjectGraph.Generation.Tests.csproj +++ b/src/ImmutableObjectGraph.Generation.Tests/ImmutableObjectGraph.Generation.Tests.csproj @@ -1,62 +1,18 @@  net472 + true - - MSBuild:GenerateCodeFromAttributes - - - MSBuild:GenerateCodeFromAttributes - - - MSBuild:GenerateCodeFromAttributes - - - MSBuild:GenerateCodeFromAttributes - - - MSBuild:GenerateCodeFromAttributes - - - MSBuild:GenerateCodeFromAttributes - - - MSBuild:GenerateCodeFromAttributes - - - MSBuild:GenerateCodeFromAttributes - - - MSBuild:GenerateCodeFromAttributes - - - MSBuild:GenerateCodeFromAttributes - - - MSBuild:GenerateCodeFromAttributes - - - MSBuild:GenerateCodeFromAttributes - - - MSBuild:GenerateCodeFromAttributes - - - MSBuild:GenerateCodeFromAttributes - - - MSBuild:GenerateCodeFromAttributes - @@ -65,12 +21,6 @@ - - MSBuild:GenerateCodeFromAttributes - - - MSBuild:GenerateCodeFromAttributes - @@ -84,15 +34,10 @@ - - - - - - + - + \ No newline at end of file diff --git a/src/ImmutableObjectGraph.Generation/CodeGen.cs b/src/ImmutableObjectGraph.Generation/CodeGen.cs index 7082d82..0afc8c8 100644 --- a/src/ImmutableObjectGraph.Generation/CodeGen.cs +++ b/src/ImmutableObjectGraph.Generation/CodeGen.cs @@ -11,7 +11,6 @@ using System.Text; using System.Threading; using System.Threading.Tasks; - using global::CodeGeneration.Roslyn; using ImmutableObjectGraph.Generation.Roslyn; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -262,7 +261,7 @@ private static bool HasEqualityOperators(ITypeSymbol symbol) var equalityOperators = from method in symbol.GetMembers().OfType() where method.MethodKind == MethodKind.BuiltinOperator || method.MethodKind == MethodKind.UserDefinedOperator - where method.Parameters.Length == 2 && method.Parameters.All(p => p.Type.Equals(symbol)) + where method.Parameters.Length == 2 && method.Parameters.All(p => p.Type.Equals(symbol, SymbolEqualityComparer.Default)) where method.Name == "op_Equality" select method; return equalityOperators.Any(); @@ -1240,9 +1239,9 @@ public IEnumerable Descendents var that = this; return from type in this.generator.TypesInInputDocument - where type != that.TypeSymbol + where !SymbolEqualityComparer.Default.Equals(type, that.TypeSymbol) let metaType = new MetaType(that.generator, type) - where metaType.Ancestors.Any(a => a.TypeSymbol == that.TypeSymbol) + where metaType.Ancestors.Any(a => SymbolEqualityComparer.Default.Equals(a.TypeSymbol, that.TypeSymbol)) select metaType; } } @@ -1252,7 +1251,7 @@ public MetaField RecursiveField get { var allowedElementTypes = this.ThisTypeAndAncestors; - var matches = this.LocalFields.Where(f => f.IsCollection && !f.IsDefinitelyNotRecursive && allowedElementTypes.Any(t => t.TypeSymbol.Equals(f.ElementType))).ToList(); + var matches = this.LocalFields.Where(f => f.IsCollection && !f.IsDefinitelyNotRecursive && allowedElementTypes.Any(t => t.TypeSymbol.Equals(f.ElementType, SymbolEqualityComparer.Default))).ToList(); return matches.Count == 1 ? matches.First() : default(MetaField); } } @@ -1343,7 +1342,7 @@ public bool ChildrenAreOrdered public IEnumerable GetFieldsBeyond(MetaType ancestor) { - if (ancestor.TypeSymbol == this.TypeSymbol) + if (SymbolEqualityComparer.Default.Equals(ancestor.TypeSymbol, this.TypeSymbol)) { return ImmutableList.Create(); } @@ -1359,7 +1358,7 @@ public bool IsAssignableFrom(ITypeSymbol type) return false; } - return type == this.TypeSymbol + return SymbolEqualityComparer.Default.Equals(type, this.TypeSymbol) || this.IsAssignableFrom(type.BaseType); } @@ -1407,12 +1406,12 @@ public override bool Equals(object obj) public bool Equals(MetaType other) { return this.generator == other.generator - && this.TypeSymbol == other.TypeSymbol; + && SymbolEqualityComparer.Default.Equals(this.TypeSymbol, other.TypeSymbol); } public override int GetHashCode() { - return this.TypeSymbol?.GetHashCode() ?? 0; + return SymbolEqualityComparer.Default.GetHashCode(this.TypeSymbol); } private MetaType FindMetaType(INamedTypeSymbol type) @@ -1475,7 +1474,7 @@ public MetaType DeclaringType public bool IsRecursiveCollection { - get { return this.IsCollection && !this.DeclaringType.RecursiveType.IsDefault && this.ElementType == this.DeclaringType.RecursiveType.TypeSymbol; } + get { return this.IsCollection && !this.DeclaringType.RecursiveType.IsDefault && SymbolEqualityComparer.Default.Equals(this.ElementType, this.DeclaringType.RecursiveType.TypeSymbol); } } public bool IsDefinitelyNotRecursive => IsAttributeApplied(this.Symbol); @@ -1486,7 +1485,7 @@ public bool IsRecursiveCollection /// public bool IsLocallyDefined { - get { return this.Symbol.ContainingType == this.metaType.Generator.applyToSymbol; } + get { return SymbolEqualityComparer.Default.Equals(this.Symbol.ContainingType, this.metaType.Generator.applyToSymbol); } } public IFieldSymbol Symbol { get; } @@ -1514,7 +1513,7 @@ public bool IsAssignableFrom(ITypeSymbol type) } var that = this; - return type == this.Symbol.Type + return SymbolEqualityComparer.Default.Equals(type, this.Symbol.Type) || this.IsAssignableFrom(type.BaseType) || type.Interfaces.Any(i => that.IsAssignableFrom(i)); } diff --git a/src/ImmutableObjectGraph.Generation/CodeGenerator.cs b/src/ImmutableObjectGraph.Generation/CodeGenerator.cs index 9828368..fb82ee2 100644 --- a/src/ImmutableObjectGraph.Generation/CodeGenerator.cs +++ b/src/ImmutableObjectGraph.Generation/CodeGenerator.cs @@ -3,47 +3,141 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; + using System.Diagnostics; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; - using CodeGeneration.Roslyn; using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Validation; + using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; - public class CodeGenerator : ICodeGenerator + [Generator] + public class CodeGenerator : IIncrementalGenerator { - private readonly AttributeData attributeData; - private readonly ImmutableDictionary data; + private const string GenerateImmutableAttributeMetadataName = "ImmutableObjectGraph.Generation.GenerateImmutableAttribute"; - public CodeGenerator(AttributeData attributeData) + public void Initialize(IncrementalGeneratorInitializationContext context) { - Requires.NotNull(attributeData, nameof(attributeData)); + var classes = context.SyntaxProvider.CreateSyntaxProvider( + predicate: (node, token) => node is ClassDeclarationSyntax @class && @class.AttributeLists.Count > 0, + transform: (ctx, token) => GetSemanticTargetForGeneration(ctx, token)) + .Where(c => c != null); - this.attributeData = attributeData; - this.data = this.attributeData.NamedArguments.ToImmutableDictionary(kv => kv.Key, kv => kv.Value); + var compilationAndClasses = context.CompilationProvider.Combine(classes.Collect()); + + context.RegisterSourceOutput(compilationAndClasses, (spc, source) => Execute(source.Left, source.Right, spc)); } - public Task> GenerateAsync(TransformationContext context, IProgress progress, CancellationToken cancellationToken) + private static ClassDeclarationSyntax GetSemanticTargetForGeneration(GeneratorSyntaxContext context, CancellationToken token) { - Requires.NotNull(context, nameof(context)); + var classSyntax = (ClassDeclarationSyntax)context.Node; - var options = new CodeGen.Options(this.attributeData) + foreach (var attributeListSyntax in classSyntax.AttributeLists) { - GenerateBuilder = this.GetBoolData(nameof(GenerateImmutableAttribute.GenerateBuilder)), - Delta = this.GetBoolData(nameof(GenerateImmutableAttribute.Delta)), - DefineInterface = this.GetBoolData(nameof(GenerateImmutableAttribute.DefineInterface)), - DefineRootedStruct = this.GetBoolData(nameof(GenerateImmutableAttribute.DefineRootedStruct)), - DefineWithMethodsPerProperty = this.GetBoolData(nameof(GenerateImmutableAttribute.DefineWithMethodsPerProperty)), - }; - - return CodeGen.GenerateAsync((ClassDeclarationSyntax)context.ProcessingNode, context.SemanticModel, progress, options, cancellationToken); + foreach (var attributeSyntax in attributeListSyntax.Attributes) + { + var attributeSymbol = context.SemanticModel.GetSymbolInfo(attributeSyntax, token).Symbol as IMethodSymbol; + if (attributeSymbol is null) + continue; + + var attributeContainingTypeSymbol = attributeSymbol.ContainingType; + var fullName = attributeContainingTypeSymbol.ToDisplayString(); + + if (fullName == GenerateImmutableAttributeMetadataName) + return classSyntax; + } + } + + return null; } - private bool GetBoolData(string name) + private static void Execute(Compilation compilation, ImmutableArray classes, SourceProductionContext context) { - return (bool)(this.data.GetValueOrDefault(name).Value ?? false); + if (classes.IsDefaultOrEmpty) + return; + + var attributeSymbol = compilation.GetTypeByMetadataName(GenerateImmutableAttributeMetadataName) + ?? throw new InvalidOperationException("Symbol not found: " + GenerateImmutableAttributeMetadataName); + + foreach (var syntax in classes.Distinct()) + { + var semanticModel = compilation.GetSemanticModel(syntax.SyntaxTree); + var classSymbol = semanticModel.GetDeclaredSymbol(syntax); + var attributeData = classSymbol?.GetAttributes().FirstOrDefault(d => d.AttributeClass?.Equals(attributeSymbol, SymbolEqualityComparer.Default) == true); + var data = attributeData.NamedArguments.ToImmutableDictionary(kv => kv.Key, kv => kv.Value); + var options = new CodeGen.Options(attributeData) + { + GenerateBuilder = GetBoolData(data, nameof(GenerateImmutableAttribute.GenerateBuilder)), + Delta = GetBoolData(data, nameof(GenerateImmutableAttribute.Delta)), + DefineInterface = GetBoolData(data, nameof(GenerateImmutableAttribute.DefineInterface)), + DefineRootedStruct = GetBoolData(data, nameof(GenerateImmutableAttribute.DefineRootedStruct)), + DefineWithMethodsPerProperty = GetBoolData(data, nameof(GenerateImmutableAttribute.DefineWithMethodsPerProperty)), + }; + + var compilationUnit = CreateSource(context, syntax, semanticModel, classSymbol, options); + var sourceText = SyntaxTree(compilationUnit, encoding: Encoding.UTF8).GetText(); + context.AddSource($"{syntax.Identifier}.g.cs", sourceText); + } + } + + private static bool GetBoolData(ImmutableDictionary data, string name) + { + return (bool)(data.GetValueOrDefault(name).Value ?? false); + } + + private static CompilationUnitSyntax CreateSource(SourceProductionContext context, ClassDeclarationSyntax declarationSyntax, SemanticModel semanticModel, INamedTypeSymbol typeSymbol, CodeGen.Options options) + { + var root = declarationSyntax.SyntaxTree.GetCompilationUnitRoot(); + var declaredUsings = root.Usings; + foreach (var ns in root.Members.OfType()) + declaredUsings = declaredUsings.AddRange(ns.Usings); + + var usings = List(declaredUsings); + + var generatedMembers = CodeGen.GenerateAsync(declarationSyntax, semanticModel, progress: new Progress(context), options, context.CancellationToken).Result; + + var containingType = typeSymbol; + while (containingType.ContainingType != null) + { + containingType = containingType.ContainingType; + + generatedMembers = SingletonList( + ClassDeclaration(containingType.Name) + .WithModifiers(TokenList(Token(SyntaxKind.PartialKeyword))) + .WithMembers(generatedMembers)); + } + + if (!containingType.ContainingNamespace.IsGlobalNamespace) + { + generatedMembers = SingletonList( + NamespaceDeclaration( + ParseName(typeSymbol.ContainingNamespace.ToDisplayString())) + .WithMembers(generatedMembers)); + } + + return + CompilationUnit() + .WithUsings(usings) + .WithMembers(generatedMembers) + .NormalizeWhitespace(); + } + + private class Progress : IProgress + { + private readonly SourceProductionContext context; + + public Progress(SourceProductionContext context) + { + this.context = context; + } + + public void Report(Diagnostic value) + { + context.ReportDiagnostic(value); + } } } } diff --git a/src/ImmutableObjectGraph.Generation/ImmutableObjectGraph.Generation.csproj b/src/ImmutableObjectGraph.Generation/ImmutableObjectGraph.Generation.csproj index b01d157..872a958 100644 --- a/src/ImmutableObjectGraph.Generation/ImmutableObjectGraph.Generation.csproj +++ b/src/ImmutableObjectGraph.Generation/ImmutableObjectGraph.Generation.csproj @@ -4,14 +4,11 @@ netstandard2.0 MSBuild-based code generator to transform simple mutable type definitions into fully functional immutable types with persistent characteristics. - true - true - - $(GenerateNuspecDependsOn);SetNuSpecPropertiesForHost - $(MSBuildProjectName).nuspec false + $(GetTargetPathDependsOn);GetDependencyTargetPaths $(TargetsForTfmSpecificContentInPackage);PackBuildOutputs + True @@ -19,9 +16,6 @@ DiagnosticsStrings.resx - - - ResXFileCodeGenerator @@ -33,29 +27,47 @@ - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - - - - + ..\..\lib\netstandard1.0\CG.Pluralization.dll - + + + + - - - + + + + + + + + + + + + + + - - - id=$(PackageId);version=$(NuGetPackageVersion);authors=$(Authors);copyright=$(Copyright);tags=$(PackageTags);requireLicenseAcceptance=$(PackageRequireLicenseAcceptance);licenseUrl=$(PackageLicenseUrl);description=$(Description);CodeGenerationRoslynVersion=$(CodeGenerationRoslynVersion);Configuration=$(Configuration);OutDir=$(OutDir) - + + + + + + + + diff --git a/src/ImmutableObjectGraph.Generation/ImmutableObjectGraph.Generation.nuspec b/src/ImmutableObjectGraph.Generation/ImmutableObjectGraph.Generation.nuspec index 81e8819..474147a 100644 --- a/src/ImmutableObjectGraph.Generation/ImmutableObjectGraph.Generation.nuspec +++ b/src/ImmutableObjectGraph.Generation/ImmutableObjectGraph.Generation.nuspec @@ -12,19 +12,17 @@ $PackageTags$ - - - - - - + + + + + - diff --git a/src/ImmutableObjectGraph.Generation/Readme.txt b/src/ImmutableObjectGraph.Generation/Readme.txt index b65e676..1f7aa74 100644 --- a/src/ImmutableObjectGraph.Generation/Readme.txt +++ b/src/ImmutableObjectGraph.Generation/Readme.txt @@ -14,16 +14,10 @@ readonly string lastName; } -2. On each source file that contains the [GenerateImmutable] attribute, set the file's - Custom Tool property to "MSBuild:GenerateCodeFromAttributes". - -3. You may need to close and reopen the project/solution after first setting the - Custom Tool property before IntelliSense includes the generated members. - -4. Create a Person in another of your code files using the generated factory method: +2. Create a Person in another of your code files using the generated factory method: var author = Person.Create(firstName: "Andrew", lastName: "Arnott"); -5. Now add more types, add arguments to the [GenerateImmutable(...)] attribute, etc. +3. Now add more types, add arguments to the [GenerateImmutable(...)] attribute, etc. Send issues to https://github.com/aarnott/immutableobjectgraph/issues diff --git a/src/ImmutableObjectGraph.Generation/build/ImmutableObjectGraph.Generation.targets b/src/ImmutableObjectGraph.Generation/build/ImmutableObjectGraph.Generation.targets deleted file mode 100644 index 5a91b7d..0000000 --- a/src/ImmutableObjectGraph.Generation/build/ImmutableObjectGraph.Generation.targets +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/ImmutableObjectGraph.sln b/src/ImmutableObjectGraph.sln index bde0def..17546c6 100644 --- a/src/ImmutableObjectGraph.sln +++ b/src/ImmutableObjectGraph.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28010.2036 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32210.238 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImmutableObjectGraph", "ImmutableObjectGraph\ImmutableObjectGraph.csproj", "{63930555-500F-4E7B-9F24-3D5C3D4F0573}" EndProject @@ -12,7 +12,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ..\CONTRIBUTING.md = ..\CONTRIBUTING.md Directory.Build.props = Directory.Build.props Directory.Build.targets = Directory.Build.targets - global.json = global.json ..\LICENSE.txt = ..\LICENSE.txt NuGet.Config = NuGet.Config ..\README.md = ..\README.md diff --git a/src/ImmutableObjectGraph/ImmutableObjectGraph.csproj b/src/ImmutableObjectGraph/ImmutableObjectGraph.csproj index 41e881d..ed86784 100644 --- a/src/ImmutableObjectGraph/ImmutableObjectGraph.csproj +++ b/src/ImmutableObjectGraph/ImmutableObjectGraph.csproj @@ -4,7 +4,6 @@ - diff --git a/src/RoslynDemo/RoslynDemo.csproj b/src/RoslynDemo/RoslynDemo.csproj index 639c146..4700d96 100644 --- a/src/RoslynDemo/RoslynDemo.csproj +++ b/src/RoslynDemo/RoslynDemo.csproj @@ -4,25 +4,9 @@ Exe false - - - MSBuild:GenerateCodeFromAttributes - - - MSBuild:GenerateCodeFromAttributes - - - - - false - - - - - - + diff --git a/src/global.json b/src/global.json deleted file mode 100644 index 3d26ee6..0000000 --- a/src/global.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "sdk": { - "version": "2.2.401" - } -} From cad0f884e06ea5fab1b543e5f19c40fcf2a639cc Mon Sep 17 00:00:00 2001 From: azeno Date: Sat, 14 May 2022 18:28:25 +0200 Subject: [PATCH 2/6] Updates GitVersioning to be able to build on Linux --- src/Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 6c4ed28..52462d8 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -17,7 +17,7 @@ immutable - + From 165dd941c1095719352a448736f749371a1e252f Mon Sep 17 00:00:00 2001 From: Elias Holzer Date: Fri, 16 Aug 2024 17:41:58 +0200 Subject: [PATCH 3/6] Fixes compile error when referenced as project --- .../ImmutableObjectGraph.Generation.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImmutableObjectGraph.Generation/ImmutableObjectGraph.Generation.csproj b/src/ImmutableObjectGraph.Generation/ImmutableObjectGraph.Generation.csproj index 872a958..eab5f14 100644 --- a/src/ImmutableObjectGraph.Generation/ImmutableObjectGraph.Generation.csproj +++ b/src/ImmutableObjectGraph.Generation/ImmutableObjectGraph.Generation.csproj @@ -66,7 +66,7 @@ - + From 43d0e71e22745d3167213c779805cb941cf02c1f Mon Sep 17 00:00:00 2001 From: Elias Holzer Date: Tue, 20 Aug 2024 15:24:15 +0200 Subject: [PATCH 4/6] Disables packages --- src/Directory.Build.props | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 52462d8..26099f1 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -15,6 +15,9 @@ MS-PL Copyright © Andrew Arnott immutable + + + false From 3c93227950aa88bb987f755d220fd73e4c32edc8 Mon Sep 17 00:00:00 2001 From: Elias Holzer Date: Tue, 20 Aug 2024 15:32:24 +0200 Subject: [PATCH 5/6] Consolidates package version --- src/Directory.Build.props | 2 +- src/Directory.Build.targets | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 26099f1..02618e5 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -20,7 +20,7 @@ false - + diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index aebcec3..5d2085f 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -1,14 +1,14 @@ - 4.1.0 + 4.8.0 - + - + From 4d39856f33e1c23e1ec8ac818161105014f60e8b Mon Sep 17 00:00:00 2001 From: Elias Holzer Date: Wed, 21 Aug 2024 12:13:01 +0200 Subject: [PATCH 6/6] Some build fixes when cross compiling --- .../ImmutableObjectGraph.Generation.csproj | 1 + src/NuGet.Config | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ImmutableObjectGraph.Generation/ImmutableObjectGraph.Generation.csproj b/src/ImmutableObjectGraph.Generation/ImmutableObjectGraph.Generation.csproj index eab5f14..638541a 100644 --- a/src/ImmutableObjectGraph.Generation/ImmutableObjectGraph.Generation.csproj +++ b/src/ImmutableObjectGraph.Generation/ImmutableObjectGraph.Generation.csproj @@ -4,6 +4,7 @@ netstandard2.0 MSBuild-based code generator to transform simple mutable type definitions into fully functional immutable types with persistent characteristics. + true false $(GetTargetPathDependsOn);GetDependencyTargetPaths $(TargetsForTfmSpecificContentInPackage);PackBuildOutputs diff --git a/src/NuGet.Config b/src/NuGet.Config index fb3aee9..e067851 100644 --- a/src/NuGet.Config +++ b/src/NuGet.Config @@ -5,7 +5,7 @@ - +