@@ -30,6 +30,12 @@ public List<ParseError> ValidateNative(NativeDefinition native, string filePath)
3030 foreach ( var param in native . Parameters )
3131 {
3232 ValidateType ( param . Type , $ "parameter '{ param . Name } '", filePath , errors ) ;
33+
34+ // Validate default values match parameter types
35+ if ( param . HasDefaultValue )
36+ {
37+ ValidateDefaultValue ( param , filePath , errors ) ;
38+ }
3339 }
3440
3541 // Validate and resolve return type
@@ -38,6 +44,224 @@ public List<ParseError> ValidateNative(NativeDefinition native, string filePath)
3844 return errors ;
3945 }
4046
47+ /// <summary>
48+ /// Validates that a default value is compatible with the parameter type.
49+ /// </summary>
50+ private static void ValidateDefaultValue ( NativeParameter param , string filePath , List < ParseError > errors )
51+ {
52+ var type = param . Type ;
53+ var defaultValue = param . DefaultValue ! ;
54+ var context = $ "parameter '{ param . Name } '";
55+
56+ var error = ValidateDefaultForType ( type , defaultValue , context ) ;
57+ if ( error != null )
58+ {
59+ errors . Add ( new ParseError
60+ {
61+ FilePath = filePath ,
62+ Line = 1 ,
63+ Column = 1 ,
64+ Message = error
65+ } ) ;
66+ }
67+ }
68+
69+ /// <summary>
70+ /// Validates a default value against a type and returns an error message if invalid.
71+ /// </summary>
72+ public static string ? ValidateDefaultForType ( TypeInfo type , string defaultValue , string context )
73+ {
74+ // Handle pointers - string pointers can be null, other pointers shouldn't have defaults
75+ if ( type . IsPointer && type . Name != "char" )
76+ {
77+ return $ "Pointer type '{ type } ' in { context } should not have a default value '{ defaultValue } '";
78+ }
79+
80+ // Validate based on category
81+ return type . Category switch
82+ {
83+ TypeCategory . Primitive => ValidatePrimitiveDefault ( type , defaultValue , context ) ,
84+ TypeCategory . Handle => ValidateHandleDefault ( type , defaultValue , context ) ,
85+ TypeCategory . Hash => ValidateHashDefault ( defaultValue , context ) ,
86+ TypeCategory . String => ValidateStringDefault ( defaultValue , context ) ,
87+ TypeCategory . Enum => ValidateEnumDefault ( defaultValue , context ) ,
88+ TypeCategory . Vector2 or TypeCategory . Vector3 or TypeCategory . Vector4
89+ => $ "Vector type '{ type } ' in { context } should not have a default value",
90+ TypeCategory . Color => $ "Color type in { context } should not have a default value",
91+ TypeCategory . Struct => $ "Struct type '{ type } ' in { context } should not have a default value",
92+ TypeCategory . Any => null , // Any type can have any default
93+ TypeCategory . Void => $ "Void type in { context } cannot have a default value",
94+ _ => null
95+ } ;
96+ }
97+
98+ private static string ? ValidatePrimitiveDefault ( TypeInfo type , string defaultValue , string context )
99+ {
100+ // Boolean types
101+ if ( type . IsBool )
102+ {
103+ if ( defaultValue is "true" or "false" or "TRUE" or "FALSE" or "0" or "1" )
104+ return null ;
105+ return $ "Boolean { context } has invalid default '{ defaultValue } '. Expected: true, false, 0, or 1";
106+ }
107+
108+ // Float types - allow numeric literals with optional decimal/scientific notation
109+ if ( type . IsFloat )
110+ {
111+ if ( IsNumericLiteral ( defaultValue , allowDecimal : true ) )
112+ return null ;
113+ return $ "Float { context } has invalid default '{ defaultValue } '. Expected: numeric literal (e.g., 0, 1.0, -3.14)";
114+ }
115+
116+ // Integer types - allow integer literals only
117+ if ( IsNumericLiteral ( defaultValue , allowDecimal : false ) )
118+ return null ;
119+
120+ return $ "Integer { context } has invalid default '{ defaultValue } '. Expected: integer literal (e.g., 0, 1, -1)";
121+ }
122+
123+ private static string ? ValidateHandleDefault ( TypeInfo type , string defaultValue , string context )
124+ {
125+ // Handle types can have 0, nullptr, or NULL as default (null handle)
126+ if ( defaultValue is "0" or "nullptr" or "NULL" )
127+ return null ;
128+
129+ // Provide helpful error for class handle vs non-class handle
130+ if ( TypeInfo . IsClassHandle ( type . Name ) )
131+ {
132+ return $ "Class handle type '{ type . Name } ' in { context } has invalid default '{ defaultValue } '. " +
133+ $ "Only '0', 'nullptr', or 'NULL' is allowed (will be converted to null). Did you mean: { type . Name } { GetParamNameFromContext ( context ) } = 0";
134+ }
135+
136+ return $ "Handle type '{ type . Name } ' in { context } has invalid default '{ defaultValue } '. " +
137+ $ "Only '0', 'nullptr', or 'NULL' is allowed for null handle";
138+ }
139+
140+ private static string ? ValidateHashDefault ( string defaultValue , string context )
141+ {
142+ // Hash can be a string literal or numeric
143+ if ( IsStringLiteral ( defaultValue ) || IsNumericLiteral ( defaultValue , allowDecimal : false ) )
144+ return null ;
145+
146+ return $ "Hash { context } has invalid default '{ defaultValue } '. Expected: string literal (e.g., \" model\" ) or numeric hash";
147+ }
148+
149+ private static string ? ValidateStringDefault ( string defaultValue , string context )
150+ {
151+ // String can be a string literal or null pointer
152+ if ( IsStringLiteral ( defaultValue ) || defaultValue is "nullptr" or "NULL" or "0" )
153+ return null ;
154+
155+ return $ "String { context } has invalid default '{ defaultValue } '. Expected: string literal (e.g., \" text\" ), nullptr, or NULL";
156+ }
157+
158+ private static string ? ValidateEnumDefault ( string defaultValue , string context )
159+ {
160+ // Enum can be numeric (cast) or an identifier (enum member name)
161+ if ( IsNumericLiteral ( defaultValue , allowDecimal : false ) || IsIdentifier ( defaultValue ) )
162+ return null ;
163+
164+ return $ "Enum { context } has invalid default '{ defaultValue } '. Expected: numeric value or enum member name";
165+ }
166+
167+ private static bool IsNumericLiteral ( string value , bool allowDecimal )
168+ {
169+ if ( string . IsNullOrEmpty ( value ) )
170+ return false ;
171+
172+ var span = value . AsSpan ( ) ;
173+ var start = 0 ;
174+
175+ // Allow leading minus for negative numbers
176+ if ( span [ 0 ] == '-' )
177+ {
178+ if ( span . Length == 1 )
179+ return false ;
180+ start = 1 ;
181+ }
182+
183+ // Check for hex literal (0x...)
184+ if ( span . Length > start + 2 && span [ start ] == '0' && ( span [ start + 1 ] == 'x' || span [ start + 1 ] == 'X' ) )
185+ {
186+ for ( var i = start + 2 ; i < span . Length ; i ++ )
187+ {
188+ if ( ! char . IsAsciiHexDigit ( span [ i ] ) )
189+ return false ;
190+ }
191+ return span . Length > start + 2 ;
192+ }
193+
194+ var hasDecimal = false ;
195+ var hasExponent = false ;
196+
197+ for ( var i = start ; i < span . Length ; i ++ )
198+ {
199+ var c = span [ i ] ;
200+
201+ if ( char . IsAsciiDigit ( c ) )
202+ continue ;
203+
204+ if ( allowDecimal && c == '.' && ! hasDecimal && ! hasExponent )
205+ {
206+ hasDecimal = true ;
207+ continue ;
208+ }
209+
210+ if ( allowDecimal && ( c == 'e' || c == 'E' ) && ! hasExponent && i > start )
211+ {
212+ hasExponent = true ;
213+ // Allow optional sign after exponent
214+ if ( i + 1 < span . Length && ( span [ i + 1 ] == '+' || span [ i + 1 ] == '-' ) )
215+ i ++ ;
216+ continue ;
217+ }
218+
219+ // Allow 'f' suffix for float literals
220+ if ( allowDecimal && ( c == 'f' || c == 'F' ) && i == span . Length - 1 )
221+ continue ;
222+
223+ return false ;
224+ }
225+
226+ return true ;
227+ }
228+
229+ private static bool IsStringLiteral ( string value )
230+ {
231+ return value . Length >= 2 &&
232+ ( ( value . StartsWith ( '"' ) && value . EndsWith ( '"' ) ) ||
233+ ( value . StartsWith ( '\' ' ) && value . EndsWith ( '\' ' ) ) ) ;
234+ }
235+
236+ private static bool IsIdentifier ( string value )
237+ {
238+ if ( string . IsNullOrEmpty ( value ) )
239+ return false ;
240+
241+ // First char must be letter or underscore
242+ if ( ! char . IsLetter ( value [ 0 ] ) && value [ 0 ] != '_' )
243+ return false ;
244+
245+ // Rest can be letter, digit, or underscore
246+ for ( var i = 1 ; i < value . Length ; i ++ )
247+ {
248+ if ( ! char . IsLetterOrDigit ( value [ i ] ) && value [ i ] != '_' )
249+ return false ;
250+ }
251+
252+ return true ;
253+ }
254+
255+ private static string GetParamNameFromContext ( string context )
256+ {
257+ // Extract param name from "parameter 'name'" format
258+ var start = context . IndexOf ( '\' ' ) ;
259+ var end = context . LastIndexOf ( '\' ' ) ;
260+ if ( start >= 0 && end > start )
261+ return context [ ( start + 1 ) ..end ] ;
262+ return "param" ;
263+ }
264+
41265 private void ValidateType ( TypeInfo type , string context , string filePath , List < ParseError > errors )
42266 {
43267 // First, try to resolve enum types
0 commit comments