@@ -582,11 +582,43 @@ fn validate_inference(token: &str, constraints: Vec<String>) -> Result<bool, Str
582582 }
583583
584584 // Handle boundary checks: length > 0, count < 100, etc.
585+ // Validates constraint syntax: LHS must be a valid identifier, operator
586+ // must be a comparison, RHS must be a valid number. Runtime value checking
587+ // happens in the ReScript AntiCrash module which has access to model state.
585588 if constraint. contains ( " > " ) || constraint. contains ( " < " ) ||
586589 constraint. contains ( " >= " ) || constraint. contains ( " <= " ) {
587- // For now, we don't have the actual values to check against
588- // This would require integrating with the model state
589- // Accept boundary constraints as valid declarations
590+ // Split on the operator to validate both sides
591+ let operators = [ " >= " , " <= " , " > " , " < " ] ;
592+ let mut validated = false ;
593+ for op in & operators {
594+ if let Some ( pos) = constraint. find ( op) {
595+ let lhs = constraint[ ..pos] . trim ( ) ;
596+ let rhs = constraint[ pos + op. len ( ) ..] . trim ( ) ;
597+
598+ // LHS must be a valid identifier (alphanumeric + underscores, not starting with digit)
599+ if lhs. is_empty ( ) || lhs. starts_with ( |c : char | c. is_ascii_digit ( ) ) ||
600+ !lhs. chars ( ) . all ( |c| c. is_alphanumeric ( ) || c == '_' ) {
601+ return Err ( format ! (
602+ "Invalid boundary constraint: '{}' is not a valid identifier in '{}'" ,
603+ lhs, constraint
604+ ) ) ;
605+ }
606+
607+ // RHS must be a valid number (integer or float, optionally negative)
608+ if rhs. parse :: < f64 > ( ) . is_err ( ) {
609+ return Err ( format ! (
610+ "Invalid boundary constraint: '{}' is not a valid number in '{}'" ,
611+ rhs, constraint
612+ ) ) ;
613+ }
614+
615+ validated = true ;
616+ break ;
617+ }
618+ }
619+ if !validated {
620+ return Err ( format ! ( "Malformed boundary constraint: {}" , constraint) ) ;
621+ }
590622 continue ;
591623 }
592624
@@ -928,6 +960,46 @@ Commands:
928960 assert_eq ! ( result. unwrap( ) , true ) ;
929961 }
930962
963+ #[ test]
964+ fn test_validate_inference_valid_boundary_passes ( ) {
965+ let token = "const x = 42;" ;
966+ let constraints = vec ! [
967+ "length > 0" . to_string( ) ,
968+ "count < 100" . to_string( ) ,
969+ "score >= -1.5" . to_string( ) ,
970+ "max_depth <= 999" . to_string( ) ,
971+ ] ;
972+ let result = validate_inference ( token, constraints) ;
973+ assert ! ( result. is_ok( ) ) ;
974+ }
975+
976+ #[ test]
977+ fn test_validate_inference_invalid_boundary_rhs_rejected ( ) {
978+ let token = "const x = 42;" ;
979+ let constraints = vec ! [ "length > \" invalid\" " . to_string( ) ] ;
980+ let result = validate_inference ( token, constraints) ;
981+ assert ! ( result. is_err( ) ) ;
982+ assert ! ( result. unwrap_err( ) . contains( "not a valid number" ) ) ;
983+ }
984+
985+ #[ test]
986+ fn test_validate_inference_invalid_boundary_lhs_rejected ( ) {
987+ let token = "const x = 42;" ;
988+ let constraints = vec ! [ "123bad > 5" . to_string( ) ] ;
989+ let result = validate_inference ( token, constraints) ;
990+ assert ! ( result. is_err( ) ) ;
991+ assert ! ( result. unwrap_err( ) . contains( "not a valid identifier" ) ) ;
992+ }
993+
994+ #[ test]
995+ fn test_validate_inference_boundary_special_chars_rejected ( ) {
996+ let token = "const x = 42;" ;
997+ let constraints = vec ! [ "foo-bar > 5" . to_string( ) ] ;
998+ let result = validate_inference ( token, constraints) ;
999+ assert ! ( result. is_err( ) ) ;
1000+ assert ! ( result. unwrap_err( ) . contains( "not a valid identifier" ) ) ;
1001+ }
1002+
9311003 #[ test]
9321004 fn test_vexation_initial_index_is_zero ( ) {
9331005 // Reset tracker by creating a new one (in a real app we'd need better reset mechanisms)
0 commit comments