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
20 changes: 15 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,18 @@ does not compile — which makes every claim in `integrals` and `derivatives` ch
contradict is refused by name, with both dimensions written out, rather than emitted as something
broken. Four are refused as the metadata stands.

The check itself now lives in `Semantics.Vocabulary` and both generators run it. C++ refuses the
four; C# reports them as SEM008 and emits the operators anyway, because removing an operator from a
shipped package is a breaking change. So the table below is what C++ does not write **and** what C#
writes with a warning against its name:
The check itself lives in `Semantics.Vocabulary`, and the vocabulary is what both generators emit
*from* rather than merely check against — `QuantityVocabulary.Types` is the list of classes to write
and `QuantityVocabulary.Relationships` the list of operators, on both sides. So a relationship the
exponents refuse is not among the ones there are to write, in either target, and the table below is
simply what neither writes. C# additionally reports each one as SEM008, having somewhere to report
it to; C++ prints them.

The two sides still differ in how many operators one relationship becomes, and that is the language
rather than the physics: the vocabulary states each relationship once, in the direction the metadata
declares it, and the C# generator expands it into the declared direction, its commutation and the
divisions that undo it, because a caller who writes `duration * velocity` is not making a different
claim. That expansion lives in `QuantitiesGenerator.CollectOperators`.

| Refused | Why |
|---|---|
Expand Down Expand Up @@ -289,7 +297,7 @@ var converted = sourceString.As<SourceType, TargetType>();
- **SEM005** — schema-level validation issue in `logarithmic.json` (missing or duplicate scale names, a conversion with no linear type).
- **SEM006** — a metadata file a generator declared in `MetadataFileNames` was not supplied as an `AdditionalFile`. Previously this produced no output and no explanation, which is indistinguishable from a generator that simply had nothing to emit.
- **SEM007** — a metadata file could not be parsed. Replaces the base generator's `CONV001` in category `SourceGenerator`, and covers the path that used to swallow the exception, where a malformed `units.json` silently produced factories with no scale factor.
- **SEM008** — a relationship's declared result does not follow from the dimensions of its operands, or its value is signed and the declared result is a magnitude. The check comes from `Semantics.Vocabulary`, shared with the C++ projection; before that this side checked the names (SEM001) and the forms (SEM003) and then emitted the operator, so `Sensitivity * Pressure -> ElectricPotential` shipped as a working C# operator computing the wrong physics — which is what found that bug, and it is now fixed. The operator is **still emitted** for the four that remain, because dropping one would be a breaking change to a shipped package. Suppressed in `Semantics.Quantities.csproj` because ktsu.Sdk builds warnings as errors and those four are outstanding; `UnkeepableRelationshipTests` pins the set so a fifth fails there rather than disappearing into the suppression.
- **SEM008** — a relationship's declared result does not follow from the dimensions of its operands, or its value is signed and the declared result is a magnitude. The check comes from `Semantics.Vocabulary`, shared with the C++ projection; before that this side checked the names (SEM001) and the forms (SEM003) and then emitted the operator, so `Sensitivity * Pressure -> ElectricPotential` shipped as a working C# operator computing the wrong physics — which is what found that bug, and it is now fixed. **No operator is generated** for a refused relationship, in any of the directions C# spells a product in — that followed from making the vocabulary drive emission rather than only check it, and the removal is documented in `docs/migration-guide-5.0.md`. Suppressed in `Semantics.Quantities.csproj` because ktsu.Sdk builds warnings as errors and the four below are outstanding; `UnkeepableRelationshipTests` pins the set, and asserts that none of them is in the compiled surface, so a fifth fails there rather than disappearing into the suppression.
- Descriptors are allocated from `SemanticsDiagnostics`, which is the one place to add a new one. `AnalyzerReleaseTrackingTests` fails if the identifier is missing from `AnalyzerReleases.Unshipped.md`, so RS2008 no longer surfaces only after a push.
- See `docs/physics-generator.md` for the full schema and an end-to-end "add a dimension" walk-through.

Expand All @@ -304,3 +312,5 @@ This file is the entry point. For deeper material:
- `docs/migration-guide-2.0.md` — 1.x → 2.0 upgrade guide (renames, namespace moves, behavioral changes).
- `docs/migration-guide-3.0.md` — 2.x → 3.0 upgrade guide (removed first-class .NET type attributes, chord flag enum renames).
- `docs/migration-guide-3.1.md` — 3.0 → 3.1 upgrade guide (JSON converter is now opt-in, `PhysicalConstants` domain fields became generic accessors).
- `docs/migration-guide-4.0.md` — 3.x → 4.0 upgrade guide (every quantity became a `readonly record struct`).
- `docs/migration-guide-5.0.md` — 4.x → 5.0 upgrade guide (the four dimensionally unkeepable relationships no longer generate operators).
4 changes: 3 additions & 1 deletion Semantics.Cpp/MetadataProjection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ [.. dimension.DotProducts.Select(Relationship)],
// A constraint is carried as the flag the vocabulary reads rather than as its value: only a
// strict-positive floor is declared anywhere, and what the vocabulary needs is whether it is
// there. The value itself is the C# generator's business, which is where the guard is emitted.
// Read off the constraint's value rather than off the presence of the object holding it, so a
// constraint of some other kind, when one is added, does not silently turn the strict floor on.
private static OverloadDeclaration Overload(MetadataOverload overload) =>
new(overload.Name, overload.Description, overload.PhysicalConstraints is not null);
new(overload.Name, overload.Description, overload.PhysicalConstraints?.MinExclusive == "0");

private static RelationshipDeclaration Relationship(MetadataRelationship relationship) =>
new(relationship.Other, relationship.Result, [.. relationship.Forms]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,5 @@ namespace ktsu.Semantics.Quantities;
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static Duration<T> operator /(Angle<T> left, AngularSpeed<T> right) => Duration<T>.Create(left.Quantity / right.Quantity);

/// <summary>
/// Multiplies Angle by TorqueMagnitude to produce Energy.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static Energy<T> operator *(Angle<T> left, TorqueMagnitude<T> right) => Energy<T>.Create(left.Quantity * right.Quantity);
}

Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,5 @@ namespace ktsu.Semantics.Quantities;
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static Duration<T> operator /(AngularAccelerationMagnitude<T> left, AngularJerkMagnitude<T> right) => Duration<T>.Create(left.Quantity / right.Quantity);

/// <summary>
/// Multiplies AngularAccelerationMagnitude by MomentOfInertia to produce TorqueMagnitude.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static TorqueMagnitude<T> operator *(AngularAccelerationMagnitude<T> left, MomentOfInertia<T> right) => TorqueMagnitude<T>.Create(left.Quantity * right.Quantity);
}

Original file line number Diff line number Diff line change
Expand Up @@ -121,17 +121,5 @@ namespace ktsu.Semantics.Quantities;
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static TorqueMagnitude<T> operator /(AngularMomentumMagnitude<T> left, Duration<T> right) => TorqueMagnitude<T>.Create(left.Quantity / right.Quantity);

/// <summary>
/// Divides AngularMomentumMagnitude by AngularSpeed to produce MomentOfInertia.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static MomentOfInertia<T> operator /(AngularMomentumMagnitude<T> left, AngularSpeed<T> right) => MomentOfInertia<T>.Create(left.Quantity / right.Quantity);

/// <summary>
/// Divides AngularMomentumMagnitude by MomentOfInertia to produce AngularSpeed.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static AngularSpeed<T> operator /(AngularMomentumMagnitude<T> left, MomentOfInertia<T> right) => AngularSpeed<T>.Create(left.Quantity / right.Quantity);
}

Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,5 @@ namespace ktsu.Semantics.Quantities;
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static Duration<T> operator /(AngularSpeed<T> left, AngularAccelerationMagnitude<T> right) => Duration<T>.Create(left.Quantity / right.Quantity);

/// <summary>
/// Multiplies AngularSpeed by MomentOfInertia to produce AngularMomentumMagnitude.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static AngularMomentumMagnitude<T> operator *(AngularSpeed<T> left, MomentOfInertia<T> right) => AngularMomentumMagnitude<T>.Create(left.Quantity * right.Quantity);
}

Original file line number Diff line number Diff line change
Expand Up @@ -180,18 +180,6 @@ namespace ktsu.Semantics.Quantities;
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static Energy<T> operator -(Energy<T> left, Energy<T> right) => Create(T.Abs(left.Quantity - right.Quantity));

/// <summary>
/// Divides Energy by Angle to produce TorqueMagnitude.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static TorqueMagnitude<T> operator /(Energy<T> left, Angle<T> right) => TorqueMagnitude<T>.Create(left.Quantity / right.Quantity);

/// <summary>
/// Divides Energy by TorqueMagnitude to produce Angle.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static Angle<T> operator /(Energy<T> left, TorqueMagnitude<T> right) => Angle<T>.Create(left.Quantity / right.Quantity);

/// <summary>
/// Divides Energy by Length to produce ForceMagnitude.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,4 @@ public Force2D<T> Normalize()
/// <summary>Force2D * Duration = Momentum2D.</summary>
public static Momentum2D<T> operator *(Force2D<T> left, Duration<T> right) => new() { X = left.X * right.Value, Y = left.Y * right.Value };

/// <summary>Typed dot product: Force2D . Displacement2D = Energy.</summary>
public Energy<T> Dot(Displacement2D<T> other) => Energy<T>.Create((X * other.X) + (Y * other.Y));

};
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,4 @@ public Force3D<T> Normalize()
/// <summary>Force3D * Duration = Momentum3D.</summary>
public static Momentum3D<T> operator *(Force3D<T> left, Duration<T> right) => new() { X = left.X * right.Value, Y = left.Y * right.Value, Z = left.Z * right.Value };

/// <summary>Typed dot product: Force3D . Displacement3D = Energy.</summary>
public Energy<T> Dot(Displacement3D<T> other) => Energy<T>.Create((X * other.X) + (Y * other.Y) + (Z * other.Z));

};
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,4 @@ public Force4D<T> Normalize()
/// <summary>Force4D * Duration = Momentum4D.</summary>
public static Momentum4D<T> operator *(Force4D<T> left, Duration<T> right) => new() { X = left.X * right.Value, Y = left.Y * right.Value, Z = left.Z * right.Value, W = left.W * right.Value };

/// <summary>Typed dot product: Force4D . Displacement4D = Energy.</summary>
public Energy<T> Dot(Displacement4D<T> other) => Energy<T>.Create((X * other.X) + (Y * other.Y) + (Z * other.Z) + (W * other.W));

};
Original file line number Diff line number Diff line change
Expand Up @@ -115,17 +115,5 @@ namespace ktsu.Semantics.Quantities;
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static MomentOfInertia<T> operator -(MomentOfInertia<T> left, MomentOfInertia<T> right) => Create(T.Abs(left.Quantity - right.Quantity));

/// <summary>
/// Multiplies MomentOfInertia by AngularSpeed to produce AngularMomentumMagnitude.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static AngularMomentumMagnitude<T> operator *(MomentOfInertia<T> left, AngularSpeed<T> right) => AngularMomentumMagnitude<T>.Create(left.Quantity * right.Quantity);

/// <summary>
/// Multiplies MomentOfInertia by AngularAccelerationMagnitude to produce TorqueMagnitude.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static TorqueMagnitude<T> operator *(MomentOfInertia<T> left, AngularAccelerationMagnitude<T> right) => TorqueMagnitude<T>.Create(left.Quantity * right.Quantity);
}

Original file line number Diff line number Diff line change
Expand Up @@ -124,28 +124,10 @@ namespace ktsu.Semantics.Quantities;
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static TorqueMagnitude<T> operator -(TorqueMagnitude<T> left, TorqueMagnitude<T> right) => Create(T.Abs(left.Quantity - right.Quantity));

/// <summary>
/// Multiplies TorqueMagnitude by Angle to produce Energy.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static Energy<T> operator *(TorqueMagnitude<T> left, Angle<T> right) => Energy<T>.Create(left.Quantity * right.Quantity);

/// <summary>
/// Multiplies TorqueMagnitude by Duration to produce AngularMomentumMagnitude.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static AngularMomentumMagnitude<T> operator *(TorqueMagnitude<T> left, Duration<T> right) => AngularMomentumMagnitude<T>.Create(left.Quantity * right.Quantity);

/// <summary>
/// Divides TorqueMagnitude by AngularAccelerationMagnitude to produce MomentOfInertia.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static MomentOfInertia<T> operator /(TorqueMagnitude<T> left, AngularAccelerationMagnitude<T> right) => MomentOfInertia<T>.Create(left.Quantity / right.Quantity);

/// <summary>
/// Divides TorqueMagnitude by MomentOfInertia to produce AngularAccelerationMagnitude.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")]
public static AngularAccelerationMagnitude<T> operator /(TorqueMagnitude<T> left, MomentOfInertia<T> right) => AngularAccelerationMagnitude<T>.Create(left.Quantity / right.Quantity);
}

14 changes: 8 additions & 6 deletions Semantics.Quantities/Semantics.Quantities.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@
<PropertyGroup>
<TargetFrameworks>net10.0;net9.0;net8.0</TargetFrameworks>
<!-- SEM008 reports a relationship in dimensions.json whose declared result does not follow
from its operands. Four are outstanding and all four are documented in CLAUDE.md: three are
the "r x F versus tau . theta" contradiction and are provably unfixable by choosing
exponents, and one needs a vector1 form on Energy. ktsu.Sdk builds warnings as errors, so
until those four are settled the diagnostic cannot be left on here.
from its operands, and no operator is generated for it. Four are outstanding and all four
are documented in CLAUDE.md: three are the "r x F versus tau . theta" contradiction and are
provably unfixable by choosing exponents, and one needs a vector1 form on Energy. ktsu.Sdk
builds warnings as errors, so until those four are settled the diagnostic cannot be left on
here.
There were five. Sensitivity * Pressure to ElectricPotential was a plain metadata bug and is
fixed, which is what this line is meant to make happen rather than hide.
It is suppressed only in this project, so a consumer editing metadata still sees it, and
UnkeepableRelationshipTests pins the set to exactly those four: a fifth fails there rather
than disappearing into this line. Delete this when dimensions.json is fixed. -->
UnkeepableRelationshipTests pins the set to exactly those four — and asserts that none of
them is in the compiled surface — so a fifth fails there rather than disappearing into this
line. Delete this when dimensions.json is fixed. -->
<NoWarn>$(NoWarn);CA1716;CA2225;KTSU0003;IDE0032;SEM008</NoWarn>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
Expand Down
Loading
Loading