Skip to content

Commit

Permalink
Introduce non-generic IStructId for string-backed ids
Browse files Browse the repository at this point in the history
Strings are not structs so we cannot use them in conjunction with the type constraints in IStructId<T>. It simplifies things to have a non-generic interface for the string-version of a struct id, so we can keep the type constraints very specific for structs and other primitives.

The analyzer project is not renamed to StructId.Analyzer so we can instead have a StructId project for development of the main interfaces and code so it can be tested and compiled independently from its inclusion in the test project (which still simulates consumption scenario more closely).
  • Loading branch information
kzu committed Nov 22, 2024
1 parent 175cac1 commit c5e0604
Show file tree
Hide file tree
Showing 19 changed files with 100 additions and 42 deletions.
18 changes: 12 additions & 6 deletions StructId.sln
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,22 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.13.35507.96
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StructId", "src\StructId\StructId.csproj", "{32E82BDE-70EE-401F-B2E5-DFD2A8B00073}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StructId.Tests", "src\StructId.Tests\StructId.Tests.csproj", "{4616804F-CDBE-47A2-B03E-2D87F2088A69}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StructId.CodeFix", "src\StructId.CodeFix\StructId.CodeFix.csproj", "{BC2FCF3B-C564-4510-9285-FCC9E5E7C8D6}"
EndProject
Project("{13B669BE-BB05-4DDF-9536-439F39A36129}") = "StructId.Package", "src\StructId.Package\StructId.Package.msbuildproj", "{C56D242D-7106-08CA-0DC9-4FC3307CCC29}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StructId.Analyzer", "src\StructId.Analyzer\StructId.Analyzer.csproj", "{B8E5FB37-9E35-74F9-A797-24E938A01A82}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StructId", "src\StructId\StructId.csproj", "{CBFE34F2-2D9E-E7D9-10DB-75CAA5AF89B1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{32E82BDE-70EE-401F-B2E5-DFD2A8B00073}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{32E82BDE-70EE-401F-B2E5-DFD2A8B00073}.Debug|Any CPU.Build.0 = Debug|Any CPU
{32E82BDE-70EE-401F-B2E5-DFD2A8B00073}.Release|Any CPU.ActiveCfg = Release|Any CPU
{32E82BDE-70EE-401F-B2E5-DFD2A8B00073}.Release|Any CPU.Build.0 = Release|Any CPU
{4616804F-CDBE-47A2-B03E-2D87F2088A69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4616804F-CDBE-47A2-B03E-2D87F2088A69}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4616804F-CDBE-47A2-B03E-2D87F2088A69}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand All @@ -33,6 +31,14 @@ Global
{C56D242D-7106-08CA-0DC9-4FC3307CCC29}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C56D242D-7106-08CA-0DC9-4FC3307CCC29}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C56D242D-7106-08CA-0DC9-4FC3307CCC29}.Release|Any CPU.Build.0 = Release|Any CPU
{B8E5FB37-9E35-74F9-A797-24E938A01A82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B8E5FB37-9E35-74F9-A797-24E938A01A82}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B8E5FB37-9E35-74F9-A797-24E938A01A82}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B8E5FB37-9E35-74F9-A797-24E938A01A82}.Release|Any CPU.Build.0 = Release|Any CPU
{CBFE34F2-2D9E-E7D9-10DB-75CAA5AF89B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CBFE34F2-2D9E-E7D9-10DB-75CAA5AF89B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CBFE34F2-2D9E-E7D9-10DB-75CAA5AF89B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CBFE34F2-2D9E-E7D9-10DB-75CAA5AF89B1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public static string GetTypeName(this ITypeSymbol type, string containingNamespa

public static string ToFileName(this ITypeSymbol type) => type.ToDisplayString(FullName).Replace('+', '.');

public static bool IsStructId(this ITypeSymbol type) => type.AllInterfaces.Any(x => x.Name == "IStructId" && x.IsGenericType && x.TypeArguments.Length == 1);
public static bool IsStructId(this ITypeSymbol type) => type.AllInterfaces.Any(x => x.Name == "IStructId");

public static bool IsPartial(this ITypeSymbol node) => node.DeclaringSyntaxReferences.Any(
r => r.GetSyntax() is TypeDeclarationSyntax { Modifiers: { } modifiers } &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
void GenerateCode(SourceProductionContext context, INamedTypeSymbol symbol)
{
var ns = symbol.ContainingNamespace.ToDisplayString();
var type = symbol.AllInterfaces.First(x => x.Name == "IStructId").TypeArguments[0].GetTypeName(ns);
// Generic IStructId<T> -> T, otherwise string
var type = symbol.AllInterfaces.First(x => x.Name == "IStructId").TypeArguments.Select(x => x.GetTypeName(ns)).FirstOrDefault() ?? "string";

var kind = symbol.IsRecord && symbol.IsValueType ?
"record struct" :
symbol.IsRecord ?
"record" :
var kind = symbol.IsRecord && symbol.IsValueType ?
"record struct" :
symbol.IsRecord ?
"record" :
"class";

var output =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ public static class Diagnostics
"Change '{0}' to a partial readonly record struct as required for types used as struct ids.",
"Build",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
isEnabledByDefault: true,
helpLinkUri: $"{ThisAssembly.Project.RepositoryUrl}/blob/{ThisAssembly.Project.RepositoryBranch}/docs/SID001.md");
}
File renamed without changes.
File renamed without changes.
30 changes: 30 additions & 0 deletions src/StructId.Analyzer/StructId.Analyzer.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<PackFolder>analyzers/dotnet/roslyn4.8/cs</PackFolder>

<IsRoslynComponent>true</IsRoslynComponent>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Pack="false" Version="4.8.0" />
<PackageReference Include="NuGetizer" Version="1.2.3" />
<PackageReference Include="PolySharp" PrivateAssets="All" Version="1.14.1" />
<PackageReference Include="ThisAssembly.AssemblyInfo" Version="42.42.1242-main" PrivateAssets="all" />
<PackageReference Include="ThisAssembly.Project" Version="42.42.1242-main" PrivateAssets="all" />
</ItemGroup>

<Target Name="CopyEmbeddedCode" Inputs="@(EmbeddedCode)" Outputs="@(EmbeddedCode -> '$(IntermediateOutputPath)%(Filename).txt')">
<Copy SourceFiles="@(EmbeddedCode)" DestinationFiles="@(EmbeddedCode -> '$(IntermediateOutputPath)%(Filename).txt')" SkipUnchangedFiles="true" />
</Target>

<Target Name="AddEmbeddedResources" DependsOnTargets="CopyEmbeddedCode" BeforeTargets="SplitResourcesByCulture">
<ItemGroup>
<EmbeddedResource Include="@(EmbeddedCode -> '$(IntermediateOutputPath)%(Filename).txt')" Link="%(EmbeddedCode.Filename).txt" Type="Non-Resx" />
</ItemGroup>
</Target>

</Project>
2 changes: 1 addition & 1 deletion src/StructId.CodeFix/RecordCodeFix.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ protected override Task<Document> GetChangedDocumentAsync(CancellationToken canc
declaration.SemicolonToken);
}

return Task.FromResult(document.WithSyntaxRoot(root.ReplaceNode(original, declaration)));
return Task.FromResult(document.WithSyntaxRoot(root.ReplaceNode(original, declaration)));
}
}
}
2 changes: 1 addition & 1 deletion src/StructId.CodeFix/StructId.CodeFix.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\StructId\StructId.csproj" />
<ProjectReference Include="..\StructId.Analyzer\StructId.Analyzer.csproj" />
</ItemGroup>

</Project>
6 changes: 0 additions & 6 deletions src/StructId.Package/IStructId.cs

This file was deleted.

6 changes: 5 additions & 1 deletion src/StructId.Package/StructId.Package.msbuildproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@
<PackageReference Include="NuGetizer" Version="1.2.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\StructId.Analyzer\StructId.Analyzer.csproj" />
<ProjectReference Include="..\StructId.CodeFix\StructId.CodeFix.csproj" />
<ProjectReference Include="..\StructId\StructId.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="..\StructId\*.cs" Visible="false" />
<None Update="@(None)" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Project>
4 changes: 0 additions & 4 deletions src/StructId.Package/StructId.props
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
<Project>

<PropertyGroup>
</PropertyGroup>

<ItemGroup>
<CompilerVisibleProperty Include="IsTestProject" />
<CompilerVisibleProperty Include="StructIdNamespace" />
<CompilerVisibleProperty Include="RootNamespace" />
</ItemGroup>
Expand Down
8 changes: 4 additions & 4 deletions src/StructId.Package/StructId.targets
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
</PropertyGroup>

<ItemGroup>
<StructId Include="$(MSBuildThisFileDirectory)*.cs" />
<StructId Include="$(MSBuildThisFileDirectory)*.cs" Link="StructId\%(Filename)%(Extension)" />
</ItemGroup>

<Target Name="CopyStructId" Inputs="@(StructId)" Outputs="@(StructId -> '$(IntermediateOutputPath)%(Filename).g%(Extension)')">
<Target Name="CopyStructId" Inputs="@(StructId)" Outputs="@(StructId -> '$(IntermediateOutputPath)%(Link)')">
<PropertyGroup>
<StructIdTargetFile>@(StructId -> '$(IntermediateOutputPath)%(Filename).g%(Extension)')</StructIdTargetFile>
<StructIdTargetFile>@(StructId -> '$(IntermediateOutputPath)%(Link)')</StructIdTargetFile>
</PropertyGroup>

<PropertyGroup Condition="$(StructIdNamespace) != 'StructId'">
Expand All @@ -32,7 +32,7 @@

<Target Name="AddStructId" DependsOnTargets="CopyStructId" BeforeTargets="GenerateMSBuildEditorConfigFileShouldRun">
<ItemGroup>
<Compile Include="@(StructId -> '$(IntermediateOutputPath)%(Filename).g%(Extension)')" Link="%(StructId.Filename)%(Extension)" Visible="false" />
<Compile Include="@(StructId -> '$(IntermediateOutputPath)%(Link)')" />
</ItemGroup>
</Target>

Expand Down
2 changes: 1 addition & 1 deletion src/StructId.Tests/RecordCodeFixTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.CodeAnalysis;
using Verifier = Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<StructId.RecordAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;

namespace StructId;
Expand Down
17 changes: 9 additions & 8 deletions src/StructId.Tests/StructId.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">

<Import Project="..\StructId.Package\StructId.props" />
<Import Project="..\StructId.Package\bin\$(Configuration)\netstandard2.0\StructId.props" />

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
Expand All @@ -22,19 +22,20 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\StructId\StructId.csproj" OutputItemType="Analyzer" />
<ProjectReference Include="..\StructId.Analyzer\StructId.Analyzer.csproj" OutputItemType="Analyzer" />
<ProjectReference Include="..\StructId.CodeFix\StructId.CodeFix.csproj" OutputItemType="Analyzer" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="..\StructId.Package\IStructId.cs" Link="IStructId.cs" Type="Non-Resx" Visible="false" />
<ProjectReference Include="..\StructId.Package\StructId.Package.msbuildproj" ReferenceOutputAssembly="false" />
</ItemGroup>

<ItemGroup>
<Using Include="Xunit" />
<Using Include="Xunit.Abstractions" />
</ItemGroup>

<Import Project="..\StructId.Package\StructId.targets" />
<Import Project="..\StructId.Package\bin\$(Configuration)\netstandard2.0\StructId.targets" />

<ItemGroup>
<EmbeddedResource Include="@(StructId)" Type="Non-Resx" />
</ItemGroup>

</Project>
</Project>
8 changes: 5 additions & 3 deletions src/StructId.Tests/TestExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Testing;

namespace StructId;
Expand All @@ -15,7 +15,8 @@ public static TTest WithCodeFixStructId<TTest>(this TTest test) where TTest : Co
{
test.WithAnalyzerStructId();

test.FixedState.Sources.Add(("IStructId.cs", ThisAssembly.Resources.IStructId.Text));
test.FixedState.Sources.Add(("IStructId.cs", ThisAssembly.Resources.StructId.IStructId.Text));
test.FixedState.Sources.Add(("IStructId`1.cs", ThisAssembly.Resources.StructId.IStructId_1.Text));
// Fixes error CS0518: Predefined type 'System.Runtime.CompilerServices.IsExternalInit' is not defined or imported
test.FixedState.Sources.Add(
"""
Expand All @@ -38,7 +39,8 @@ public static TTest WithAnalyzerStructId<TTest>(this TTest test) where TTest : A
return project.WithParseOptions(parseOptions).Solution;
});

test.TestState.Sources.Add(("IStructId.cs", ThisAssembly.Resources.IStructId.Text));
test.TestState.Sources.Add(("IStructId.cs", ThisAssembly.Resources.StructId.IStructId.Text));
test.TestState.Sources.Add(("IStructId`1.cs", ThisAssembly.Resources.StructId.IStructId_1.Text));
// Fixes error CS0518: Predefined type 'System.Runtime.CompilerServices.IsExternalInit' is not defined or imported
test.TestState.Sources.Add(
"""
Expand Down
8 changes: 8 additions & 0 deletions src/StructId/IStructId.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// <auto-generated />

namespace StructId;

public partial interface IStructId
{
string Value { get; }
}
8 changes: 8 additions & 0 deletions src/StructId/IStructId`1.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// <auto-generated />

namespace StructId;

public partial interface IStructId<T> where T : struct
{
T Value { get; }
}
8 changes: 8 additions & 0 deletions src/StructId/StructId.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
</PropertyGroup>

<ItemGroup>
<Compile Remove="IStructId`1.cs" />
</ItemGroup>

<ItemGroup>
<None Include="IStructId`1.cs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Pack="false" Version="4.8.0" />
<PackageReference Include="NuGetizer" Version="1.2.3" />
Expand Down

0 comments on commit c5e0604

Please sign in to comment.