Skip to content

Add validation for type declarations and schema generation#976

Open
moose-code wants to merge 2 commits intomainfrom
claude/code-review-improvements-TdLog
Open

Add validation for type declarations and schema generation#976
moose-code wants to merge 2 commits intomainfrom
claude/code-review-improvements-TdLog

Conversation

@moose-code
Copy link
Member

@moose-code moose-code commented Feb 20, 2026

Summary

This PR adds comprehensive validation to type declarations and schema generation, converting several TODO comments into implemented validation logic. The changes improve error handling by detecting invalid configurations early and providing clear error messages.

Key Changes

  • TypeDeclMulti validation:

    • Changed new() to return Result<Self> instead of Self
    • Added validation to reject empty type declaration lists
    • Added duplicate name detection among type declarations
  • TypeDecl validation:

    • Changed new() to return Result<Self> instead of Self
    • Added validation to detect duplicate type parameters
  • Topological sort improvements:

    • Changed to_rescript_schema() to return Result<String> instead of String
    • Added cycle detection in dependency resolution to prevent infinite loops
    • Distinguished between external dependencies (allowed) and internal cyclic dependencies (error)
    • Improved algorithm to detect when no progress is made during sorting
  • Schema validation:

    • Implemented check_field_names_for_proto() to detect __proto__ field names that could cause prototype pollution issues
    • Integrated the check into the schema validation pipeline
  • Contract event validation:

    • Implemented duplicate event name detection in Contract::new()
    • Provides clear error messages when duplicate event names are found
  • Updated all call sites: Modified all test cases and production code to handle the new Result return types with .unwrap() or ? operators as appropriate

Notable Implementation Details

  • The topological sort now tracks both declared type names and dependencies, allowing it to distinguish between missing internal types (error) and external type references (allowed)
  • Cycle detection uses a progress-tracking approach: if no new types are added in an iteration, a cycle must exist
  • All validation errors use anyhow!() for consistent error handling across the codebase

https://claude.ai/code/session_01BwrGigSvKsk4MR43QhkSrx

Summary by CodeRabbit

  • New Features

    • Added schema validation to prevent configuration issues with restricted field naming patterns.
  • Bug Fixes

    • Improved duplicate event name detection with detailed error reporting.
    • Enhanced error propagation in type definition handling.
    • Strengthened type schema validation with cycle-dependency detection.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 20, 2026

📝 Walkthrough

Walkthrough

This PR introduces stricter validation and error handling across the CLI package. Changes include prototype-pollution protection for entity fields, explicit duplicate detection for event names, enhanced type declaration validation with cycle detection and parameter uniqueness checks, and improved error propagation in ABI type processing.

Changes

Cohort / File(s) Summary
Field Validation
packages/cli/src/config_parsing/entity_parsing.rs, packages/cli/src/config_parsing/system_config.rs
Added new validation checks: prototype-pollution protection rejecting __proto__ field names; event name duplicate detection using HashSet with detailed error reporting listing duplicate names.
Type Schema Refactoring
packages/cli/src/type_schema.rs
Major public API changes: TypeDeclMulti::new and TypeDecl::new now return Result instead of Self; TypeDeclMulti::to_rescript_schema returns Result<String>. Added validation for empty declarations, duplicate type names, duplicate parameters, and cyclic dependencies in topological sort. Added #[derive(Debug)] to TypeDeclMulti.
Error Propagation
packages/cli/src/fuel/abi.rs, packages/cli/src/hbs_templating/codegen_templates.rs
Propagated errors from TypeDecl::new and TypeDeclMulti::new using ? operator; pre-computed ABI type schema via to_rescript_schema before template generation instead of inline recomputation.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • JonoPrest

Poem

🐰 With __proto__ fields now barred from sight,
And duplicates caught in validation's light,
Type schemas cycle-checked, pure and true—
The rabbit bounces, these safeguards brand new!
✨ Error flows where they should, oh what a delight!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.33% 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 validation for type declarations and schema generation' is directly related to the main changes in the PR, which center on implementing comprehensive validation for type declarations and schema generation with proper error handling.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/code-review-improvements-TdLog

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
Member

@DZakh DZakh left a comment

Choose a reason for hiding this comment

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

This looks good, but I'd like to have a test for every case which was fixed. I see there are tests in type_schema.rs, but it uses internal API which is not very convenient to review + more subject to break on any internal change. Ideally to have the tests accepting schema.graphql code as a string, parsing it and verifing that it either works correctly, or we get a specific error message 👍

- Fix potential infinite loop in TypeDeclMulti::to_rescript_schema when
  type declarations have cyclic dependencies. The while loop now detects
  lack of progress and returns a clear error instead of hanging forever.
- Add validation in TypeDeclMulti::new: reject empty declarations and
  duplicate type names.
- Add validation in TypeDecl::new: reject duplicate type parameters.
- Add event name uniqueness validation in Contract::new to catch
  duplicate event names within a contract early.
- Add __proto__ field name check in schema validation to prevent
  prototype pollution issues in the JavaScript runtime.
- All new validations include corresponding unit tests.

https://claude.ai/code/session_01BwrGigSvKsk4MR43QhkSrx
@moose-code moose-code force-pushed the claude/code-review-improvements-TdLog branch from ba2b68f to 4259dd6 Compare February 25, 2026 19:41
@moose-code moose-code requested a review from DZakh March 4, 2026 12:25
Copy link
Contributor

@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: 1

🧹 Nitpick comments (1)
packages/cli/src/config_parsing/entity_parsing.rs (1)

174-187: Make __proto__ violation output deterministic.

self.entities is a HashMap, so the joined violation list can be emitted in varying orders. Sorting keeps error messages stable and avoids flaky snapshots/log comparisons.

♻️ Suggested patch
     fn check_field_names_for_proto(self) -> anyhow::Result<Self> {
         let mut violations: Vec<String> = vec![];
         for entity in self.entities.values() {
             for field in &entity.fields {
                 if field.name == "__proto__" {
                     violations.push(format!("{}.{}", entity.name, field.name));
                 }
             }
         }
+        violations.sort();
+        violations.dedup();
         if !violations.is_empty() {
             return Err(anyhow!(
                 "Schema contains fields named '__proto__' which can cause prototype \
                  pollution issues: {}. Please rename these fields.",
                 violations.join(", ")
             ));
         }
         Ok(self)
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/config_parsing/entity_parsing.rs` around lines 174 - 187,
The error message can be non-deterministic because self.entities is a HashMap;
collect the offending field identifiers into the violations Vec<String> as you
do now, then sort the violations (call sort() on violations) before building the
anyhow! error string so the joined list is stable; update the code around the
violations variable in entity_parsing.rs (the loop over self.entities, the
violations Vec, and the Err(...) return) to sort violations prior to
violations.join(", ").
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/cli/src/type_schema.rs`:
- Around line 155-169: The duplicate-parameter check in TypeSchema::new is
case-sensitive, so parameters like "T" and "t" slip through; normalize parameter
names (e.g., to_lowercase()) before inserting into seen_params and building
duplicate_params. Concretely, map parameters to a normalized list (let
normalized = parameters.iter().map(|p| p.to_lowercase()).collect::<Vec<_>>()),
then run the existing seen_params/duplicate_params logic against normalized
instead of the raw parameters; keep the stored parameters unchanged unless you
intend to canonicalize them elsewhere.

---

Nitpick comments:
In `@packages/cli/src/config_parsing/entity_parsing.rs`:
- Around line 174-187: The error message can be non-deterministic because
self.entities is a HashMap; collect the offending field identifiers into the
violations Vec<String> as you do now, then sort the violations (call sort() on
violations) before building the anyhow! error string so the joined list is
stable; update the code around the violations variable in entity_parsing.rs (the
loop over self.entities, the violations Vec, and the Err(...) return) to sort
violations prior to violations.join(", ").

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 501db211-08a7-47e8-a851-61acc6327eeb

📥 Commits

Reviewing files that changed from the base of the PR and between a4148a6 and 81ce58b.

📒 Files selected for processing (5)
  • packages/cli/src/config_parsing/entity_parsing.rs
  • packages/cli/src/config_parsing/system_config.rs
  • packages/cli/src/fuel/abi.rs
  • packages/cli/src/hbs_templating/codegen_templates.rs
  • packages/cli/src/type_schema.rs

Comment on lines +155 to +169
pub fn new(name: String, type_expr: TypeExpr, parameters: Vec<String>) -> Result<Self> {
let mut seen_params: HashSet<String> = HashSet::new();
let mut duplicate_params: Vec<String> = vec![];
for param in &parameters {
if !seen_params.insert(param.clone()) {
duplicate_params.push(param.clone());
}
}
if !duplicate_params.is_empty() {
return Err(anyhow!(
"Type declaration '{}' has duplicate parameters: {}",
name,
duplicate_params.join(", ")
));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Normalize generic parameter names before duplicate checks.

Duplicate detection is currently case-sensitive, but parameters are lowercased when rendered (Line 194). Inputs like ["T", "t"] can pass validation and still emit conflicting generics.

🐛 Suggested patch
     pub fn new(name: String, type_expr: TypeExpr, parameters: Vec<String>) -> Result<Self> {
         let mut seen_params: HashSet<String> = HashSet::new();
         let mut duplicate_params: Vec<String> = vec![];
         for param in &parameters {
-            if !seen_params.insert(param.clone()) {
+            let normalized = param.to_lowercase();
+            if !seen_params.insert(normalized) {
                 duplicate_params.push(param.clone());
             }
         }
         if !duplicate_params.is_empty() {
             return Err(anyhow!(
📝 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
pub fn new(name: String, type_expr: TypeExpr, parameters: Vec<String>) -> Result<Self> {
let mut seen_params: HashSet<String> = HashSet::new();
let mut duplicate_params: Vec<String> = vec![];
for param in &parameters {
if !seen_params.insert(param.clone()) {
duplicate_params.push(param.clone());
}
}
if !duplicate_params.is_empty() {
return Err(anyhow!(
"Type declaration '{}' has duplicate parameters: {}",
name,
duplicate_params.join(", ")
));
}
pub fn new(name: String, type_expr: TypeExpr, parameters: Vec<String>) -> Result<Self> {
let mut seen_params: HashSet<String> = HashSet::new();
let mut duplicate_params: Vec<String> = vec![];
for param in &parameters {
let normalized = param.to_lowercase();
if !seen_params.insert(normalized) {
duplicate_params.push(param.clone());
}
}
if !duplicate_params.is_empty() {
return Err(anyhow!(
"Type declaration '{}' has duplicate parameters: {}",
name,
duplicate_params.join(", ")
));
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/type_schema.rs` around lines 155 - 169, The
duplicate-parameter check in TypeSchema::new is case-sensitive, so parameters
like "T" and "t" slip through; normalize parameter names (e.g., to_lowercase())
before inserting into seen_params and building duplicate_params. Concretely, map
parameters to a normalized list (let normalized = parameters.iter().map(|p|
p.to_lowercase()).collect::<Vec<_>>()), then run the existing
seen_params/duplicate_params logic against normalized instead of the raw
parameters; keep the stored parameters unchanged unless you intend to
canonicalize them elsewhere.

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.

3 participants