Skip to content

Commit 6806b63

Browse files
committed
feat(generator): add SEM002 metadata validation; refresh stale generator output
- Adds DimensionsMetadata.Validate() and a SEM002 Roslyn diagnostic so malformed entries in dimensions.json (missing name/symbol, empty availableUnits, duplicate type names, no vector forms declared) surface in the build log instead of crashing the emit pass or dropping output silently. Pairs with the existing SEM001 (closes #60). - Adds AnalyzerReleases.Shipped/Unshipped.md so SEM001/SEM002 satisfy RS2008 analyzer release tracking; the generator project now builds clean. - Re-runs QuantitiesGenerator to refresh 132 committed .g.cs files that were stale relative to the V0 non-negativity / absolute V0-V0 subtraction work merged in #66/#68. Diff is purely the documented #50/#52 changes (Vector0Guards.EnsureNonNegative in factories; same-type T.Abs subtraction replacing the old V1-typed result). - CLAUDE.md: corrects the V0 invariant entry (constraints are enforced structurally, not yet declared in metadata), fixes the PhysicalConstants surface to match what's actually emitted (no Conversion sub-class), and lists the SEM00x diagnostics.
1 parent 0ee52bd commit 6806b63

137 files changed

Lines changed: 755 additions & 240 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.

CLAUDE.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,19 +51,25 @@ These are now baked into the generator and enforced by tests. **Do not reopen wi
5151
1. **`V0 - V0` returns the same `V0` of `T.Abs(a - b)`.** Magnitude subtraction stays non-negative; signed subtraction must use the V1 form explicitly.
5252
2. **Dimensionless and angular quantities have both `Ratio` (V0) and `SignedRatio` (V1) bases.** Ratios that semantically must be non-negative (e.g. `RefractiveIndex`, `MachNumber`, `SpecificGravity`) are V0 overloads of `Ratio`.
5353
3. **Semantic overloads widen implicitly to their base, narrow explicitly from it.** A `Weight` is implicitly a `ForceMagnitude`; the reverse requires `Weight.From(forceMagnitude)` or an explicit cast.
54-
4. **Physical constraints come from per-dimension metadata.** Floors like absolute zero or non-negative frequency are declared in `dimensions.json` and the generator emits `ArgumentException`-throwing guards inside the `Create`/`From*` factories.
54+
4. **Physical constraints are enforced structurally via the V0 (magnitude) form.** `Vector0` factories run `Vector0Guards.EnsureNonNegative` and throw `ArgumentException` on a negative value. That covers absolute zero (Temperature is V0, so Kelvin must be ≥ 0), non-negative frequency, non-negative absolute pressure, etc. Strict-positive or upper-bound constraints are not yet declared in metadata (tracked separately).
5555

5656
### Physical constants
5757

58-
`PhysicalConstants` is **generated** from `dimensions.json` (and a constants fixture). Public surface:
58+
`PhysicalConstants` is **generated** from `domains.json`. Public surface:
5959

6060
```csharp
61+
// Domain-grouped PreciseNumber values:
62+
PhysicalConstants.Fundamental.SpeedOfLight
63+
PhysicalConstants.Fundamental.PlanckConstant
64+
PhysicalConstants.AngularMechanics.DegreesPerRadian
65+
66+
// Generic accessors that materialise into any T : INumber<T>:
6167
PhysicalConstants.Generic.SpeedOfLight<T>()
6268
PhysicalConstants.Generic.PlanckConstant<T>()
63-
PhysicalConstants.Conversion.FeetToMeters<T>()
69+
PhysicalConstants.Generic.DegreesPerRadian<T>()
6470
```
6571

66-
Use these accessors instead of hard-coded numerics. Backing values are stored as `PreciseNumber` and converted with `T.CreateChecked` per call.
72+
Backing values are stored as `PreciseNumber` and converted with `T.CreateChecked` per call.
6773

6874
### Operators and physics relationships
6975

@@ -139,6 +145,9 @@ var converted = sourceString.As<SourceType, TargetType>();
139145
- Edit `Semantics.SourceGenerators/Metadata/dimensions.json` to add a dimension, vector form, semantic overload, or relationship.
140146
- Rebuild `Semantics.SourceGenerators` and the consuming `Semantics.Quantities` project; emitted files appear in `Semantics.Quantities/Generated/Semantics.SourceGenerators/<GeneratorName>/`.
141147
- Treat generator output as committed source. Diff it before commit so accidental regressions are visible.
148+
- Generator diagnostics:
149+
- **SEM001** — a relationship in `dimensions.json` references a dimension that does not exist (typo or rename). The operator is silently dropped.
150+
- **SEM002** — schema-level validation issue (missing `name`/`symbol`, empty `availableUnits`, duplicate type names, no vector forms declared).
142151
- See `docs/physics-generator.md` for the full schema and an end-to-end "add a dimension" walk-through.
143152

144153
This file is the entry point. For deeper material:

Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AbsorbedDose.g.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ public record AbsorbedDose<T> : PhysicalQuantity<AbsorbedDose<T>, T>, IVector0<A
2222
/// </summary>
2323
/// <param name="value">The value in Gray.</param>
2424
/// <returns>A new <see cref="AbsorbedDose{T}"/> instance.</returns>
25-
public static AbsorbedDose<T> FromGray(T value) => Create(value);
25+
/// <exception cref="System.ArgumentException">Thrown when the resulting magnitude would be negative.</exception>
26+
public static AbsorbedDose<T> FromGray(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value)));
27+
/// <summary>
28+
/// Subtracts two AbsorbedDose values, returning the absolute difference as a non-negative AbsorbedDose.
29+
/// Magnitude subtraction stays a magnitude (per the unified-vector model).
30+
/// </summary>
31+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AbsorbedDose<T> operator -(AbsorbedDose<T> left, AbsorbedDose<T> right) => Create(T.Abs(left.Quantity - right.Quantity));
2632
};
2733

Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AccelerationMagnitude.g.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ public record AccelerationMagnitude<T> : PhysicalQuantity<AccelerationMagnitude<
2222
/// </summary>
2323
/// <param name="value">The value in MetersPerSecondSquared.</param>
2424
/// <returns>A new <see cref="AccelerationMagnitude{T}"/> instance.</returns>
25-
public static AccelerationMagnitude<T> FromMetersPerSecondSquared(T value) => Create(value);
25+
/// <exception cref="System.ArgumentException">Thrown when the resulting magnitude would be negative.</exception>
26+
public static AccelerationMagnitude<T> FromMetersPerSecondSquared(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value)));
2627
/// <summary>
27-
/// Subtracts two AccelerationMagnitude values, returning a signed Acceleration1D result.
28+
/// Subtracts two AccelerationMagnitude values, returning the absolute difference as a non-negative AccelerationMagnitude.
29+
/// Magnitude subtraction stays a magnitude (per the unified-vector model).
2830
/// </summary>
29-
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Acceleration1D<T> operator -(AccelerationMagnitude<T> left, AccelerationMagnitude<T> right) => Acceleration1D<T>.Create(left.Quantity - right.Quantity);
31+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AccelerationMagnitude<T> operator -(AccelerationMagnitude<T> left, AccelerationMagnitude<T> right) => Create(T.Abs(left.Quantity - right.Quantity));
3032
/// <summary>
3133
/// Multiplies AccelerationMagnitude by Mass to produce ForceMagnitude.
3234
/// </summary>

Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AcousticImpedance.g.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ public record AcousticImpedance<T> : PhysicalQuantity<AcousticImpedance<T>, T>,
2222
/// </summary>
2323
/// <param name="value">The value in PascalSecondPerMeter.</param>
2424
/// <returns>A new <see cref="AcousticImpedance{T}"/> instance.</returns>
25-
public static AcousticImpedance<T> FromPascalSecondPerMeter(T value) => Create(value);
25+
/// <exception cref="System.ArgumentException">Thrown when the resulting magnitude would be negative.</exception>
26+
public static AcousticImpedance<T> FromPascalSecondPerMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value)));
27+
/// <summary>
28+
/// Subtracts two AcousticImpedance values, returning the absolute difference as a non-negative AcousticImpedance.
29+
/// Magnitude subtraction stays a magnitude (per the unified-vector model).
30+
/// </summary>
31+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AcousticImpedance<T> operator -(AcousticImpedance<T> left, AcousticImpedance<T> right) => Create(T.Abs(left.Quantity - right.Quantity));
2632
};
2733

Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ActivationEnergy.g.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@ public record ActivationEnergy<T> : PhysicalQuantity<ActivationEnergy<T>, T>, IV
1919
public static ActivationEnergy<T> Zero => Create(T.Zero);
2020

2121
/// <summary>Creates a new ActivationEnergy from a value in JoulePerMole.</summary>
22-
public static ActivationEnergy<T> FromJoulePerMole(T value) => Create(value);
22+
public static ActivationEnergy<T> FromJoulePerMole(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value)));
2323
/// <summary>Implicit conversion to MolarEnergy.</summary>
2424
public static implicit operator MolarEnergy<T>(ActivationEnergy<T> value) => MolarEnergy<T>.Create(value.Value);
2525
/// <summary>Explicit conversion from MolarEnergy.</summary>
2626
public static explicit operator ActivationEnergy<T>(MolarEnergy<T> value) => Create(value.Value);
2727
/// <summary>Creates a ActivationEnergy from a MolarEnergy value.</summary>
2828
public static ActivationEnergy<T> From(MolarEnergy<T> value) => Create(value.Value);
29+
/// <summary>Subtracts two ActivationEnergy values, returning the absolute difference as a non-negative ActivationEnergy.</summary>
30+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ActivationEnergy<T> operator -(ActivationEnergy<T> left, ActivationEnergy<T> right) => Create(T.Abs(left.Quantity - right.Quantity));
2931
};
3032

Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Admittance.g.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@ public record Admittance<T> : PhysicalQuantity<Admittance<T>, T>, IVector0<Admit
1919
public static Admittance<T> Zero => Create(T.Zero);
2020

2121
/// <summary>Creates a new Admittance from a value in Siemens.</summary>
22-
public static Admittance<T> FromSiemens(T value) => Create(value);
22+
public static Admittance<T> FromSiemens(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value)));
2323
/// <summary>Implicit conversion to Conductance.</summary>
2424
public static implicit operator Conductance<T>(Admittance<T> value) => Conductance<T>.Create(value.Value);
2525
/// <summary>Explicit conversion from Conductance.</summary>
2626
public static explicit operator Admittance<T>(Conductance<T> value) => Create(value.Value);
2727
/// <summary>Creates a Admittance from a Conductance value.</summary>
2828
public static Admittance<T> From(Conductance<T> value) => Create(value.Value);
29+
/// <summary>Subtracts two Admittance values, returning the absolute difference as a non-negative Admittance.</summary>
30+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Admittance<T> operator -(Admittance<T> left, Admittance<T> right) => Create(T.Abs(left.Quantity - right.Quantity));
2931
};
3032

Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Airspeed.g.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ public record Airspeed<T> : PhysicalQuantity<Airspeed<T>, T>, IVector0<Airspeed<
1919
public static Airspeed<T> Zero => Create(T.Zero);
2020

2121
/// <summary>Creates a new Airspeed from a value in MetersPerSecond.</summary>
22-
public static Airspeed<T> FromMetersPerSecond(T value) => Create(value);
22+
public static Airspeed<T> FromMetersPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value)));
2323
/// <summary>Implicit conversion to Speed.</summary>
2424
public static implicit operator Speed<T>(Airspeed<T> value) => Speed<T>.Create(value.Value);
2525
/// <summary>Explicit conversion from Speed.</summary>
2626
public static explicit operator Airspeed<T>(Speed<T> value) => Create(value.Value);
2727
/// <summary>Creates a Airspeed from a Speed value.</summary>
2828
public static Airspeed<T> From(Speed<T> value) => Create(value.Value);
29-
/// <summary>Subtracts two Airspeed values, returning a signed Velocity1D result.</summary>
30-
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Velocity1D<T> operator -(Airspeed<T> left, Airspeed<T> right) => Velocity1D<T>.Create(left.Quantity - right.Quantity);
29+
/// <summary>Subtracts two Airspeed values, returning the absolute difference as a non-negative Airspeed.</summary>
30+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Airspeed<T> operator -(Airspeed<T> left, Airspeed<T> right) => Create(T.Abs(left.Quantity - right.Quantity));
3131
};
3232

Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Altitude.g.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ public record Altitude<T> : PhysicalQuantity<Altitude<T>, T>, IVector0<Altitude<
1919
public static Altitude<T> Zero => Create(T.Zero);
2020

2121
/// <summary>Creates a new Altitude from a value in Meter.</summary>
22-
public static Altitude<T> FromMeter(T value) => Create(value);
22+
public static Altitude<T> FromMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value)));
2323
/// <summary>Implicit conversion to Length.</summary>
2424
public static implicit operator Length<T>(Altitude<T> value) => Length<T>.Create(value.Value);
2525
/// <summary>Explicit conversion from Length.</summary>
2626
public static explicit operator Altitude<T>(Length<T> value) => Create(value.Value);
2727
/// <summary>Creates a Altitude from a Length value.</summary>
2828
public static Altitude<T> From(Length<T> value) => Create(value.Value);
29-
/// <summary>Subtracts two Altitude values, returning a signed Displacement1D result.</summary>
30-
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Displacement1D<T> operator -(Altitude<T> left, Altitude<T> right) => Displacement1D<T>.Create(left.Quantity - right.Quantity);
29+
/// <summary>Subtracts two Altitude values, returning the absolute difference as a non-negative Altitude.</summary>
30+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Altitude<T> operator -(Altitude<T> left, Altitude<T> right) => Create(T.Abs(left.Quantity - right.Quantity));
3131
};
3232

Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AmountOfSubstance.g.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@ public record AmountOfSubstance<T> : PhysicalQuantity<AmountOfSubstance<T>, T>,
2222
/// </summary>
2323
/// <param name="value">The value in Mole.</param>
2424
/// <returns>A new <see cref="AmountOfSubstance{T}"/> instance.</returns>
25-
public static AmountOfSubstance<T> FromMole(T value) => Create(value);
25+
/// <exception cref="System.ArgumentException">Thrown when the resulting magnitude would be negative.</exception>
26+
public static AmountOfSubstance<T> FromMole(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value)));
27+
/// <summary>
28+
/// Subtracts two AmountOfSubstance values, returning the absolute difference as a non-negative AmountOfSubstance.
29+
/// Magnitude subtraction stays a magnitude (per the unified-vector model).
30+
/// </summary>
31+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AmountOfSubstance<T> operator -(AmountOfSubstance<T> left, AmountOfSubstance<T> right) => Create(T.Abs(left.Quantity - right.Quantity));
2632
/// <summary>
2733
/// Divides AmountOfSubstance by Volume to produce Concentration.
2834
/// </summary>

Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Angle.g.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ public record Angle<T> : PhysicalQuantity<Angle<T>, T>, IVector0<Angle<T>, T>
2222
/// </summary>
2323
/// <param name="value">The value in Radian.</param>
2424
/// <returns>A new <see cref="Angle{T}"/> instance.</returns>
25-
public static Angle<T> FromRadian(T value) => Create(value);
25+
/// <exception cref="System.ArgumentException">Thrown when the resulting magnitude would be negative.</exception>
26+
public static Angle<T> FromRadian(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value)));
2627
/// <summary>
27-
/// Subtracts two Angle values, returning a signed SignedAngle result.
28+
/// Subtracts two Angle values, returning the absolute difference as a non-negative Angle.
29+
/// Magnitude subtraction stays a magnitude (per the unified-vector model).
2830
/// </summary>
29-
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static SignedAngle<T> operator -(Angle<T> left, Angle<T> right) => SignedAngle<T>.Create(left.Quantity - right.Quantity);
31+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Angle<T> operator -(Angle<T> left, Angle<T> right) => Create(T.Abs(left.Quantity - right.Quantity));
3032
/// <summary>
3133
/// Divides Angle by Duration to produce AngularSpeed.
3234
/// </summary>

0 commit comments

Comments
 (0)