Skip to content

Add user-defined constraints#3

Open
tvenclovas96 wants to merge 2 commits intooutfox:mainfrom
tvenclovas96:constraints
Open

Add user-defined constraints#3
tvenclovas96 wants to merge 2 commits intooutfox:mainfrom
tvenclovas96:constraints

Conversation

@tvenclovas96
Copy link

@tvenclovas96 tvenclovas96 commented Feb 8, 2026

I've noticed in my own projects that often when I create a "type alias", it is a constrained version of the wrapped type, e.g:

public record struct Direction(Vector2 Value); // <- should always be normalized, otherwise makes no sense

Usually this involves writing repeat boilerplate involving Debug.Assert within the type constructors/setters, as a simple verification layer during design and testing. In relatively high-performance code, I don't want to be checking for this excessively

This PR adds a "valid" constraint to newtype, injecting a user-defined validation method into the constructor, helping to avoid invariants. Optionally enabled, optionally enabled in release.

Example:

[newtype<Vector2>(ConstraintOptions = NewtypeConstraintOptions.Enabled)]
public readonly partial struct Direction
{
    private static bool IsValid(Vector2 direction)
    {
        // enforce normalized
        const float epsilon = 0.0001f;
        return Math.Abs(direction.LengthSquared() - 1f) < epsilon;
    }
}

Generated code:

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public Direction(global::System.Numerics.Vector2 value)
        {
#if DEBUG
            if (!IsValid(value))
                throw new InvalidOperationException($"Failed validation check when trying to create Direction with Vector2 value: {value}");
#endif
            _value = value;
        }

Includes an analyzer warning if the constraint is enabled but a method is not provided
image

Feedback on API and naming appreciated.

(I was actually originally going the route of making my own source generator for this, but as I tried to implement it I realized the design was growing closer and closer to what newtype already was, so I decided it makes more sense to add on to it instead xd)

Summary by CodeRabbit

  • New Features

    • Attribute gains a new MethodImpl setting and expanded documentation; generation can enforce validation constraints and emit constraint-aware constructors.
  • Tests

    • Added tests covering valid/invalid construction, implicit/forwarded creation, and operations that propagate validation.
  • Diagnostics / Docs

    • New diagnostics report malformed or multiple validators; added release-tracking documentation for analyzer rules.

@coderabbitai
Copy link

coderabbitai bot commented Feb 8, 2026

Walkthrough

Adds opt-in constraint/validation handling and MethodImpl metadata to the newtype generator: new attribute members, model and extractor changes to record validation-method metadata and locations, generator diagnostics for malformed/multiple validators, constraint-aware code emission (constructor validation/throwing, DEBUG gating), tests, and analyzer release metadata.

Changes

Cohort / File(s) Summary
Attribute Source
NewType.Generator/AliasAttributeSource.cs
Expand XML docs for generic newtypeAttribute<T> and add public MethodImpl property to both generic and non-generic attribute declarations (default AggressiveInlining).
Validation Attribute Source
NewType.Generator/AliasValidationAttributeSource.cs
Add generated newtypeValidationAttribute source (IncludeInRelease property) used to mark validation methods.
Model & Options
NewType.Generator/AliasModel.cs, NewType.Generator/AliasModelExtractor.cs
Change AliasModel from readonly record struct to reference record; add LocationInfo? and ConstraintModel; introduce ExtractedOptions and propagate MethodImpl via options; update extractor to locate validation method and populate ConstraintModel.
Generator & Diagnostics
NewType.Generator/AliasGenerator.cs, NewType.Generator/AnalyzerReleases.Unshipped.md, NewType.Generator/AnalyzerReleases.Shipped.md, NewType.Generator/NewType.Generator.csproj
Register validation attribute post-init source, switch ExtractNamedArguments→ExtractedOptions, add diagnostics NEWTYPE001/NEWTYPE002 for malformed/multiple validators, short-circuit generation on invalid/multiple validators, and include analyzer metadata files in project.
Code Emission & Indentation
NewType.Generator/AliasCodeGenerator.cs
Introduce SingleIndent and memberIndent usage; emit multi-line constructors when constraints enabled; add AppendConstraintChecker to emit validation call and throw logic (DEBUG/IncludeInRelease gating); update forwarded constructors and many emission sites to use memberIndent.
Tests & Example Type
NewType.Tests/ConstraintValidationTests.cs, NewType.Tests/Types.cs
Add ConstraintValidationTests covering valid/invalid/implicit/forwarded cases; add Direction newtype with validation helper; minor operator formatting change.

Sequence Diagram

sequenceDiagram
    participant SourceGenerator as Code Generator
    participant AttrParser as Attribute Parser
    participant ModelExtractor as Model Extractor
    participant CodeGen as Constructor CodeGen
    participant Diagnostics as Diagnostic Emitter

    SourceGenerator->>AttrParser: Parse `newtype` attribute (includes Options/MethodImpl)
    AttrParser->>ModelExtractor: Provide attribute args & aliased type
    ModelExtractor->>ModelExtractor: Locate validation method, build ConstraintModel (name, IncludeInRelease, valid/multiple, location)
    ModelExtractor->>SourceGenerator: Return AliasModel with ConstraintModel & ExtractedOptions

    alt Constraints requested and validator invalid/multiple
        SourceGenerator->>Diagnostics: Emit NEWTYPE001 / NEWTYPE002
        SourceGenerator-->>SourceGenerator: Short-circuit generation for this type
    else
        SourceGenerator->>CodeGen: Request constructor emission for AliasModel
        alt Use constraints and valid validator
            CodeGen->>CodeGen: Emit multi-line ctor, call validator, throw if invalid (DEBUG gating / IncludeInRelease)
        else
            CodeGen->>CodeGen: Emit simple single-line value constructor
        end
        CodeGen->>SourceGenerator: Return generated source
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • Incremental generation #1 — Overlapping generator refactor touching AliasGenerator/AliasModel/AliasModelExtractor/AliasCodeGenerator; relevant prior work on model-driven generation that this PR extends.

Poem

🐰 I hop through tokens, checking every line,
I find the validator and mark its sign,
Constructors now guarded, MethodImpl set neat,
Debug ears twitch when errors we meet,
🥕 a tiny rabbit applauds this feat.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.63% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add user-defined constraints' is clear, concise, and accurately describes the main feature being added—a constraint validation system for newtype that allows user-defined validation methods.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
NewType.Generator/AliasModel.cs (1)

11-64: ⚠️ Potential issue | 🟡 Minor

Update the model summary and validation flag naming.

The summary still says there are no Roslyn types, but Location? is now part of the model. Also, validValidationMethod breaks PascalCase consistency; consider HasValidValidationMethod (update usages accordingly).

✏️ Suggested doc tweak
-/// Contains only strings, bools, plain enums, and EquatableArrays — no Roslyn symbols.
+/// Contains only strings, bools, plain enums, EquatableArrays, and Location for diagnostics.
🤖 Fix all issues with AI agents
In `@NewType.Generator/AliasCodeGenerator.cs`:
- Around line 141-173: When _model.IncludeConstraints is true, forwarded
constructors emitted by AppendForwardedConstructor/for each ctor in
_model.ForwardedConstructors currently assign _value directly and bypass
validation; modify AppendForwardedConstructor so forwarded constructors route to
the validated primary constructor (the ctor taking _model.AliasedTypeFullName
and running AppendConstraintChecker) or explicitly call AppendConstraintChecker
before assigning _value, ensuring every path that sets _value (including
constructors generated for ForwardedConstructors) enforces the same IsValid
checks for _model.TypeName.

In `@NewType.Generator/AliasGenerator.cs`:
- Around line 128-142: The diagnostic is being reported after emitting the
generated file; move the validation check so that you call
context.ReportDiagnostic(...) (using MissingIsValidMethodDiagnostic,
model.Location, model.TypeName, model.AliasedTypeFullName) and return if
model.IncludeConstraints && !model.validValidationMethod before calling
context.AddSource(...) (the current call that constructs fileName and invokes
context.AddSource with SourceText.From(source, Encoding.UTF8)); alternatively,
when the validation fails simply skip the context.AddSource(...) path so no
source is emitted for that model.

In `@NewType.Generator/AliasModelExtractor.cs`:
- Around line 365-379: HasValidIsValid currently detects a matching IsValid
method but does not verify it's static, which lets an instance method slip
through and causes generated code that calls IsValid(value) to fail to compile;
update HasValidIsValid to also check that the found IMethodSymbol.IsStatic is
true when selecting members from targetType via
AliasModel.ConstraintValidationMethodSymbol (and keep the other checks: return
type boolean and single parameter type equals aliasedType) so only static
validation methods are accepted.

In `@NewType.Generator/AnalyzerReleases.Unshipped.md`:
- Around line 1-8: The release entry for NEWTYPE001 uses category `<Undetected>`
but the DiagnosticDescriptor in AliasGenerator (check the
DiagnosticDescriptor/constructor in AliasGenerator.cs where the category is set
to "Unknown") declares the category as "Unknown"; update the Unshipped release
table row for NEWTYPE001 in NewType.Generator/AnalyzerReleases.Unshipped.md to
use `Unknown` as the Category so the table reads: NEWTYPE001 | `Unknown` | Error
| AliasGenerator.
🧹 Nitpick comments (5)
NewType.Generator/AliasAttributeSource.cs (1)

38-79: Expose ConstraintOptions on the non-generic attribute for parity.

The non-generic newtypeAttribute is the fallback API; without ConstraintOptions, constraint-enabled aliases can’t be expressed there. Consider adding the same property to keep feature parity.

🧩 Suggested addition (non-generic attribute)
 public NewtypeOptions Options { get; set; }

+/// <summary>Controls which constraint features the generator emits.</summary>
+public NewtypeConstraintOptions ConstraintOptions { get; set; }
+
 /// <summary>
 /// Overrides the MethodImplOptions applied to generated members.
 /// Default is <see cref="global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining"/>.
NewType.Generator/AliasGenerator.cs (1)

15-24: Consider a more descriptive diagnostic category.

The category "Unknown" at line 20 is not informative. Roslyn diagnostic categories help users filter and organize warnings/errors. Consider using a category like "NewType" or "CodeGeneration" to align with the analyzer's purpose.

Suggested change
-            category: "Unknown",
+            category: "NewType",
NewType.Tests/ConstraintValidationTests.cs (2)

7-19: Add explicit assertions to verify test intent.

These tests rely on "no exception thrown" as the success criterion, but this is implicit. Consider adding explicit assertions to make the test intent clear and catch potential regressions (e.g., if the constructor becomes a no-op).

Suggested improvement
 [Fact]
 public void Direction_Valid()
 {
     //doesn't throw
     var dir = new Direction(new Vector2(0, 1));
+    Assert.Equal(new Vector2(0, 1), dir.Value);
 }

 [Fact]
 public void Direction_ValidImplicit()
 {
     //doesn't throw
     Direction dir = new Vector2(0, 1);
+    Assert.Equal(new Vector2(0, 1), dir.Value);
 }

33-39: Add assertion to verify the operation result.

Similar to the construction tests, this test should assert on the result value to verify the operation completed correctly, not just that it didn't throw.

Suggested improvement
 [Fact]
 public void Direction_ForwardedOperation_ValidResult()
 {
     var dir1 = new Direction(new Vector2(0, 1f));
     var dir2 = new Direction(new Vector2(0, 1f));
     var dir3 = dir1 * dir2;
+    // Assert expected result of multiplication
+    Assert.NotNull(dir3);
 }
NewType.Generator/AliasModelExtractor.cs (1)

60-62: Minor: trailing whitespace.

Lines 61-62 have trailing whitespace. Consider removing for cleaner formatting.

Suggested fix
     var useConstraints = (allOptions.ConstraintOptions & OptionsUseConstraints) != 0;
-     
-        
+
     return new AliasModel(

@tvenclovas96
Copy link
Author

You are absolutely correct Mr. AI! (Mostly)

@tvenclovas96
Copy link
Author

Thinking if a separate attribute on the validation method would be more ergonomic/intuitive/clear what is happening 🤔

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
NewType.Generator/AliasAttributeSource.cs (1)

94-120: ⚠️ Potential issue | 🟡 Minor

Non-generic attribute is missing ConstraintOptions property.

The generic newtypeAttribute<T> (line 78) includes the new ConstraintOptions property, but the non-generic newtypeAttribute class does not. This inconsistency means users of the non-generic attribute cannot enable constraint validation.

Proposed fix
         /// <summary>Controls which features the generator emits.</summary>
         public NewtypeOptions Options { get; set; }

+        /// <summary>Controls which constraint features the generator emits.</summary>
+        public NewtypeConstraintOptions ConstraintOptions { get; set; }
+
         /// <summary>
         /// Overrides the MethodImplOptions applied to generated members.
🤖 Fix all issues with AI agents
In `@NewType.Generator/AliasCodeGenerator.cs`:
- Around line 150-163: The constructor body indentation is inconsistent: in
AliasCodeGenerator.cs when _model.IncludeConstraints is true you append the
assignment using three SingleIndent units which ignores memberIndent and
mis-indents types without namespaces; change the code that writes the body (the
AppendConstraintChecker call and the line writing "_value = value;") to build
indentation relative to memberIndent (e.g., append memberIndent then one
SingleIndent before body lines) instead of using SingleIndent repeated three
times so the body is always indented exactly one level deeper than memberIndent;
keep AppendConstraintChecker("SingleIndent", "value") usage but ensure the
subsequent "_value = value;" write uses memberIndent + SingleIndent.
- Around line 690-705: The forwarded constructor uses hardcoded SingleIndent
repetitions which don't respect the current member indentation; change both
branches to build indentation from the existing indent variable plus
SingleIndent(s) instead of appending SingleIndent three times. Specifically, in
the block that calls AppendConstraintChecker and sets _value, replace the
_sb.Append(SingleIndent).Append(SingleIndent).Append(SingleIndent)... call with
something that starts from indent and then appends the needed SingleIndent(s)
(for example _sb.Append(indent).Append(SingleIndent).Append(SingleIndent) before
writing the "_value" assignment), and in the else branch replace the single-line
AppendLine with a properly indented multi-line emission that uses indent +
SingleIndent(s) so the forwarded constructor matches the same member-indent
level; reference AppendConstraintChecker, _sb, indent, SingleIndent,
_model.TypeName and _model.AliasedTypeFullName to locate the lines to change.
- Around line 728-743: AppendConstraintChecker currently hardcodes triple
indentation by calling .Append(indent) three times; instead use the passed-in
indent as the correct "body" indent: emit the if line with a single
.Append(indent).AppendLine($"if (!IsValid({valueName}))"), and emit the throw
line with the body indent plus one more indent (e.g.
.Append(indent).Append(indent).AppendLine(...)) so nesting is correct; keep the
`#if` DEBUG guard logic and the same exception text referencing _model.TypeName
and _model.AliasedTypeMinimalName and write to _sb as before.
🧹 Nitpick comments (6)
NewType.Generator/AliasGenerator.cs (1)

15-24: Consider using a more descriptive diagnostic category.

The diagnostic category is set to "Unknown" (line 20). Using a more specific category like "NewType" or "CodeGeneration" would improve discoverability when filtering diagnostics.

Proposed fix
     private static readonly DiagnosticDescriptor MissingIsValidMethodDiagnostic =
         new DiagnosticDescriptor(
             id: "NEWTYPE001",
             title: "Missing validation method",
             messageFormat: $"Type '{{0}}' uses constraints but does not define a compatible validation method. Expected signature: 'bool {AliasModel.ConstraintValidationMethodSymbol}({{1}})'.",
-            category: "Unknown",
+            category: "NewType",
             DiagnosticSeverity.Error,
             isEnabledByDefault: true,
             description: "A constraint-enabled wrapped type must define a validation method."
         );
NewType.Generator/AliasModel.cs (2)

47-49: Naming convention: validValidationMethod should be ValidValidationMethod.

The property uses camelCase (validValidationMethod) instead of PascalCase, which is the standard C# convention for public properties/fields.

Proposed fix
     bool IncludeConstraints,
     bool DebugOnlyConstraints,
-    bool validValidationMethod,
+    bool ValidValidationMethod,

Note: This requires updating all usages in AliasCodeGenerator.cs and AliasGenerator.cs as well.


19-21: Location stores a Roslyn symbol reference.

The doc comment (line 9) states "no Roslyn symbols," but Location is a Roslyn type. While this is necessary for diagnostic reporting and is acceptable, it's worth noting this deviation from the stated design goal. Consider updating the comment to reflect this exception.

NewType.Tests/ConstraintValidationTests.cs (3)

1-3: Consider adding explicit using System; directive.

InvalidOperationException (used in assertions at lines 31, 37, 43, 60) is in the System namespace. This will compile with implicit usings enabled, but adding an explicit using System; improves clarity and prevents issues if implicit usings are disabled.

 using System.Numerics;
+using System;
 using newtype.tests;
 using Xunit;

7-26: Use discard pattern for "doesn't throw" tests to suppress unused variable warnings.

The variables dir in these tests are intentionally unused—their purpose is to verify construction doesn't throw. Using the discard pattern _ = ... makes this intent explicit and avoids compiler warnings.

♻️ Proposed fix
     [Fact]
     public void Direction_Valid()
     {
         //doesn't throw
-        var dir = new Direction(new Vector2(0, 1));
+        _ = new Direction(new Vector2(0, 1));
     }

     [Fact]
     public void Direction_ValidImplicit()
     {
         //doesn't throw
-        Direction dir = new Vector2(0, 1);
+        Direction _ = new Vector2(0, 1);
     }

     [Fact]
     public void Direction_Valid_Forwarded()
     {
         //doesn't throw
-        var dir = new Direction(0, 1);
+        _ = new Direction(0, 1);
     }

46-52: Discard unused dir3 variable or add an assertion.

Similar to the earlier tests, dir3 is unused. Either discard it to clarify intent or add an assertion to verify the result.

♻️ Proposed fix
     [Fact]
     public void Direction_ForwardedOperation_ValidResult()
     {
         var dir1 = new Direction(new Vector2(0, 1f));
         var dir2 = new Direction(new Vector2(0, 1f));
-        var dir3 = dir1 * dir2;
+        _ = dir1 * dir2;
     }

Comment on lines 150 to 163
if (_model.IncludeConstraints)
{
_sb.AppendLine($"{memberIndent}public {_model.TypeName}({_model.AliasedTypeFullName} value)");
_sb.Append(memberIndent).Append('{').AppendLine();

AppendConstraintChecker(SingleIndent, "value");
_sb.Append(SingleIndent).Append(SingleIndent).Append(SingleIndent).AppendLine("_value = value;");

_sb.Append(memberIndent).Append('}').AppendLine();
}
else
{
_sb.AppendLine($"{memberIndent}public {_model.TypeName}({_model.AliasedTypeFullName} value) => _value = value;");
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Inconsistent indentation in generated constructor body.

The _value = value; assignment (line 156) uses three SingleIndent levels hardcoded, but memberIndent already accounts for namespace depth. When a namespace is present, memberIndent is 8 spaces, and the body should be indented one level deeper (12 spaces). However, SingleIndent * 3 always produces 12 spaces regardless of namespace presence.

For types without a namespace, the member indent is 4 spaces, so body lines should be 8 spaces, but this code emits 12.

Proposed fix
         if (_model.IncludeConstraints)
         {
             _sb.AppendLine($"{memberIndent}public {_model.TypeName}({_model.AliasedTypeFullName} value)");
-            _sb.Append(memberIndent).Append('{').AppendLine();
-            
-            AppendConstraintChecker(SingleIndent, "value");
-            _sb.Append(SingleIndent).Append(SingleIndent).Append(SingleIndent).AppendLine("_value = value;"); 
-            
-            _sb.Append(memberIndent).Append('}').AppendLine();
+            _sb.AppendLine($"{memberIndent}{{");
+
+            var bodyIndent = memberIndent + SingleIndent;
+            AppendConstraintChecker(bodyIndent, "value");
+            _sb.AppendLine($"{bodyIndent}_value = value;");
+
+            _sb.AppendLine($"{memberIndent}}}");
         }
🤖 Prompt for AI Agents
In `@NewType.Generator/AliasCodeGenerator.cs` around lines 150 - 163, The
constructor body indentation is inconsistent: in AliasCodeGenerator.cs when
_model.IncludeConstraints is true you append the assignment using three
SingleIndent units which ignores memberIndent and mis-indents types without
namespaces; change the code that writes the body (the AppendConstraintChecker
call and the line writing "_value = value;") to build indentation relative to
memberIndent (e.g., append memberIndent then one SingleIndent before body lines)
instead of using SingleIndent repeated three times so the body is always
indented exactly one level deeper than memberIndent; keep
AppendConstraintChecker("SingleIndent", "value") usage but ensure the subsequent
"_value = value;" write uses memberIndent + SingleIndent.

@tvenclovas96
Copy link
Author

After playing around with it, using an attribute felt nicer

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
NewType.Generator/AliasGenerator.cs (1)

140-158: Consider using a more specific diagnostic category.

Both ValidatorInvalidDiagnostic and ValidatorMultipleDiagnostic use category: "Unknown" which appears to be a placeholder. Consider using a descriptive category like "NewtypeGenerator" or "Validation" for better diagnostic organization and filtering.

📝 Proposed fix
     private static readonly DiagnosticDescriptor ValidatorInvalidDiagnostic =
         new(
             id: "NEWTYPE001",
             title: "Malformed validation method",
             messageFormat: "Incorrectly formed validation method for type '{0}'. Expected signature: 'bool {1}({2})'.",
-            category: "Unknown",
+            category: "NewtypeGenerator",
             DiagnosticSeverity.Error,
             isEnabledByDefault: true
         );

     private static readonly DiagnosticDescriptor ValidatorMultipleDiagnostic =
         new(
             id: "NEWTYPE002",
             title: "Multiple validators",
             messageFormat: "Only a single validation method should be used",
-            category: "Unknown",
+            category: "NewtypeGenerator",
             DiagnosticSeverity.Error,
             isEnabledByDefault: true
         );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@NewType.Generator/AliasGenerator.cs` around lines 140 - 158, The diagnostics
ValidatorInvalidDiagnostic and ValidatorMultipleDiagnostic currently use a
placeholder category ("Unknown"); update their category argument to a
descriptive string such as "NewtypeGenerator" or "Validation" to improve
filtering and organization — locate the two static fields
ValidatorInvalidDiagnostic and ValidatorMultipleDiagnostic in AliasGenerator.cs
and replace the category value for each DiagnosticDescriptor construction with
the chosen specific category.
NewType.Generator/AliasModel.cs (1)

58-59: ConstraintOptions field in ExtractedOptions appears unused.

ExtractedOptions contains a ConstraintOptions field, but looking at AliasGenerator.ExtractNamedArguments (lines 85-106), this field is always set to DefaultConstraintOptions (0) and is never populated from the attribute's named arguments. Additionally, AliasModelExtractor.Extract doesn't appear to use allOptions.ConstraintOptions.

If this is placeholder for future functionality, consider adding a TODO comment. Otherwise, it should be removed to avoid confusion.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@NewType.Generator/AliasModel.cs` around lines 58 - 59, The ConstraintOptions
field on the ExtractedOptions record struct is unused; remove it to avoid
confusion: delete the ConstraintOptions parameter from the ExtractedOptions
declaration and update all call sites (e.g., the construction in
AliasGenerator.ExtractNamedArguments and any references in
AliasModelExtractor.Extract) to use the new two-parameter (Options, MethodImpl)
shape, and remove DefaultConstraintOptions if it becomes unused; alternatively,
if you intend it as a placeholder, add a clear TODO comment above
ExtractedOptions referencing planned use in AliasGenerator.ExtractNamedArguments
and AliasModelExtractor.Extract instead of leaving it silently unused.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@NewType.Generator/AliasModel.cs`:
- Around line 19-21: AliasModel and ConstraintModel currently store Roslyn
Location objects (Location? Location) which breaks incremental generator
caching; replace those Location fields with plain primitives (e.g., string?
SourceFilePath, int? StartLine, int? StartColumn or a small struct of file path
+ line/column) so the models contain only strings/bools/enums/EquatableArrays,
and remove any nested Roslyn types from ConstraintModel; when you need to report
diagnostics reconstruct a Roslyn Location via Location.Create(...) from the
stored primitives in the diagnostic/emission path rather than keeping Location
in the model.

In `@NewType.Generator/AliasModelExtractor.cs`:
- Around line 370-372: The loop in AliasModelExtractor.cs that iterates
method.GetAttributes() dereferences attributeData.AttributeClass with a
null-forgiving operator; add a null check (e.g., skip/continue when
attributeData.AttributeClass is null) before comparing its Name to
ConstraintAttributeSource.AttributeName so you don't risk a
NullReferenceException; update the comparison to only access AttributeClass.Name
when non-null (or use attributeData.AttributeClass?.Name and compare safely) in
the method that contains this foreach.

In `@NewType.Generator/AliasValidationAttributeSource.cs`:
- Around line 9-10: AttributeNamespace is set to "newtypeValidation" which makes
FullyQualifiedName inconsistent with the actual generated attribute namespace in
Source; update AttributeNamespace to match the namespace used in the generated
attribute (the "newtype" namespace used when emitting the validation attribute)
so FullyQualifiedName (which uses AttributeName) resolves to the real type name,
and double-check AttributeName and FullyQualifiedName still compose correctly
with the generated attribute symbol.
- Around line 16-27: Fix the XML documentation typos on the
newtypeValidationAttribute: change "valdiation" to "validation" in the summary
that describes methods, and update the second summary (currently "Creates a new
alias attribute") to correctly describe that the constructor creates a new
validation attribute for aliased types (or similar wording) so the docs
accurately reflect newtypeValidationAttribute's purpose.

---

Nitpick comments:
In `@NewType.Generator/AliasGenerator.cs`:
- Around line 140-158: The diagnostics ValidatorInvalidDiagnostic and
ValidatorMultipleDiagnostic currently use a placeholder category ("Unknown");
update their category argument to a descriptive string such as
"NewtypeGenerator" or "Validation" to improve filtering and organization —
locate the two static fields ValidatorInvalidDiagnostic and
ValidatorMultipleDiagnostic in AliasGenerator.cs and replace the category value
for each DiagnosticDescriptor construction with the chosen specific category.

In `@NewType.Generator/AliasModel.cs`:
- Around line 58-59: The ConstraintOptions field on the ExtractedOptions record
struct is unused; remove it to avoid confusion: delete the ConstraintOptions
parameter from the ExtractedOptions declaration and update all call sites (e.g.,
the construction in AliasGenerator.ExtractNamedArguments and any references in
AliasModelExtractor.Extract) to use the new two-parameter (Options, MethodImpl)
shape, and remove DefaultConstraintOptions if it becomes unused; alternatively,
if you intend it as a placeholder, add a clear TODO comment above
ExtractedOptions referencing planned use in AliasGenerator.ExtractNamedArguments
and AliasModelExtractor.Extract instead of leaving it silently unused.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7faa613 and 0c886cb.

📒 Files selected for processing (7)
  • NewType.Generator/AliasAttributeSource.cs
  • NewType.Generator/AliasCodeGenerator.cs
  • NewType.Generator/AliasGenerator.cs
  • NewType.Generator/AliasModel.cs
  • NewType.Generator/AliasModelExtractor.cs
  • NewType.Generator/AliasValidationAttributeSource.cs
  • NewType.Tests/Types.cs

Comment on lines 370 to 372
foreach (var attributeData in method.GetAttributes())
{
if (attributeData.AttributeClass!.Name == ConstraintAttributeSource.AttributeName)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential null reference on AttributeClass.

Line 372 uses attributeData.AttributeClass!.Name with a null-forgiving operator. While AttributeClass being null is rare, it can occur for malformed attributes or during error recovery. Consider adding a null check to avoid potential runtime exceptions during compilation.

🛡️ Proposed fix
             foreach (var attributeData in method.GetAttributes())
             {
-                if (attributeData.AttributeClass!.Name == ConstraintAttributeSource.AttributeName)
+                if (attributeData.AttributeClass?.Name == ConstraintAttributeSource.AttributeName)
                 {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
foreach (var attributeData in method.GetAttributes())
{
if (attributeData.AttributeClass!.Name == ConstraintAttributeSource.AttributeName)
foreach (var attributeData in method.GetAttributes())
{
if (attributeData.AttributeClass?.Name == ConstraintAttributeSource.AttributeName)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@NewType.Generator/AliasModelExtractor.cs` around lines 370 - 372, The loop in
AliasModelExtractor.cs that iterates method.GetAttributes() dereferences
attributeData.AttributeClass with a null-forgiving operator; add a null check
(e.g., skip/continue when attributeData.AttributeClass is null) before comparing
its Name to ConstraintAttributeSource.AttributeName so you don't risk a
NullReferenceException; update the comparison to only access AttributeClass.Name
when non-null (or use attributeData.AttributeClass?.Name and compare safely) in
the method that contains this foreach.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (4)
NewType.Generator/AliasModelExtractor.cs (1)

371-373: ⚠️ Potential issue | 🟡 Minor

Potential null reference on AttributeClass not fully addressed.

While the Where(x => x is not null) filter on line 371 ensures attributeData is non-null, attributeData.AttributeClass can still be null for malformed attributes. The null-forgiving operator on line 373 could cause a NullReferenceException.

🛡️ Proposed fix
             foreach (var attributeData in method.GetAttributes().Where(x => x is not null))
             {
-                if (attributeData.AttributeClass!.Name == ConstraintAttributeSource.AttributeName)
+                if (attributeData.AttributeClass?.Name == ConstraintAttributeSource.AttributeName)
                 {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@NewType.Generator/AliasModelExtractor.cs` around lines 371 - 373, The code
assumes attributeData.AttributeClass is non-null when comparing its Name to
ConstraintAttributeSource.AttributeName inside AliasModelExtractor (loop over
method.GetAttributes()); update the check to guard against a null AttributeClass
before accessing Name (for example, use a null-conditional check or an explicit
null check on attributeData.AttributeClass) so the if condition only compares
Name when AttributeClass is non-null and avoids the null-forgiving operator.
NewType.Generator/AliasCodeGenerator.cs (3)

760-777: ⚠️ Potential issue | 🟡 Minor

AppendConstraintChecker uses incorrect indentation approach.

The method receives an indent parameter but then triples it via .Append(indent).Append(indent).Append(indent) (lines 767-769), which doesn't adapt to actual nesting level. The caller should pass the correct body indent, and this method should use it directly.

♻️ Proposed fix
-    private void AppendConstraintChecker(string indent, string valueName)
+    private void AppendConstraintChecker(string bodyIndent, string valueName)
     {
-        if (!_model.ConstraintModel.Valid) return;
+        if (!_model.ConstraintModel.UseConstraints) return;

         if (!_model.ConstraintModel.InRelease)
             _sb.AppendLine("#if DEBUG");

-        _sb.Append(indent).Append(indent).Append(indent)
-            .AppendLine($"if (!{_model.ConstraintModel.ValidationSymbolName}({valueName}))");
-        _sb.Append(indent).Append(indent).Append(indent).Append(indent)
-            .AppendLine(
-                $"throw new InvalidOperationException($\"Failed validation check when trying to create '{_model.TypeName}' with '{_model.AliasedTypeMinimalName}' value: {{{valueName}}}\");");
+        _sb.AppendLine($"{bodyIndent}if (!{_model.ConstraintModel.ValidationSymbolName}({valueName}))");
+        _sb.AppendLine($"{bodyIndent}{SingleIndent}throw new InvalidOperationException($\"Failed validation check when trying to create '{_model.TypeName}' with '{_model.AliasedTypeMinimalName}' value: {{{valueName}}}\");");

         if (!_model.ConstraintModel.InRelease)
             _sb.AppendLine("#endif");
         else
             _sb.AppendLine();
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@NewType.Generator/AliasCodeGenerator.cs` around lines 760 - 777, The method
AppendConstraintChecker currently multiplies the passed indent (using
.Append(indent) three times) instead of using the caller-provided body indent
directly; change AppendConstraintChecker to use the given indent once for the
if-line (use _sb.Append(indent).AppendLine(...)) and use two levels for the
inner throw-line (use _sb.Append(indent).Append(indent).AppendLine(...)) so the
if and its body are indented relative to the body indent the caller passes; keep
all other logic and symbols (_sb, _model.ConstraintModel, ValidationSymbolName,
TypeName, AliasedTypeMinimalName, the `#if` DEBUG guards) unchanged.

150-164: ⚠️ Potential issue | 🟡 Minor

Indentation is hardcoded and ignores namespace depth.

The constructor body uses SingleIndent repeated 3 times (line 155-156), which always produces 12 spaces. However, the correct indentation depends on whether the type has a namespace:

  • With namespace: memberIndent (8 spaces) + SingleIndent (4 spaces) = 12 spaces ✓
  • Without namespace: memberIndent (4 spaces) + SingleIndent (4 spaces) = 8 spaces ✗ (code emits 12)

The generated code will be visually misaligned for types without a namespace.

♻️ Proposed fix
         if (_model.ConstraintModel.UseConstraints)
         {
+            var bodyIndent = memberIndent + SingleIndent;
             _sb.AppendLine($"{memberIndent}public {_model.TypeName}({_model.AliasedTypeFullName} value)");
-            _sb.Append(memberIndent).Append('{').AppendLine();
+            _sb.AppendLine($"{memberIndent}{{");
 
-            AppendConstraintChecker(SingleIndent, "value");
-            _sb.Append(SingleIndent).Append(SingleIndent).Append(SingleIndent).AppendLine("_value = value;");
+            AppendConstraintChecker(bodyIndent, "value");
+            _sb.AppendLine($"{bodyIndent}_value = value;");
 
-            _sb.Append(memberIndent).Append('}').AppendLine();
+            _sb.AppendLine($"{memberIndent}}}");
         }

And update AppendConstraintChecker to use the passed indent directly:

     private void AppendConstraintChecker(string bodyIndent, string valueName)
     {
         if (!_model.ConstraintModel.Valid) return;

         if (!_model.ConstraintModel.InRelease)
             _sb.AppendLine("#if DEBUG");

-        _sb.Append(indent).Append(indent).Append(indent)
-            .AppendLine($"if (!{_model.ConstraintModel.ValidationSymbolName}({valueName}))");
-        _sb.Append(indent).Append(indent).Append(indent).Append(indent)
-            .AppendLine($"throw new InvalidOperationException(...);");
+        _sb.AppendLine($"{bodyIndent}if (!{_model.ConstraintModel.ValidationSymbolName}({valueName}))");
+        _sb.AppendLine($"{bodyIndent}{SingleIndent}throw new InvalidOperationException(...);");

         if (!_model.ConstraintModel.InRelease)
             _sb.AppendLine("#endif");
         else
             _sb.AppendLine();
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@NewType.Generator/AliasCodeGenerator.cs` around lines 150 - 164, The
constructor body indentation is hardcoded by repeating SingleIndent three times
which misaligns code when memberIndent varies (types without namespaces); modify
the constructor generation in AliasCodeGenerator (the block that builds the
public {_model.TypeName}({_model.AliasedTypeFullName} value) constructor) to
compute indentation by concatenating memberIndent with a single SingleIndent
(instead of using SingleIndent repeated 3 times) for the lines inside the
braces, and update AppendConstraintChecker to accept and use the caller-provided
indent string (pass memberIndent + SingleIndent when calling
AppendConstraintChecker) so the constraint checks and the subsequent "_value =
value;" line align correctly regardless of namespace depth.

719-736: ⚠️ Potential issue | 🟡 Minor

Same indentation issue in forwarded constructor.

Lines 727-728 use the same hardcoded triple-indent pattern that doesn't adapt to namespace presence.

♻️ Proposed fix
         if (_model.ConstraintModel.UseConstraints)
         {
             const string valueName = "newValue";
+            var bodyIndent = indent + SingleIndent;
             _sb.AppendLine($"{indent}public {_model.TypeName}({parameters})");
-            _sb.Append(indent).Append('{').AppendLine();
-            _sb.Append(indent).Append(SingleIndent)
-                .AppendLine($"var {valueName} = new {_model.AliasedTypeFullName}({arguments});");
+            _sb.AppendLine($"{indent}{{");
+            _sb.AppendLine($"{bodyIndent}var {valueName} = new {_model.AliasedTypeFullName}({arguments});");

-            AppendConstraintChecker(SingleIndent, valueName);
-            _sb.Append(SingleIndent).Append(SingleIndent).Append(SingleIndent).AppendLine($"_value = {valueName};");
+            AppendConstraintChecker(bodyIndent, valueName);
+            _sb.AppendLine($"{bodyIndent}_value = {valueName};");

-            _sb.Append(indent).Append('}').AppendLine();
+            _sb.AppendLine($"{indent}}}");
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@NewType.Generator/AliasCodeGenerator.cs` around lines 719 - 736, The
constructor generation uses a hardcoded triple SingleIndent when writing the
"_value = ..." assignment (see the block generating public
{_model.TypeName}(...) with constraints and the call to
AppendConstraintChecker), which fails to adapt to the surrounding "indent"
(namespace/class nesting); replace the chained
.Append(SingleIndent).Append(SingleIndent).Append(SingleIndent) with a
composition that begins with the existing indent (e.g.,
.Append(indent).Append(SingleIndent).Append(SingleIndent) or equivalent) so the
assignment line aligns relative to the current indent, and apply the same change
where the forwarded constructor line is emitted so both paths use the indent +
SingleIndent pattern rather than fixed triple SingleIndent.
🧹 Nitpick comments (2)
NewType.Generator/AliasGenerator.cs (1)

138-156: Consider using a more descriptive diagnostic category.

Both diagnostic descriptors use category: "Unknown". Roslyn conventions suggest using a meaningful category like "Usage", "Design", or a project-specific category like "Newtype" for better tooling integration and filtering.

♻️ Suggested improvement
     private static readonly DiagnosticDescriptor ValidatorInvalidDiagnostic =
         new(
             id: "NEWTYPE001",
             title: "Malformed validation method",
             messageFormat: "Incorrectly formed validation method for type '{0}'. Expected signature: 'bool {1}({2})'.",
-            category: "Unknown",
+            category: "Newtype",
             DiagnosticSeverity.Error,
             isEnabledByDefault: true
         );

     private static readonly DiagnosticDescriptor ValidatorMultipleDiagnostic =
         new(
             id: "NEWTYPE002",
             title: "Multiple validators",
             messageFormat: "Only a single validation method should be used",
-            category: "Unknown",
+            category: "Newtype",
             DiagnosticSeverity.Error,
             isEnabledByDefault: true
         );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@NewType.Generator/AliasGenerator.cs` around lines 138 - 156, The diagnostics
ValidatorInvalidDiagnostic and ValidatorMultipleDiagnostic currently use
category "Unknown"; change their category strings to a meaningful value (e.g.
"Usage", "Design", or a project-specific category like "Newtype") to improve
tooling/filtering; update the category parameter in the constructors for
ValidatorInvalidDiagnostic and ValidatorMultipleDiagnostic to the chosen
descriptive category and ensure both descriptors use the same consistent
category name across AliasGenerator.cs.
NewType.Generator/AliasModelExtractor.cs (1)

470-478: Minor: Formatting inconsistency in ternary expression.

Missing space before null on line 478.

♻️ Suggested fix
     private static LocationInfo? ToLocationStruct(Location? location) =>
         location is not null && location.IsInSource ?  
             new LocationInfo(
                 location.SourceTree.FilePath,
                 location.SourceSpan, 
                 new LinePositionSpan(
                     location.GetLineSpan().StartLinePosition, 
                     location.GetLineSpan().EndLinePosition))
-            :null;
+            : null;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@NewType.Generator/AliasModelExtractor.cs` around lines 470 - 478, The ternary
expression in ToLocationStruct has a formatting inconsistency: there's no space
before the trailing null; update the expression inside the ToLocationStruct
method so the conditional's false branch is formatted with a space before null
(i.e., "... : null;") preserving current semantics and keeping the use of
Location, LocationInfo, SourceTree/FilePath, SourceSpan, and LinePositionSpan
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@NewType.Generator/AliasCodeGenerator.cs`:
- Line 762: The guard currently checks _model.ConstraintModel.Valid but should
check _model.ConstraintModel.UseConstraints so code generation only emits
constraint checks when a real validation method exists; change the early return
in the method that contains the line "if (!_model.ConstraintModel.Valid)
return;" to "if (!_model.ConstraintModel.UseConstraints) return;" (ensure any
subsequent logic that references ValidationSymbolName or valueName remains
unchanged so you don't emit "if (!({valueName}))" when no validator exists).

In `@NewType.Generator/AliasValidationAttributeSource.cs`:
- Around line 3-5: The XML summary on AliasValidationAttributeSource.cs
incorrectly calls this the "[Alias] attribute"; update the XML doc comment to
accurately describe the file as the source text for the Alias validation
attribute (e.g., "Source text for the alias validation attribute that gets
injected into user compilations") so it reflects the actual purpose of the
AliasValidationAttributeSource class/file and remove the copy-paste wording from
NewtypeAttributeSource.

---

Duplicate comments:
In `@NewType.Generator/AliasCodeGenerator.cs`:
- Around line 760-777: The method AppendConstraintChecker currently multiplies
the passed indent (using .Append(indent) three times) instead of using the
caller-provided body indent directly; change AppendConstraintChecker to use the
given indent once for the if-line (use _sb.Append(indent).AppendLine(...)) and
use two levels for the inner throw-line (use
_sb.Append(indent).Append(indent).AppendLine(...)) so the if and its body are
indented relative to the body indent the caller passes; keep all other logic and
symbols (_sb, _model.ConstraintModel, ValidationSymbolName, TypeName,
AliasedTypeMinimalName, the `#if` DEBUG guards) unchanged.
- Around line 150-164: The constructor body indentation is hardcoded by
repeating SingleIndent three times which misaligns code when memberIndent varies
(types without namespaces); modify the constructor generation in
AliasCodeGenerator (the block that builds the public
{_model.TypeName}({_model.AliasedTypeFullName} value) constructor) to compute
indentation by concatenating memberIndent with a single SingleIndent (instead of
using SingleIndent repeated 3 times) for the lines inside the braces, and update
AppendConstraintChecker to accept and use the caller-provided indent string
(pass memberIndent + SingleIndent when calling AppendConstraintChecker) so the
constraint checks and the subsequent "_value = value;" line align correctly
regardless of namespace depth.
- Around line 719-736: The constructor generation uses a hardcoded triple
SingleIndent when writing the "_value = ..." assignment (see the block
generating public {_model.TypeName}(...) with constraints and the call to
AppendConstraintChecker), which fails to adapt to the surrounding "indent"
(namespace/class nesting); replace the chained
.Append(SingleIndent).Append(SingleIndent).Append(SingleIndent) with a
composition that begins with the existing indent (e.g.,
.Append(indent).Append(SingleIndent).Append(SingleIndent) or equivalent) so the
assignment line aligns relative to the current indent, and apply the same change
where the forwarded constructor line is emitted so both paths use the indent +
SingleIndent pattern rather than fixed triple SingleIndent.

In `@NewType.Generator/AliasModelExtractor.cs`:
- Around line 371-373: The code assumes attributeData.AttributeClass is non-null
when comparing its Name to ConstraintAttributeSource.AttributeName inside
AliasModelExtractor (loop over method.GetAttributes()); update the check to
guard against a null AttributeClass before accessing Name (for example, use a
null-conditional check or an explicit null check on
attributeData.AttributeClass) so the if condition only compares Name when
AttributeClass is non-null and avoids the null-forgiving operator.

---

Nitpick comments:
In `@NewType.Generator/AliasGenerator.cs`:
- Around line 138-156: The diagnostics ValidatorInvalidDiagnostic and
ValidatorMultipleDiagnostic currently use category "Unknown"; change their
category strings to a meaningful value (e.g. "Usage", "Design", or a
project-specific category like "Newtype") to improve tooling/filtering; update
the category parameter in the constructors for ValidatorInvalidDiagnostic and
ValidatorMultipleDiagnostic to the chosen descriptive category and ensure both
descriptors use the same consistent category name across AliasGenerator.cs.

In `@NewType.Generator/AliasModelExtractor.cs`:
- Around line 470-478: The ternary expression in ToLocationStruct has a
formatting inconsistency: there's no space before the trailing null; update the
expression inside the ToLocationStruct method so the conditional's false branch
is formatted with a space before null (i.e., "... : null;") preserving current
semantics and keeping the use of Location, LocationInfo, SourceTree/FilePath,
SourceSpan, and LinePositionSpan unchanged.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0c886cb and 38b359e.

📒 Files selected for processing (8)
  • NewType.Generator/AliasAttributeSource.cs
  • NewType.Generator/AliasCodeGenerator.cs
  • NewType.Generator/AliasGenerator.cs
  • NewType.Generator/AliasModel.cs
  • NewType.Generator/AliasModelExtractor.cs
  • NewType.Generator/AliasValidationAttributeSource.cs
  • NewType.Generator/AnalyzerReleases.Unshipped.md
  • NewType.Tests/Types.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • NewType.Tests/Types.cs


private void AppendConstraintChecker(string indent, string valueName)
{
if (!_model.ConstraintModel.Valid) return;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard condition checks Valid but should check UseConstraints.

Line 762 checks _model.ConstraintModel.Valid which is true when no validation method exists OR when the method is correctly formed. This means the method will still try to emit constraint code when no validation method exists (ValidationSymbolName is null), causing line 768 to emit if (!({valueName})).

The guard should use UseConstraints which is only true when a valid validation method actually exists.

🐛 Proposed fix
     private void AppendConstraintChecker(string indent, string valueName)
     {
-        if (!_model.ConstraintModel.Valid) return;
+        if (!_model.ConstraintModel.UseConstraints) return;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!_model.ConstraintModel.Valid) return;
if (!_model.ConstraintModel.UseConstraints) return;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@NewType.Generator/AliasCodeGenerator.cs` at line 762, The guard currently
checks _model.ConstraintModel.Valid but should check
_model.ConstraintModel.UseConstraints so code generation only emits constraint
checks when a real validation method exists; change the early return in the
method that contains the line "if (!_model.ConstraintModel.Valid) return;" to
"if (!_model.ConstraintModel.UseConstraints) return;" (ensure any subsequent
logic that references ValidationSymbolName or valueName remains unchanged so you
don't emit "if (!({valueName}))" when no validator exists).

Comment on lines +3 to +5
/// <summary>
/// Source text for the [Alias] attribute that gets injected into user compilations.
/// </summary>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Incorrect summary describes this as an "[Alias] attribute".

The XML doc says "Source text for the [Alias] attribute" but this file provides the validation attribute source. This appears to be a copy-paste error from NewtypeAttributeSource.

📝 Proposed fix
 /// <summary>
-/// Source text for the [Alias] attribute that gets injected into user compilations.
+/// Source text for the [newtypeValidation] attribute that gets injected into user compilations.
 /// </summary>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// <summary>
/// Source text for the [Alias] attribute that gets injected into user compilations.
/// </summary>
/// <summary>
/// Source text for the [newtypeValidation] attribute that gets injected into user compilations.
/// </summary>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@NewType.Generator/AliasValidationAttributeSource.cs` around lines 3 - 5, The
XML summary on AliasValidationAttributeSource.cs incorrectly calls this the
"[Alias] attribute"; update the XML doc comment to accurately describe the file
as the source text for the Alias validation attribute (e.g., "Source text for
the alias validation attribute that gets injected into user compilations") so it
reflects the actual purpose of the AliasValidationAttributeSource class/file and
remove the copy-paste wording from NewtypeAttributeSource.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant