Skip to content

Commit 33324ba

Browse files
authored
Merge pull request #41 from andrewlock/nested-types
Add support for nested types
2 parents b44ab92 + c6d09ce commit 33324ba

24 files changed

+864
-61
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## [v1.0.0-beta04]
4+
5+
New Features:
6+
7+
* Added support for IDs inside nested classes/records/structs (Fixes https://github.com/andrewlock/StronglyTypedId/issues/40)
8+
39
## [v1.0.0-beta03]
410

511
Breaking Changes:

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Version 0.x of this library used the helper library [CodeGeneration.Roslyn](http
4444

4545
### Bug Fixes
4646

47-
* Some converters had incorrect implementations, such as in ([#26](https://github.com/andrewlock/StronglyTypedId/issues/24)). These have been addressed in version 1.x.
47+
* Some converters had incorrect implementations, such as in ([#24](https://github.com/andrewlock/StronglyTypedId/issues/24)). These have been addressed in version 1.x.
4848
* Better null handling has been added for the `String` backing type, handling issues such as [#32](https://github.com/andrewlock/StronglyTypedId/issues/32).
4949
* The code is marked as auto generated, to avoid errors such as #CS1591 as described in [#27](https://github.com/andrewlock/StronglyTypedId/issues/27)
5050
* An error deserializing nullable StronglyTypedIds with Newtonsoft.Json [#36](https://github.com/andrewlock/StronglyTypedId/issues/36)
@@ -69,7 +69,7 @@ To install the packages, add the references to your _csproj_ file so that it loo
6969
</PropertyGroup>
7070

7171
<!-- Core package -->
72-
<PackageReference Include="StronglyTypedId" Version="1.0.0-beta03" />
72+
<PackageReference Include="StronglyTypedId" Version="1.0.0-beta04" />
7373
<!-- -->
7474

7575
</Project>
@@ -146,8 +146,8 @@ Add the package to your solution, ensuring you set `"PrivateAssets="All"` in the
146146
</PropertyGroup>
147147

148148
<!-- Core package -->
149-
<PackageReference Include="StronglyTypedId" Version="1.0.0-beta03" />
150-
<PackageReference Include="StronglyTypedId.Attributes" Version="1.0.0-beta03">
149+
<PackageReference Include="StronglyTypedId" Version="1.0.0-beta04" />
150+
<PackageReference Include="StronglyTypedId.Attributes" Version="1.0.0-beta04">
151151
<PrivateAssets>All</PrivateAssets>
152152
</PackageReference>
153153
<!-- -->
@@ -179,7 +179,7 @@ The StronglyTypedId NuGet package is a .NET Standard 2.0 package.
179179

180180
You must be using the .NET 6+ SDK (though you can compile for other target frameworks like .NET Core 2.1 and .NET Framework 4.8)
181181

182-
The `struct`s you decorate with the `StronglyTypedId` attribute must be marked `partial`, and cannot be nested inside another class.
182+
The `struct`s you decorate with the `StronglyTypedId` attribute must be marked `partial`.
183183

184184
## Credits
185185
[Credits]: #credits

build/Build.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ class Build : NukeBuild
119119
TestsDirectory / "StronglyTypedIds.Nuget.Attributes.IntegrationTests",
120120
};
121121

122+
if (!string.IsNullOrEmpty(PackagesDirectory))
123+
{
124+
DeleteDirectory(PackagesDirectory / "stronglytypedid");
125+
DeleteDirectory(PackagesDirectory / "stronglytypedid.attributes");
126+
}
127+
122128
DotNetRestore(s => s
123129
.When(!string.IsNullOrEmpty(PackagesDirectory), x => x.SetPackageDirectory(PackagesDirectory))
124130
.SetConfigFile(RootDirectory / "NuGet.integration-tests.config")

releasenotes.props

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
66
Version 0.x of this library used the helper library CodeGeneration.Roslyn for build-time source generation. In version 1.x this approach has been completely replaced in favour of source generators, as these are explicitly supported in .NET 5+. As part of this change, there were a number of additional features added and breaking changes made.
77
8+
## Changes in 1.0.0-beta04:
9+
10+
New Features:
11+
12+
* Added support for IDs inside nested classes/records/structs (Fixes https://github.com/andrewlock/StronglyTypedId/issues/40)
13+
814
## Changes in 1.0.0-beta03:
915
1016
Breaking Changes:

src/StronglyTypedIds.Attributes/StronglyTypedIds.Attributes.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
<PackageId>StronglyTypedId.Attributes</PackageId>
88
<Description>Helper library for StronglyTypedId generator containg attributes only. See README for when to use this package </Description>
99
<IsPackable>true</IsPackable>
10-
<DevelopmentDependency>true</DevelopmentDependency>
1110
</PropertyGroup>
1211

1312
</Project>

src/StronglyTypedIds/Diagnostics/NestedTypeDiagnostic.cs

Lines changed: 0 additions & 17 deletions
This file was deleted.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace StronglyTypedIds;
2+
3+
internal class ParentClass
4+
{
5+
public ParentClass(string keyword, string name, string constraints, ParentClass? child)
6+
{
7+
Keyword = keyword;
8+
Name = name;
9+
Constraints = constraints;
10+
Child = child;
11+
}
12+
13+
public ParentClass? Child { get; }
14+
public string Keyword { get; }
15+
public string Name { get; }
16+
public string Constraints { get; }
17+
}

src/StronglyTypedIds/Parser.cs

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,13 @@ public static bool IsAttributeTargetForGeneration(SyntaxNode node)
8484
return null;
8585
}
8686

87-
public static List<(string Name, string NameSpace, StronglyTypedIdConfiguration Config)> GetTypesToGenerate(
87+
public static List<(string Name, string NameSpace, StronglyTypedIdConfiguration Config, ParentClass? Parent)> GetTypesToGenerate(
8888
Compilation compilation,
8989
ImmutableArray<StructDeclarationSyntax> targets,
9090
Action<Diagnostic> reportDiagnostic,
9191
CancellationToken ct)
9292
{
93-
var idsToGenerate = new List<(string Name, string NameSpace, StronglyTypedIdConfiguration Config)>();
93+
var idsToGenerate = new List<(string Name, string NameSpace, StronglyTypedIdConfiguration Config, ParentClass? Parent)>();
9494
INamedTypeSymbol? idAttribute = compilation.GetTypeByMetadataName(StronglyTypedIdAttribute);
9595
if (idAttribute == null)
9696
{
@@ -226,15 +226,11 @@ public static bool IsAttributeTargetForGeneration(SyntaxNode node)
226226
reportDiagnostic(NotPartialDiagnostic.Create(structDeclarationSyntax));
227227
}
228228

229-
if (structSymbol.ContainingType is not null)
230-
{
231-
reportDiagnostic(NestedTypeDiagnostic.Create(structDeclarationSyntax));
232-
}
233-
234-
string nameSpace = structSymbol.ContainingNamespace.IsGlobalNamespace ? string.Empty : structSymbol.ContainingNamespace.ToString();
229+
string nameSpace = GetNameSpace(structDeclarationSyntax);
230+
var parentClass = GetParentClasses(structDeclarationSyntax);
235231
var name = structSymbol.Name;
236232

237-
idsToGenerate.Add((Name: name, NameSpace: nameSpace, Config: config.Value));
233+
idsToGenerate.Add((Name: name, NameSpace: nameSpace, Config: config.Value, Parent: parentClass));
238234
}
239235

240236
return idsToGenerate;
@@ -370,4 +366,58 @@ public static bool IsAttributeTargetForGeneration(SyntaxNode node)
370366

371367
return null;
372368
}
369+
370+
private static string GetNameSpace(StructDeclarationSyntax structSymbol)
371+
{
372+
// determine the namespace the struct is declared in, if any
373+
SyntaxNode? potentialNamespaceParent = structSymbol.Parent;
374+
while (potentialNamespaceParent != null &&
375+
potentialNamespaceParent is not NamespaceDeclarationSyntax
376+
&& potentialNamespaceParent is not FileScopedNamespaceDeclarationSyntax)
377+
{
378+
potentialNamespaceParent = potentialNamespaceParent.Parent;
379+
}
380+
381+
if (potentialNamespaceParent is BaseNamespaceDeclarationSyntax namespaceParent)
382+
{
383+
string nameSpace = namespaceParent.Name.ToString();
384+
while (true)
385+
{
386+
if(namespaceParent.Parent is not NamespaceDeclarationSyntax namespaceParentParent)
387+
{
388+
break;
389+
}
390+
391+
namespaceParent = namespaceParentParent;
392+
nameSpace = $"{namespaceParent.Name}.{nameSpace}";
393+
}
394+
395+
return nameSpace;
396+
}
397+
return string.Empty;
398+
}
399+
400+
private static ParentClass? GetParentClasses(StructDeclarationSyntax structSymbol)
401+
{
402+
TypeDeclarationSyntax? parentIdClass = structSymbol.Parent as TypeDeclarationSyntax;
403+
ParentClass? parentClass = null;
404+
405+
while (parentIdClass != null && IsAllowedKind(parentIdClass.Kind()))
406+
{
407+
parentClass = new ParentClass(
408+
keyword: parentIdClass.Keyword.ValueText,
409+
name: parentIdClass.Identifier.ToString() + parentIdClass.TypeParameterList,
410+
constraints: parentIdClass.ConstraintClauses.ToString(),
411+
child: parentClass);
412+
413+
parentIdClass = (parentIdClass.Parent as TypeDeclarationSyntax);
414+
}
415+
416+
return parentClass;
417+
418+
static bool IsAllowedKind(SyntaxKind kind) =>
419+
kind == SyntaxKind.ClassDeclaration ||
420+
kind == SyntaxKind.StructDeclaration ||
421+
kind == SyntaxKind.RecordDeclaration;
422+
}
373423
}

src/StronglyTypedIds/SourceGenerationHelper.cs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,16 @@ internal static class SourceGenerationHelper
1010
public static string CreateId(
1111
string idNamespace,
1212
string idName,
13+
ParentClass? parentClass,
1314
StronglyTypedIdConverter converters,
1415
StronglyTypedIdBackingType backingType,
1516
StronglyTypedIdImplementations implementations)
16-
=> CreateId(idNamespace, idName, converters, backingType, implementations, null);
17+
=> CreateId(idNamespace, idName, parentClass, converters, backingType, implementations, null);
1718

1819
public static string CreateId(
1920
string idNamespace,
2021
string idName,
22+
ParentClass? parentClass,
2123
StronglyTypedIdConverter converters,
2224
StronglyTypedIdBackingType backingType,
2325
StronglyTypedIdImplementations implementations,
@@ -33,12 +35,13 @@ public static string CreateId(
3335
_ => throw new ArgumentException("Unknown backing type: " + backingType, nameof(backingType)),
3436
};
3537

36-
return CreateId(idNamespace, idName, converters, implementations, resources, sb);
38+
return CreateId(idNamespace, idName, parentClass, converters, implementations, resources, sb);
3739
}
3840

3941
static string CreateId(
4042
string idNamespace,
4143
string idName,
44+
ParentClass? parentClass,
4245
StronglyTypedIdConverter converters,
4346
StronglyTypedIdImplementations implementations,
4447
EmbeddedSources.ResourceCollection resources,
@@ -70,6 +73,8 @@ static string CreateId(
7073
var useIEquatable = implementations.IsSet(StronglyTypedIdImplementations.IEquatable);
7174
var useIComparable = implementations.IsSet(StronglyTypedIdImplementations.IComparable);
7275

76+
var parentsCount = 0;
77+
7378
sb ??= new StringBuilder();
7479
sb.Append(resources.Header);
7580

@@ -87,6 +92,21 @@ static string CreateId(
8792
{");
8893
}
8994

95+
while (parentClass is not null)
96+
{
97+
sb
98+
.Append(" partial ")
99+
.Append(parentClass.Keyword)
100+
.Append(' ')
101+
.Append(parentClass.Name)
102+
.Append(' ')
103+
.Append(parentClass.Constraints)
104+
.AppendLine(@"
105+
{");
106+
parentsCount++;
107+
parentClass = parentClass.Child;
108+
}
109+
90110
if (useNewtonsoftJson)
91111
{
92112
sb.AppendLine(EmbeddedSources.NewtonsoftJsonAttributeSource);
@@ -139,6 +159,12 @@ static string CreateId(
139159

140160
sb.Replace("TESTID", idName);
141161
sb.AppendLine(@" }");
162+
163+
for (int i = 0; i < parentsCount; i++)
164+
{
165+
sb.AppendLine(@" }");
166+
}
167+
142168
if (hasNamespace)
143169
{
144170
sb.Append('}').AppendLine();

src/StronglyTypedIds/StronglyTypedIdGenerator.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ static void Execute(
5858
return;
5959
}
6060

61-
List<(string Name, string NameSpace, StronglyTypedIdConfiguration Config)> idsToGenerate =
61+
List<(string Name, string NameSpace, StronglyTypedIdConfiguration Config, ParentClass? Parent)> idsToGenerate =
6262
Parser.GetTypesToGenerate(compilation, structs, context.ReportDiagnostic, context.CancellationToken);
6363

6464
if (idsToGenerate.Count > 0)
@@ -72,6 +72,7 @@ static void Execute(
7272
var result = SourceGenerationHelper.CreateId(
7373
idToGenerate.NameSpace,
7474
idToGenerate.Name,
75+
idToGenerate.Parent,
7576
values.Converters,
7677
values.BackingType,
7778
values.Implementations,

0 commit comments

Comments
 (0)