Skip to content

Commit c74f567

Browse files
committed
feat(generator): emit SEM001 when relationship references unknown dimension
When dimensions.json declares an integral / derivative / dotProduct / crossProduct that targets a dimension that doesn't exist (typo, stale name after a rename), QuantitiesGenerator silently dropped the operator. Typos were invisible until someone noticed the missing operator at the call site. Now: emit a Warning-severity SEM001 diagnostic naming the owning dimension, the unresolved reference, and the metadata path, e.g. warning SEM001: Dimension 'Force' references unknown dimension 'Lengt' in integrals[Lengt -> Energy].other; the operator will not be generated. Check spelling and that the referenced dimension exists in dimensions.json. The operator is still skipped (build doesn't fail) so existing valid trees keep working, but typos surface in build output where they're noticeable. Closes #56.
1 parent 5452dba commit c74f567

1 file changed

Lines changed: 30 additions & 4 deletions

File tree

Semantics.SourceGenerators/Generators/QuantitiesGenerator.cs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ namespace Semantics.SourceGenerators;
2020
[Generator]
2121
public class QuantitiesGenerator : GeneratorBase<DimensionsMetadata>
2222
{
23+
private static readonly DiagnosticDescriptor UnknownDimensionReference = new(
24+
id: "SEM001",
25+
title: "Unknown dimension reference in physics relationship",
26+
messageFormat: "Dimension '{0}' references unknown dimension '{1}' in {2}; the operator will not be generated. Check spelling and that the referenced dimension exists in dimensions.json.",
27+
category: "Semantics.SourceGenerators",
28+
defaultSeverity: DiagnosticSeverity.Warning,
29+
isEnabledByDefault: true);
30+
2331
public QuantitiesGenerator() : base("dimensions.json") { }
2432

2533
protected override void Generate(SourceProductionContext context, DimensionsMetadata metadata, CodeBlocker codeBlocker)
@@ -32,8 +40,8 @@ protected override void Generate(SourceProductionContext context, DimensionsMeta
3240
// Phase A: Build maps and collect operators
3341
Dictionary<string, PhysicalDimension> dimensionMap = BuildDimensionMap(metadata);
3442
Dictionary<string, int> typeFormMap = BuildTypeFormMap(metadata);
35-
List<OperatorInfo> allOperators = CollectAllOperators(metadata, dimensionMap);
36-
List<ProductInfo> allProducts = CollectAllProducts(metadata, dimensionMap);
43+
List<OperatorInfo> allOperators = CollectAllOperators(context, metadata, dimensionMap);
44+
List<ProductInfo> allProducts = CollectAllProducts(context, metadata, dimensionMap);
3745
Dictionary<string, List<OperatorInfo>> operatorsByOwner = GroupBy(allOperators, o => o.OwnerTypeName);
3846
Dictionary<string, List<ProductInfo>> productsByOwner = GroupBy(allProducts, p => p.SelfTypeName);
3947

@@ -112,7 +120,7 @@ private static Dictionary<string, int> BuildTypeFormMap(DimensionsMetadata metad
112120
return map;
113121
}
114122

115-
private static List<OperatorInfo> CollectAllOperators(DimensionsMetadata metadata, Dictionary<string, PhysicalDimension> dimMap)
123+
private static List<OperatorInfo> CollectAllOperators(SourceProductionContext context, DimensionsMetadata metadata, Dictionary<string, PhysicalDimension> dimMap)
116124
{
117125
HashSet<string> seen = [];
118126
List<OperatorInfo> result = [];
@@ -124,11 +132,13 @@ private static List<OperatorInfo> CollectAllOperators(DimensionsMetadata metadat
124132
{
125133
if (!dimMap.TryGetValue(integral.Other, out PhysicalDimension? otherDim))
126134
{
135+
ReportUnknownReference(context, dim.Name, integral.Other, $"integrals[{integral.Other} -> {integral.Result}].other");
127136
continue;
128137
}
129138

130139
if (!dimMap.TryGetValue(integral.Result, out PhysicalDimension? resultDim))
131140
{
141+
ReportUnknownReference(context, dim.Name, integral.Result, $"integrals[{integral.Other} -> {integral.Result}].result");
132142
continue;
133143
}
134144

@@ -168,11 +178,13 @@ private static List<OperatorInfo> CollectAllOperators(DimensionsMetadata metadat
168178
{
169179
if (!dimMap.TryGetValue(derivative.Other, out PhysicalDimension? otherDim))
170180
{
181+
ReportUnknownReference(context, dim.Name, derivative.Other, $"derivatives[{derivative.Other} -> {derivative.Result}].other");
171182
continue;
172183
}
173184

174185
if (!dimMap.TryGetValue(derivative.Result, out PhysicalDimension? resultDim))
175186
{
187+
ReportUnknownReference(context, dim.Name, derivative.Result, $"derivatives[{derivative.Other} -> {derivative.Result}].result");
176188
continue;
177189
}
178190

@@ -205,7 +217,7 @@ private static List<OperatorInfo> CollectAllOperators(DimensionsMetadata metadat
205217
return result;
206218
}
207219

208-
private static List<ProductInfo> CollectAllProducts(DimensionsMetadata metadata, Dictionary<string, PhysicalDimension> dimMap)
220+
private static List<ProductInfo> CollectAllProducts(SourceProductionContext context, DimensionsMetadata metadata, Dictionary<string, PhysicalDimension> dimMap)
209221
{
210222
HashSet<string> seen = [];
211223
List<ProductInfo> result = [];
@@ -217,11 +229,13 @@ private static List<ProductInfo> CollectAllProducts(DimensionsMetadata metadata,
217229
{
218230
if (!dimMap.TryGetValue(dot.Other, out PhysicalDimension? otherDim))
219231
{
232+
ReportUnknownReference(context, dim.Name, dot.Other, $"dotProducts[{dot.Other} -> {dot.Result}].other");
220233
continue;
221234
}
222235

223236
if (!dimMap.TryGetValue(dot.Result, out PhysicalDimension? resultDim))
224237
{
238+
ReportUnknownReference(context, dim.Name, dot.Result, $"dotProducts[{dot.Other} -> {dot.Result}].result");
225239
continue;
226240
}
227241

@@ -255,11 +269,13 @@ private static List<ProductInfo> CollectAllProducts(DimensionsMetadata metadata,
255269
{
256270
if (!dimMap.TryGetValue(cross.Other, out PhysicalDimension? otherDim))
257271
{
272+
ReportUnknownReference(context, dim.Name, cross.Other, $"crossProducts[{cross.Other} -> {cross.Result}].other");
258273
continue;
259274
}
260275

261276
if (!dimMap.TryGetValue(cross.Result, out PhysicalDimension? resultDim))
262277
{
278+
ReportUnknownReference(context, dim.Name, cross.Result, $"crossProducts[{cross.Other} -> {cross.Result}].result");
263279
continue;
264280
}
265281

@@ -297,6 +313,16 @@ private static void AddOp(List<OperatorInfo> list, HashSet<string> seen, string
297313
}
298314
}
299315

316+
private static void ReportUnknownReference(SourceProductionContext context, string owningDimension, string unknownReference, string fieldPath)
317+
{
318+
context.ReportDiagnostic(Diagnostic.Create(
319+
UnknownDimensionReference,
320+
Location.None,
321+
owningDimension,
322+
unknownReference,
323+
fieldPath));
324+
}
325+
300326
private static Dictionary<string, List<T>> GroupBy<T>(List<T> items, Func<T, string> keySelector)
301327
{
302328
Dictionary<string, List<T>> groups = [];

0 commit comments

Comments
 (0)