1
- use kclvm_ast:: ast:: { Program , Node , Stmt , AssignStmt , Expr } ;
2
- use kclvm_error:: { Diagnostic , Level } ;
1
+ use kclvm_ast:: ast:: { AssignStmt , Expr , Node , Program , Stmt } ;
3
2
use kclvm_error:: diagnostic:: Position ;
3
+ use kclvm_error:: { Diagnostic , Level } ;
4
4
use kclvm_sema:: resolver:: scope:: ProgramScope ;
5
5
use std:: collections:: HashMap ;
6
6
7
- pub fn validate_schema_attributes ( program : & Program , _scope : & ProgramScope ) -> Result < ( ) , Vec < Diagnostic > > {
7
+ pub fn validate_schema_attributes (
8
+ program : & Program ,
9
+ _scope : & ProgramScope ,
10
+ ) -> Result < ( ) , Vec < Diagnostic > > {
8
11
let mut diagnostics = Vec :: new ( ) ;
9
12
10
13
// Process schemas and validate instances in a single pass
11
14
for ( _, modules) in & program. pkgs {
12
15
for module_path in modules {
13
16
if let Ok ( Some ( module) ) = program. get_module ( module_path) {
14
17
let mut schema_attrs = HashMap :: new ( ) ;
15
-
18
+
19
+ // First pass: collect all schema definitions
16
20
for stmt in & module. body {
17
- match & * * stmt {
18
- Node { node : Stmt :: Schema ( schema) , .. } => {
19
- let mut required_attrs = Vec :: new ( ) ;
20
- for attr in & schema. body {
21
- if let Node { node : Stmt :: SchemaAttr ( attr_stmt) , .. } = & * * attr {
22
- if !attr_stmt. is_optional && attr_stmt. value . is_none ( ) {
23
- required_attrs. push ( attr_stmt. name . node . clone ( ) ) ;
24
- }
21
+ if let Node {
22
+ node : Stmt :: Schema ( schema) ,
23
+ ..
24
+ } = & * * stmt
25
+ {
26
+ let mut required_attrs = Vec :: new ( ) ;
27
+ for attr in & schema. body {
28
+ if let Node {
29
+ node : Stmt :: SchemaAttr ( attr_stmt) ,
30
+ ..
31
+ } = & * * attr
32
+ {
33
+ if !attr_stmt. is_optional && attr_stmt. value . is_none ( ) {
34
+ required_attrs. push ( attr_stmt. name . node . clone ( ) ) ;
25
35
}
26
36
}
27
- schema_attrs. insert ( schema. name . node . clone ( ) , required_attrs) ;
28
- } ,
29
- Node { node : Stmt :: Assign ( assign_stmt) , filename, line, column, .. } => {
30
- if let Some ( schema_name) = get_schema_name ( assign_stmt) {
31
- if let Some ( required_attrs) = schema_attrs. get ( & schema_name) {
32
- let missing_attrs = get_missing_attrs ( assign_stmt, required_attrs) ;
33
- if !missing_attrs. is_empty ( ) {
34
- diagnostics. push ( Diagnostic :: new (
35
- Level :: Error ,
36
- & format ! (
37
- "Missing required attributes in {} instance: {}" ,
38
- schema_name,
39
- missing_attrs. join( ", " )
40
- ) ,
41
- (
42
- Position {
43
- filename : filename. clone ( ) ,
44
- line : * line,
45
- column : Some ( * column) ,
46
- } ,
47
- Position {
48
- filename : filename. clone ( ) ,
49
- line : * line,
50
- column : Some ( * column + schema_name. len ( ) as u64 ) ,
51
- }
52
- ) ,
53
- ) ) ;
54
- }
37
+ }
38
+ schema_attrs. insert ( schema. name . node . clone ( ) , required_attrs) ;
39
+ }
40
+ }
41
+
42
+ // Second pass: validate all instances including nested ones and lambdas
43
+ for stmt in & module. body {
44
+ match & * * stmt {
45
+ Node {
46
+ node : Stmt :: Assign ( assign_stmt) ,
47
+ filename,
48
+ line,
49
+ column,
50
+ ..
51
+ } => {
52
+ validate_schema_instance (
53
+ assign_stmt,
54
+ & schema_attrs,
55
+ filename,
56
+ * line,
57
+ * column,
58
+ & mut diagnostics,
59
+ ) ;
60
+
61
+ // Check if the assignment is a lambda that returns a schema
62
+ if let Node {
63
+ node : Expr :: Lambda ( lambda_expr) ,
64
+ ..
65
+ } = & * assign_stmt. value
66
+ {
67
+ if let Some ( schema_expr) =
68
+ get_schema_from_lambda_body ( & lambda_expr. body )
69
+ {
70
+ let nested_assign = AssignStmt {
71
+ value : Box :: new ( Node {
72
+ node : Expr :: Schema ( schema_expr. clone ( ) ) ,
73
+ filename : filename. clone ( ) ,
74
+ line : * line,
75
+ column : * column,
76
+ end_line : * line,
77
+ end_column : * column,
78
+ id : kclvm_ast:: ast:: AstIndex :: default ( ) ,
79
+ } ) ,
80
+ ..assign_stmt. clone ( )
81
+ } ;
82
+ validate_schema_instance (
83
+ & nested_assign,
84
+ & schema_attrs,
85
+ filename,
86
+ * line,
87
+ * column,
88
+ & mut diagnostics,
89
+ ) ;
55
90
}
56
91
}
57
- } ,
92
+ }
58
93
_ => { }
59
94
}
60
95
}
@@ -69,31 +104,137 @@ pub fn validate_schema_attributes(program: &Program, _scope: &ProgramScope) -> R
69
104
}
70
105
}
71
106
107
+ fn get_schema_from_lambda_body ( body : & [ Box < Node < Stmt > > ] ) -> Option < & kclvm_ast:: ast:: SchemaExpr > {
108
+ for stmt in body {
109
+ if let Node {
110
+ node : Stmt :: Expr ( expr_stmt) ,
111
+ ..
112
+ } = & * * stmt
113
+ {
114
+ if let Node {
115
+ node : Expr :: Schema ( schema_expr) ,
116
+ ..
117
+ } = & * expr_stmt. exprs [ 0 ]
118
+ {
119
+ return Some ( schema_expr) ;
120
+ }
121
+ }
122
+ }
123
+ None
124
+ }
125
+
126
+ fn validate_schema_instance (
127
+ assign_stmt : & AssignStmt ,
128
+ schema_attrs : & HashMap < String , Vec < String > > ,
129
+ filename : & str ,
130
+ line : u64 ,
131
+ column : u64 ,
132
+ diagnostics : & mut Vec < Diagnostic > ,
133
+ ) {
134
+ if let Node {
135
+ node : Expr :: Schema ( schema_expr) ,
136
+ ..
137
+ } = & * assign_stmt. value
138
+ {
139
+ let schema_name = schema_expr. name . node . names . last ( ) . unwrap ( ) . node . clone ( ) ;
140
+
141
+ if let Some ( required_attrs) = schema_attrs. get ( & schema_name) {
142
+ let missing_attrs = get_missing_attrs ( assign_stmt, required_attrs) ;
143
+ if !missing_attrs. is_empty ( ) {
144
+ diagnostics. push ( Diagnostic :: new (
145
+ Level :: Error ,
146
+ & format ! (
147
+ "Missing required attributes in {} instance: {}" ,
148
+ schema_name,
149
+ missing_attrs. join( ", " )
150
+ ) ,
151
+ (
152
+ Position {
153
+ filename : filename. to_string ( ) ,
154
+ line,
155
+ column : Some ( column) ,
156
+ } ,
157
+ Position {
158
+ filename : filename. to_string ( ) ,
159
+ line,
160
+ column : Some ( column + schema_name. len ( ) as u64 ) ,
161
+ } ,
162
+ ) ,
163
+ ) ) ;
164
+ }
165
+
166
+ // Recursively validate nested schema instances
167
+ if let Node {
168
+ node : Expr :: Config ( config_expr) ,
169
+ ..
170
+ } = & * schema_expr. config
171
+ {
172
+ for item in & config_expr. items {
173
+ if let Node {
174
+ node : Expr :: Schema ( _) ,
175
+ ..
176
+ } = & * item. node . value
177
+ {
178
+ let nested_assign = AssignStmt {
179
+ value : item. node . value . clone ( ) ,
180
+ ..assign_stmt. clone ( )
181
+ } ;
182
+ validate_schema_instance (
183
+ & nested_assign,
184
+ schema_attrs,
185
+ filename,
186
+ line,
187
+ column,
188
+ diagnostics,
189
+ ) ;
190
+ }
191
+ }
192
+ }
193
+ }
194
+ }
195
+ }
196
+
72
197
fn get_schema_name ( assign_stmt : & AssignStmt ) -> Option < String > {
73
- if let Node { node : Expr :: Schema ( schema_expr) , .. } = & * assign_stmt. value {
198
+ if let Node {
199
+ node : Expr :: Schema ( schema_expr) ,
200
+ ..
201
+ } = & * assign_stmt. value
202
+ {
74
203
schema_expr. name . node . names . last ( ) . map ( |n| n. node . clone ( ) )
75
204
} else {
76
205
None
77
206
}
78
207
}
79
208
80
209
fn get_missing_attrs ( assign_stmt : & AssignStmt , required_attrs : & [ String ] ) -> Vec < String > {
81
- if let Node { node : Expr :: Schema ( schema_expr) , .. } = & * assign_stmt. value {
82
- if let Node { node : Expr :: Config ( config_expr) , .. } = & * schema_expr. config {
210
+ if let Node {
211
+ node : Expr :: Schema ( schema_expr) ,
212
+ ..
213
+ } = & * assign_stmt. value
214
+ {
215
+ if let Node {
216
+ node : Expr :: Config ( config_expr) ,
217
+ ..
218
+ } = & * schema_expr. config
219
+ {
83
220
let provided_attrs: Vec < String > = config_expr
84
221
. items
85
222
. iter ( )
86
223
. filter_map ( |item| {
87
224
item. node . key . as_ref ( ) . and_then ( |key| {
88
- if let Node { node : Expr :: Identifier ( ident) , .. } = & * * key {
225
+ if let Node {
226
+ node : Expr :: Identifier ( ident) ,
227
+ ..
228
+ } = & * * key
229
+ {
89
230
ident. names . last ( ) . map ( |n| n. node . clone ( ) )
90
231
} else {
91
232
None
92
233
}
93
234
} )
94
235
} )
95
236
. collect ( ) ;
96
-
237
+
97
238
required_attrs
98
239
. iter ( )
99
240
. filter ( |attr| !provided_attrs. contains ( attr) )
@@ -105,4 +246,4 @@ fn get_missing_attrs(assign_stmt: &AssignStmt, required_attrs: &[String]) -> Vec
105
246
} else {
106
247
Vec :: new ( )
107
248
}
108
- }
249
+ }
0 commit comments