Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 8 additions & 31 deletions src/AutoCtor.Roslyn3.11/AutoConstructSourceGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace AutoCtor;

Expand All @@ -23,48 +22,26 @@ private sealed class SyntaxContextReceiver(CancellationToken cancellationToken)

public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
{
if (GeneratorUtilities.IsTypeDeclaration(context.Node, CancellationToken.None)
INamedTypeSymbol? type;
IMethodSymbol? method;
if (GeneratorUtilities.IsTypeDeclarationWithAttributes(context.Node, cancellationToken)

&& MemberHasAttribute(AttributeNames.AutoConstruct, context, cancellationToken)
&& (type = GeneratorUtilities.GetSymbol<INamedTypeSymbol>(context, cancellationToken)) != null

&& context.SemanticModel.GetDeclaredSymbol(context.Node, cancellationToken)
is INamedTypeSymbol type)
&& GeneratorUtilities.HasAttribute(type, AttributeNames.AutoConstruct))
{
(TypeModels ??= []).Add(TypeModel.Create(type));
}

else if (GeneratorUtilities.IsMethodDeclaration(context.Node, CancellationToken.None)
else if (GeneratorUtilities.IsMethodDeclarationWithAttributes(context.Node, cancellationToken)

&& MemberHasAttribute(AttributeNames.AutoPostConstruct, context, cancellationToken)
&& (method = GeneratorUtilities.GetSymbol<IMethodSymbol>(context, cancellationToken)) != null

&& context.SemanticModel.GetDeclaredSymbol(context.Node, cancellationToken)
is IMethodSymbol method)
&& GeneratorUtilities.HasAttribute(method, AttributeNames.AutoPostConstruct))
{
(MarkedMethods ??= []).Add(PostCtorModel.Create(method));
}
}

private static bool MemberHasAttribute(
string attribute,
GeneratorSyntaxContext context,
CancellationToken cancellationToken)
{
foreach (var attributeListSyntax in ((MemberDeclarationSyntax)context.Node).AttributeLists)
foreach (var attributeSyntax in attributeListSyntax.Attributes)
{
if (context.SemanticModel.GetSymbolInfo(attributeSyntax, cancellationToken)
.Symbol is not IMethodSymbol attributeSymbol)
continue;

var attributeContainingTypeSymbol = attributeSymbol.ContainingType;
var fullName = attributeContainingTypeSymbol.ToDisplayString();

if (fullName != attribute) continue;

return true;
}
return false;
}
}

public void Initialize(GeneratorInitializationContext context)
Expand Down
46 changes: 8 additions & 38 deletions src/AutoCtor.Roslyn4.0/AutoConstructSourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,51 +24,21 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
});

var types = context.SyntaxProvider.CreateSyntaxProvider(
static (n, ct) => GeneratorUtilities.IsTypeDeclaration(n, ct),
GetTypeModel)
.Where(static x => x.HasValue)
.Select(static (x, _) => x!.Value)
GeneratorUtilities.IsTypeDeclarationWithAttributes,
GeneratorUtilities.GetSymbol<INamedTypeSymbol>)
.Where(static x => GeneratorUtilities.HasAttribute(x, AttributeNames.AutoConstruct))
.Select(static (x, _) => TypeModel.Create(x!))
.Collect();

var postCtorMethods = context.SyntaxProvider.CreateSyntaxProvider(
static (n, ct) => GeneratorUtilities.IsMethodDeclaration(n, ct),
GetPostCtorModel)
.Where(static x => x.HasValue)
.Select(static (x, _) => x!.Value)
GeneratorUtilities.IsMethodDeclarationWithAttributes,
GeneratorUtilities.GetSymbol<IMethodSymbol>)
.Where(static x => GeneratorUtilities.HasAttribute(x, AttributeNames.AutoPostConstruct))
.Select(static (x, _) => PostCtorModel.Create(x!))
.Collect();

context.RegisterSourceOutput(
types.Combine(postCtorMethods).Combine(properties),
Emitter.GenerateSource);
}

private static TypeModel? GetTypeModel(GeneratorSyntaxContext context, CancellationToken cancellationToken)
{
if (context.SemanticModel
.GetDeclaredSymbol(context.Node, cancellationToken) is not INamedTypeSymbol type)
return null;

foreach (var attr in type.GetAttributes())
{
if (attr.AttributeClass?.ToDisplayString() == AttributeNames.AutoConstruct)
return TypeModel.Create(type);
}

return null;
}

private static PostCtorModel? GetPostCtorModel(GeneratorSyntaxContext context, CancellationToken cancellationToken)
{
if (context.SemanticModel
.GetDeclaredSymbol(context.Node, cancellationToken) is not IMethodSymbol method)
return null;

foreach (var attr in method.GetAttributes())
{
if (attr.AttributeClass?.ToDisplayString() == AttributeNames.AutoPostConstruct)
return PostCtorModel.Create(method);
}

return null;
}
}
4 changes: 2 additions & 2 deletions src/AutoCtor.Roslyn4.4/AutoConstructSourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ public void Initialize(IncrementalGeneratorInitializationContext context)

var types = context.SyntaxProvider.ForAttributeWithMetadataName(
AttributeNames.AutoConstruct,
GeneratorUtilities.IsTypeDeclaration,
GeneratorUtilities.IsTypeDeclarationWithAttributes,
static (c, ct) => TypeModel.Create((INamedTypeSymbol)c.TargetSymbol))
.WithTrackingName(TrackingNames.TypeModels)
.Collect();

var postCtorMethods = context.SyntaxProvider.ForAttributeWithMetadataName(
AttributeNames.AutoPostConstruct,
GeneratorUtilities.IsMethodDeclaration,
GeneratorUtilities.IsMethodDeclarationWithAttributes,
static (c, ct) => PostCtorModel.Create((IMethodSymbol)c.TargetSymbol))
.WithTrackingName(TrackingNames.PostCtorMethods)
.Collect();
Expand Down
1 change: 1 addition & 0 deletions src/AutoCtor.Shared/AutoCtor.Shared.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Models\AttributeNames.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\IHaveDiagnostics.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\MemberModel.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\ModelUtilities.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\ParameterModel.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\ParameterList.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\PostCtorModel.cs" />
Expand Down
19 changes: 7 additions & 12 deletions src/AutoCtor.Shared/Helpers/GeneratorUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,22 +68,17 @@ public static string GetHintName(ITypeSymbol type)
.Replace('>', ']');
}

public static bool IsTypeDeclaration(SyntaxNode node, CancellationToken cancellationToken)
public static bool IsTypeDeclarationWithAttributes(SyntaxNode node, CancellationToken cancellationToken)
=> node is TypeDeclarationSyntax { AttributeLists.Count: > 0 };

public static bool IsMethodDeclaration(SyntaxNode node, CancellationToken cancellationToken)
public static bool IsMethodDeclarationWithAttributes(SyntaxNode node, CancellationToken cancellationToken)
=> node is MethodDeclarationSyntax { AttributeLists.Count: > 0 };

public static string? GetServiceKey(ISymbol symbol)
{
var keyedService = symbol.GetAttributes()
.Where(a => a.AttributeClass?.ToDisplayString() == AttributeNames.AutoKeyedService
|| a.AttributeClass?.ToDisplayString() == "Microsoft.Extensions.DependencyInjection.FromKeyedServicesAttribute")
.FirstOrDefault();

if (keyedService != null)
return keyedService.ConstructorArguments[0].ToCSharpString();
public static TSymbol? GetSymbol<TSymbol>(GeneratorSyntaxContext context, CancellationToken cancellationToken) where TSymbol : class, ISymbol
=> context.SemanticModel.GetDeclaredSymbol(context.Node, cancellationToken) as TSymbol;

return null;
public static bool HasAttribute(ISymbol? symbol, string attributeName)
{
return symbol != null && symbol.GetAttributes().Any(a => a.AttributeClass?.ToDisplayString() == attributeName);
}
}
2 changes: 1 addition & 1 deletion src/AutoCtor.Shared/Models/MemberModel.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using Microsoft.CodeAnalysis;
using static GeneratorUtilities;
using static ModelUtilities;

internal readonly record struct MemberModel(
EquatableTypeSymbol Type,
Expand Down
18 changes: 18 additions & 0 deletions src/AutoCtor.Shared/Models/ModelUtilities.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;

internal static class ModelUtilities
{
public static string? GetServiceKey(ISymbol symbol)
{
var keyedService = symbol.GetAttributes()
.Where(a => a.AttributeClass?.ToDisplayString() == AttributeNames.AutoKeyedService
|| a.AttributeClass?.ToDisplayString() == "Microsoft.Extensions.DependencyInjection.FromKeyedServicesAttribute")
.FirstOrDefault();

if (keyedService != null)
return keyedService.ConstructorArguments[0].ToCSharpString();

return null;
}
}
2 changes: 1 addition & 1 deletion src/AutoCtor.Shared/Models/ParameterModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public static ParameterModel Create(IParameterSymbol parameter)
RefKind: parameter.RefKind,
Name: parameter.Name.EscapeKeywordIdentifier(),
ErrorName: parameter.Name,
KeyedService: GeneratorUtilities.GetServiceKey(parameter),
KeyedService: ModelUtilities.GetServiceKey(parameter),
IsOptional: parameter.IsOptional,
IsOutOrRef: parameter.RefKind == RefKind.Ref
|| parameter.RefKind == RefKind.Out,
Expand Down
16 changes: 7 additions & 9 deletions src/AutoCtor.Tests/AutoCtor.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Exe</OutputType>
<IsPackable>false</IsPackable>
<NoWarn>CS0169;CS0414</NoWarn>
</PropertyGroup>
Expand All @@ -13,8 +14,8 @@
</PropertyGroup>

<PropertyGroup>
<IntermediateOutputPath>obj\generator(GeneratorVersion)\</IntermediateOutputPath>
<OutputPath>bin\generator(GeneratorVersion)\</OutputPath>
<IntermediateOutputPath>obj\roslyn$(GeneratorVersion)\</IntermediateOutputPath>
<OutputPath>bin\roslyn$(GeneratorVersion)\</OutputPath>
</PropertyGroup>

<PropertyGroup Condition=" '$(GeneratorVersion)' == '3.11' ">
Expand All @@ -41,25 +42,22 @@
<ItemGroup>
<PackageReference Include="GitHubActionsTestLogger" PrivateAssets="all" />
<PackageReference Include="MarkdownSnippets.MsBuild" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Verify.SourceGenerators" />
<PackageReference Include="Verify.Xunit" />
<PackageReference Include="xunit" />
<PackageReference Include="Verify.XunitV3" />
<PackageReference Include="xunit.analyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio" PrivateAssets="all" />
<PackageReference Include="xunit.v3" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\AutoCtor.Attributes\AutoCtor.Attributes.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" />
<ProjectReference Include="..\AutoCtor.Roslyn$(GeneratorVersion)\AutoCtor.Roslyn$(GeneratorVersion).csproj" />
</ItemGroup>

Expand Down
17 changes: 10 additions & 7 deletions src/AutoCtor.Tests/ExampleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ public async Task ExamplesGeneratedCode(CodeFileTheoryData theoryData)
.AddGenerator(new AutoConstructSourceGenerator())
.WithAnalyzerOptions(theoryData.Options)
.Build(builder.ParseOptions)
.RunGenerators(compilation);
.RunGenerators(compilation, TestContext.Current.CancellationToken);

await Verify(driver)
.UseDirectory(theoryData.VerifiedDirectory)
.UseTypeName(theoryData.Name);
.UseTypeName(theoryData.Name)
.IgnoreParametersForVerified();
}

[Theory]
Expand All @@ -34,9 +35,10 @@ public async Task CodeCompilesProperly(CodeFileTheoryData theoryData)
.AddGenerator(new AutoConstructSourceGenerator())
.WithAnalyzerOptions(theoryData.Options)
.Build(builder.ParseOptions)
.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out _);
.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out _, TestContext.Current.CancellationToken);

Assert.Empty(outputCompilation.GetDiagnostics().Where(d => !theoryData.IgnoredCompileDiagnostics.Contains(d.Id)));
Assert.Empty(outputCompilation.GetDiagnostics(TestContext.Current.CancellationToken)
.Where(d => !theoryData.IgnoredCompileDiagnostics.Contains(d.Id)));
}

#if ROSLYN_4_4
Expand All @@ -52,16 +54,17 @@ public async Task EnsureRunsAreCachedCorrectly(CodeFileTheoryData theoryData)
.WithAnalyzerOptions(theoryData.Options)
.Build(builder.ParseOptions);

driver = driver.RunGenerators(compilation);
driver = driver.RunGenerators(compilation, TestContext.Current.CancellationToken);
var firstResult = driver.GetRunResult();

// Change the compilation
compilation = compilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText("// dummy",
CSharpParseOptions.Default.WithLanguageVersion(theoryData.LangPreview
? LanguageVersion.Preview
: LanguageVersion.Latest)));
: LanguageVersion.Latest),
cancellationToken: TestContext.Current.CancellationToken));

driver = driver.RunGenerators(compilation);
driver = driver.RunGenerators(compilation, TestContext.Current.CancellationToken);
var secondResult = driver.GetRunResult();

AssertRunsEqual(firstResult, secondResult,
Expand Down
10 changes: 5 additions & 5 deletions src/AutoCtor.Tests/GeneratedAttributeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public async Task AttributeGeneratedCode()
var driver = new GeneratorDriverBuilder()
.AddGenerator(new AttributeSourceGenerator())
.Build(builder.ParseOptions)
.RunGenerators(compilation);
.RunGenerators(compilation, TestContext.Current.CancellationToken);

await Verify(driver);
}
Expand All @@ -27,10 +27,10 @@ public async Task AttributeCompilesProperly()
var driver = new GeneratorDriverBuilder()
.AddGenerator(new AttributeSourceGenerator())
.Build(builder.ParseOptions)
.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out var diagnostics);
.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out var diagnostics, TestContext.Current.CancellationToken);

Assert.Empty(diagnostics);
Assert.Empty(outputCompilation.GetDiagnostics());
Assert.Empty(outputCompilation.GetDiagnostics(TestContext.Current.CancellationToken));
}

[Fact]
Expand All @@ -46,9 +46,9 @@ public async Task PreserveAttributesTest()
.AddGenerator(new AttributeSourceGenerator())
.AddGenerator(new AutoConstructSourceGenerator())
.Build(builder.ParseOptions)
.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out var diagnostics);
.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out var diagnostics, TestContext.Current.CancellationToken);

Assert.Empty(diagnostics);
Assert.Empty(outputCompilation.GetDiagnostics());
Assert.Empty(outputCompilation.GetDiagnostics(TestContext.Current.CancellationToken));
}
}
6 changes: 3 additions & 3 deletions src/AutoCtor.Tests/Issue73.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public async Task VerifyGeneratedCode()
var driver = new GeneratorDriverBuilder()
.AddGenerator(new AutoConstructSourceGenerator())
.Build(common.ParseOptions)
.RunGenerators(compilation);
.RunGenerators(compilation, TestContext.Current.CancellationToken);

await Verify(driver);
}
Expand All @@ -28,10 +28,10 @@ public async Task CodeCompilesWithoutErrors()
new GeneratorDriverBuilder()
.AddGenerator(new AutoConstructSourceGenerator())
.Build(common.ParseOptions)
.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out var diagnostics);
.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out var diagnostics, TestContext.Current.CancellationToken);

Assert.Empty(diagnostics);
Assert.Empty(outputCompilation.GetDiagnostics().Where(d => !ignoredWarnings.Contains(d.Id)));
Assert.Empty(outputCompilation.GetDiagnostics(TestContext.Current.CancellationToken).Where(d => !ignoredWarnings.Contains(d.Id)));
}

private static CompilationBuilder Common()
Expand Down
2 changes: 1 addition & 1 deletion src/AutoCtor.Tests/Utilities/CodeFileTheoryData.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
using Xunit.Abstractions;
using Xunit.Sdk;

public record CodeFileTheoryData : IXunitSerializable
{
Expand Down
6 changes: 3 additions & 3 deletions src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<PackageVersion Include="Verify.SourceGenerators" Version="2.5.0" />
<PackageVersion Include="Verify.Xunit" Version="29.3.0" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="Verify.XunitV3" Version="29.3.0" />
<PackageVersion Include="xunit.analyzers" Version="1.21.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.2" />
<PackageVersion Include="xunit.v3" Version="2.0.1" />
</ItemGroup>
</Project>
</Project>