Skip to content

Commit f4d64f6

Browse files
author
Joanna May
committed
test: add tests, cleanup project, add warnings
1 parent 9d9f4ed commit f4d64f6

57 files changed

Lines changed: 854 additions & 412 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
namespace Chickensoft.LogicBlocks.Generator.Tests.TestCases;
2+
3+
// using Chickensoft.Introspection;
4+
5+
// [Introspective("bad_because_identical")]
6+
// public partial class BadBecauseIdentical;
7+
8+
// [Introspective("bad_because_identical")]
9+
// public partial class BadBecauseAlsoIdentical;
10+
//
11+
// [Introspective("bad_because_generic")]
12+
// public partial class BadBecauseGeneric<T> {
13+
// [Introspective("bad_because_nested_in_generic")]
14+
// public partial class BadBecauseNestedInGeneric {
15+
// [Introspective("bad_because_deeply_nested_in_generic")]
16+
// public partial class BadBecauseDeeplyNestedInGeneric;
17+
// }
18+
// }
19+
20+
// public partial class Visible {
21+
// [Introspective("bad_because_not_public")]
22+
// private partial class BadBecauseNotPublic {
23+
// [Introspective("bad_because_nested_in_not_public")]
24+
// public partial class BadBecauseNestedInNotPublic {
25+
// [Introspective("bad_because_deeply_nested_in_not_public")]
26+
// public partial class BadBecauseDeeplyNestedInNotPublic;
27+
// }
28+
// }
29+
// }

Chickensoft.Introspection.Generator/src/TypeGenerator.cs

Lines changed: 92 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace Chickensoft.LogicBlocks.Generator;
1+
namespace Chickensoft.Introspection.Generator;
22

33
using System;
44
using System.CodeDom.Compiler;
@@ -7,8 +7,8 @@ namespace Chickensoft.LogicBlocks.Generator;
77
using System.IO;
88
using System.Linq;
99
using System.Threading;
10-
using Chickensoft.LogicBlocks.Generator.Types.Models;
11-
using Chickensoft.LogicBlocks.Generator.Utils;
10+
using Chickensoft.Introspection.Generator.Types.Models;
11+
using Chickensoft.Introspection.Generator.Utils;
1212
using Microsoft.CodeAnalysis;
1313
using Microsoft.CodeAnalysis.CSharp;
1414
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -50,7 +50,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) {
5050

5151
// --------------------------------------------------------------------- //
5252
// while (!System.Diagnostics.Debugger.IsAttached) {
53-
// Thread.Sleep(500);
53+
// Thread.Sleep(100);
5454
// }
5555
// System.Diagnostics.Debugger.Break();
5656
// --------------------------------------------------------------------- //
@@ -110,7 +110,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) {
110110
concreteVisibleTypesBuilder.Add(type.FullName, type);
111111
}
112112

113-
if (type.CanGenerateMetatypeInfo) {
113+
if (type.HasIntrospectiveAttribute) {
114114
metatypesBuilder.Add(type.FullName, type);
115115
}
116116

@@ -120,6 +120,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) {
120120
}
121121

122122
return new GenerationData(
123+
AllTypes: uniqueTypes.ToImmutableDictionary(),
123124
Metatypes: metatypesBuilder.ToImmutable(),
124125
VisibleTypes: visibleTypesBuilder.ToImmutable(),
125126
ConcreteVisibleTypes: concreteVisibleTypesBuilder.ToImmutable(),
@@ -133,24 +134,90 @@ public void Initialize(IncrementalGeneratorInitializationContext context) {
133134
SourceProductionContext context,
134135
GenerationData data
135136
) => {
136-
GenerateTypeRegistry(context, data);
137-
OutputMetatypesAndReportDiagnostics(context, data);
137+
if (OutputMetatypesAndReportDiagnostics(context, data)) {
138+
GenerateTypeRegistry(context, data);
139+
}
138140
}
139141
);
140142
}
141143

142-
public static void OutputMetatypesAndReportDiagnostics(
144+
public static bool OutputMetatypesAndReportDiagnostics(
143145
SourceProductionContext context,
144146
GenerationData data
145147
) {
148+
var diagnostics = new List<Diagnostic>();
149+
var seenMetatypeIds = new Dictionary<string, DeclaredType>();
150+
146151
// A metatype is a class generated by the source generator that contains
147152
// information about the class it is generated inside of.
148-
foreach (var type in data.VisibleTypes.Values) {
149-
foreach (var diagnostic in type.Diagnostics) {
150-
context.ReportDiagnostic(diagnostic);
153+
foreach (var type in data.AllTypes.Values) {
154+
if (!type.HasIntrospectiveAttribute) {
155+
continue;
156+
}
157+
158+
if (seenMetatypeIds.TryGetValue(type.Id, out var existingType)) {
159+
// Metatype id's must be unique across all assemblies.
160+
161+
diagnostics.Add(
162+
Diagnostics.TypeDoesNotHaveUniqueId(
163+
type.SyntaxLocation,
164+
type.FullName,
165+
existingType
166+
)
167+
);
168+
169+
continue;
170+
}
171+
172+
seenMetatypeIds.Add(type.Id, type);
173+
174+
var invisibleTypes = type.ValidateTypeAndContainingTypes(
175+
data.AllTypes, (t) => t.IsTopLevelAccessible
176+
).ToArray();
177+
178+
if (invisibleTypes.Length > 0) {
179+
diagnostics.Add(
180+
Diagnostics.TypeNotVisible(
181+
type.SyntaxLocation,
182+
type.FullName,
183+
invisibleTypes
184+
)
185+
);
186+
}
187+
188+
var nonPartialTypes = type.ValidateTypeAndContainingTypes(
189+
data.AllTypes, (t) => t.Reference.IsPartial
190+
).ToArray();
191+
192+
if (nonPartialTypes.Length > 0) {
193+
diagnostics.Add(
194+
Diagnostics.TypeNotFullyPartial(
195+
type.SyntaxLocation,
196+
type.FullName,
197+
nonPartialTypes
198+
)
199+
);
151200
}
152201

153-
if (!type.CanGenerateMetatypeInfo) {
202+
var genericTypes = type.ValidateTypeAndContainingTypes(
203+
data.AllTypes, (t) => t.Reference.TypeParameters.Length == 0
204+
).ToArray();
205+
206+
if (genericTypes.Length > 0) {
207+
diagnostics.Add(
208+
Diagnostics.TypeIsGeneric(
209+
type.SyntaxLocation,
210+
type.FullName,
211+
genericTypes
212+
)
213+
);
214+
}
215+
216+
if (diagnostics.Count > 0 || !type.CanGenerateMetatypeInfo) {
217+
// For one or more of the above reasons, we can't proceed with
218+
// generating metatype information, even though the developer wants us
219+
// to. They will get one or more of the diagnostics above, allowing
220+
// them to fix the issue and repent of their sins.
154221
continue;
155222
}
156223

@@ -194,7 +261,7 @@ GenerationData data
194261

195262
// Add a mixin state bucket to the type itself.
196263
code.WriteLine(
197-
$"public {Constants.DATA_TABLE} MixinState {{ get; }} = new();"
264+
$"public {Constants.MIXIN_BLACKBOARD} MixinState {{ get; }} = new();"
198265
);
199266
code.WriteLine();
200267

@@ -225,6 +292,12 @@ GenerationData data
225292
source: code.InnerWriter.ToString()
226293
);
227294
}
295+
296+
foreach (var diagnostic in diagnostics) {
297+
context.ReportDiagnostic(diagnostic);
298+
}
299+
300+
return diagnostics.Count == 0;
228301
}
229302

230303
private static void WriteFileStart(IndentedTextWriter code) {
@@ -479,28 +552,14 @@ public static DeclaredType ResolveDeclaredTypeInfo(
479552

480553
var diagnostics = new HashSet<Diagnostic>();
481554

482-
if (
483-
hasIntrospectiveAttribute && (
484-
!isTopLevelAccessible || // Must be top-level accessible
485-
!isPartial || // Must be partial
486-
typeParameters.Length > 0 // Must be non-generic
487-
)
488-
) {
489-
diagnostics.Add(
490-
Diagnostics.InvalidIntrospectiveType(
491-
typeDecl,
492-
name
493-
)
494-
);
495-
}
496-
497555
var usings = GetUsings(typeDecl);
498556
var properties = GetProperties(typeDecl);
499557
var attributes = GetAttributes(typeDecl.AttributeLists);
500558
var mixins = GetMixins(typeDecl);
501559

502560
return new DeclaredType(
503561
Reference: reference,
562+
SyntaxLocation: typeDecl.GetLocation(),
504563
Location: location,
505564
Usings: usings,
506565
Kind: kind,
@@ -509,8 +568,7 @@ public static DeclaredType ResolveDeclaredTypeInfo(
509568
IsTopLevelAccessible: isTopLevelAccessible,
510569
Properties: properties,
511570
Attributes: attributes,
512-
Mixins: mixins,
513-
Diagnostics: diagnostics.ToImmutableHashSet()
571+
Mixins: mixins
514572
);
515573
}
516574

@@ -731,7 +789,10 @@ var namespacePart in @namespace.Name.ToString().Split('.').Reverse()
731789
}
732790
}
733791

734-
return new TypeLocation(namespaces, types);
792+
return new TypeLocation(
793+
namespaces.ToImmutableArray(),
794+
types.ToImmutableArray()
795+
);
735796
}
736797

737798
public static ImmutableHashSet<UsingDirective> GetUsings(

Chickensoft.Introspection.Generator/src/models/Construction.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
2-
namespace Chickensoft.LogicBlocks.Generator.Types.Models;
1+
namespace Chickensoft.Introspection.Generator.Types.Models;
32

43
public enum Construction {
54
StaticClass,

Chickensoft.Introspection.Generator/src/models/DeclaredAttribute.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace Chickensoft.LogicBlocks.Generator.Types.Models;
1+
namespace Chickensoft.Introspection.Generator.Types.Models;
22

33
using System.Collections.Immutable;
44

Chickensoft.Introspection.Generator/src/models/DeclaredProperty.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace Chickensoft.LogicBlocks.Generator.Types.Models;
1+
namespace Chickensoft.Introspection.Generator.Types.Models;
22

33
using System.Collections.Immutable;
44

Chickensoft.Introspection.Generator/src/models/DeclaredType.cs

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11

2-
namespace Chickensoft.LogicBlocks.Generator.Types.Models;
2+
namespace Chickensoft.Introspection.Generator.Types.Models;
33

4+
using System;
5+
using System.Collections.Generic;
46
using System.Collections.Immutable;
57
using System.Linq;
68
using Microsoft.CodeAnalysis;
@@ -10,6 +12,8 @@ namespace Chickensoft.LogicBlocks.Generator.Types.Models;
1012
/// </summary>
1113
/// <param name="Reference">Type reference, including the name, construction,
1214
/// type parameters, and whether or not the type is partial.</param>
15+
/// <param name="SyntaxLocation">Syntax node location (used for diagnostics).
16+
/// </param>
1317
/// <param name="Location">Location of the type in the source code, including
1418
/// namespaces and containing types.</param>
1519
/// <param name="Usings">Using directives that are in scope for the type.
@@ -24,10 +28,9 @@ namespace Chickensoft.LogicBlocks.Generator.Types.Models;
2428
/// <param name="Properties">Properties declared on the type.</param>
2529
/// <param name="Attributes">Attributes declared on the type.</param>
2630
/// <param name="Mixins">Mixins that are applied to the type.</param>
27-
/// <param name="Diagnostics">Diagnostics that were generated during generator
28-
/// transformation.</param>
2931
public record DeclaredType(
3032
TypeReference Reference,
33+
Location SyntaxLocation,
3134
TypeLocation Location,
3235
ImmutableHashSet<UsingDirective> Usings,
3336
DeclaredTypeKind Kind,
@@ -36,8 +39,7 @@ public record DeclaredType(
3639
bool IsTopLevelAccessible,
3740
ImmutableArray<DeclaredProperty> Properties,
3841
ImmutableArray<DeclaredAttribute> Attributes,
39-
ImmutableArray<string> Mixins,
40-
ImmutableHashSet<Diagnostic> Diagnostics
42+
ImmutableArray<string> Mixins
4143
) {
4244
/// <summary>Output filename (only works for non-generic types).</summary>
4345
public string Filename => FullName.Replace('.', '_');
@@ -70,6 +72,41 @@ ImmutableHashSet<Diagnostic> Diagnostics
7072
public string Id => IntrospectiveAttribute?.ConstructorArgs.FirstOrDefault()
7173
?? $"nameof({Reference.NameWithOpenGenerics})";
7274

75+
/// <summary>
76+
/// Validates that the DeclaredType of this type and its containing types
77+
/// satisfy the given predicate. Returns a list of types that do not satisfy
78+
/// the predicate.
79+
/// </summary>
80+
/// <param name="allTypes">Table of type full names with open generics to
81+
/// the declared type they represent.</param>
82+
/// <param name="predicate">Predicate each type must satisfy.</param>
83+
/// <returns>Enumerable of types that do not satisfy the predicate.</returns>
84+
public IEnumerable<DeclaredType> ValidateTypeAndContainingTypes(
85+
IDictionary<string, DeclaredType> allTypes,
86+
Func<DeclaredType, bool> predicate
87+
) {
88+
// Have to reconstruct the full names of the containing types from our
89+
// type reference and location information.
90+
var fullName = Location.Namespace;
91+
var containingTypeFullNames = new Dictionary<TypeReference, string>();
92+
93+
foreach (var containingType in Location.ContainingTypes) {
94+
fullName +=
95+
(fullName.Length == 0 ? "" : ".") +
96+
containingType.NameWithOpenGenerics;
97+
98+
containingTypeFullNames[containingType] = fullName;
99+
}
100+
101+
var typesToValidate =
102+
new[] { this }.Concat(Location.ContainingTypes.Select(
103+
(typeRef) => allTypes[containingTypeFullNames[typeRef]]
104+
)
105+
);
106+
107+
return typesToValidate.Where((type) => !predicate(type));
108+
}
109+
73110
private DeclaredAttribute? IntrospectiveAttribute => Attributes
74111
.FirstOrDefault(
75112
(attr) => attr.Name == Constants.INTROSPECTIVE_ATTRIBUTE_NAME
@@ -86,6 +123,7 @@ public DeclaredType MergePartialDefinition(
86123
DeclaredType declaredType
87124
) => new(
88125
Reference.MergePartialDefinition(declaredType.Reference),
126+
HasIntrospectiveAttribute ? SyntaxLocation : declaredType.SyntaxLocation,
89127
Location,
90128
Usings.Union(declaredType.Usings),
91129
Kind,
@@ -97,7 +135,6 @@ DeclaredType declaredType
97135
.Union(declaredType.Properties)
98136
.ToImmutableArray(),
99137
Attributes.Concat(declaredType.Attributes).ToImmutableArray(),
100-
Mixins.Concat(declaredType.Mixins).ToImmutableArray(),
101-
Diagnostics.Union(declaredType.Diagnostics)
138+
Mixins.Concat(declaredType.Mixins).ToImmutableArray()
102139
);
103140
}

Chickensoft.Introspection.Generator/src/models/DeclaredTypeKind.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace Chickensoft.LogicBlocks.Generator.Types.Models;
1+
namespace Chickensoft.Introspection.Generator.Types.Models;
22

33
public enum DeclaredTypeKind {
44
StaticClass,

Chickensoft.Introspection.Generator/src/models/GenerationData.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
namespace Chickensoft.LogicBlocks.Generator.Types.Models;
1+
namespace Chickensoft.Introspection.Generator.Types.Models;
22

33
using System.Collections.Immutable;
44

55
/// <summary>
66
/// Data used to generate source output. Represents the finished work of the
77
/// generator before it is converted into source code output.
88
/// </summary>
9+
/// <param name="AllTypes">All identified types.</param>
910
/// <param name="Metatypes">Valid metatypes that were found in the project's
1011
/// source code.</param>
1112
/// <param name="VisibleTypes">Declared types that are visible at the top-level
@@ -16,6 +17,7 @@ namespace Chickensoft.LogicBlocks.Generator.Types.Models;
1617
/// <param name="Mixins">Map of declared types that are marked with the mixin
1718
/// attribute by their type name.</param>
1819
public record GenerationData(
20+
ImmutableDictionary<string, DeclaredType> AllTypes,
1921
ImmutableDictionary<string, DeclaredType> Metatypes,
2022
ImmutableDictionary<string, DeclaredType> VisibleTypes,
2123
ImmutableDictionary<string, DeclaredType> ConcreteVisibleTypes,

0 commit comments

Comments
 (0)