gitql_parser/
type_checker.rs1use std::collections::HashMap;
2
3use gitql_ast::expression::CastExpr;
4use gitql_ast::expression::Expr;
5use gitql_ast::statement::TableSelection;
6use gitql_ast::types::any::AnyType;
7use gitql_ast::types::dynamic::DynamicType;
8use gitql_ast::types::varargs::VarargsType;
9use gitql_ast::types::DataType;
10use gitql_core::environment::Environment;
11
12use crate::diagnostic::Diagnostic;
13use crate::token::SourceLocation;
14
15pub fn check_all_values_are_same_type(arguments: &[Box<dyn Expr>]) -> Option<Box<dyn DataType>> {
18 let arguments_count = arguments.len();
19 if arguments_count == 0 {
20 return Some(Box::new(AnyType));
21 }
22
23 let data_type = arguments[0].expr_type();
24 for argument in arguments.iter().take(arguments_count).skip(1) {
25 let expr_type = argument.expr_type();
26 if !data_type.equals(&expr_type) {
27 return None;
28 }
29 }
30
31 Some(data_type)
32}
33
34pub fn check_function_call_arguments(
37 arguments: &mut [Box<dyn Expr>],
38 parameters: &[Box<dyn DataType>],
39 function_name: String,
40 location: SourceLocation,
41) -> Result<(), Box<Diagnostic>> {
42 let parameters_count = parameters.len();
43 let arguments_count = arguments.len();
44
45 let mut has_varargs_parameter = false;
46 let mut optional_parameters_count = 0;
47 if parameters_count != 0 {
48 let last_parameter = parameters.last().unwrap();
49 has_varargs_parameter = last_parameter.is_varargs();
50
51 for parameter_type in parameters.iter().take(parameters_count) {
53 if parameter_type.is_optional() {
54 optional_parameters_count += 1;
55 }
56 }
57 }
58
59 let mut min_arguments_count = parameters_count - optional_parameters_count;
60 if has_varargs_parameter {
61 min_arguments_count -= 1;
62 }
63
64 if arguments_count < min_arguments_count {
65 return Err(Diagnostic::error(&format!(
66 "Function `{}` expects at least `{}` arguments but got `{}`",
67 function_name, min_arguments_count, arguments_count
68 ))
69 .with_location(location)
70 .as_boxed());
71 }
72
73 if !has_varargs_parameter && arguments_count > parameters_count {
74 return Err(Diagnostic::error(&format!(
75 "Function `{}` expects `{}` arguments but got `{}`",
76 function_name, parameters_count, arguments_count
77 ))
78 .with_location(location)
79 .as_boxed());
80 }
81
82 for index in 0..min_arguments_count {
84 let parameter_type =
85 resolve_dynamic_data_type(parameters, arguments, parameters.get(index).unwrap());
86 let argument = arguments.get(index).unwrap();
87 let argument_type = argument.expr_type();
88
89 if argument_type.is_undefined() {
91 return Err(Diagnostic::error(&format!(
92 "Function `{}` argument number {} has Undefined type",
93 function_name, index,
94 ))
95 .add_help("Make sure you used a correct field name")
96 .add_help("Check column names for each table from docs website")
97 .with_location(location)
98 .as_boxed());
99 }
100
101 if parameter_type.equals(&argument_type) {
103 continue;
104 }
105
106 if parameter_type.has_implicit_cast_from(argument) {
108 arguments[index] = Box::new(CastExpr {
109 value: argument.clone(),
110 result_type: parameter_type.clone(),
111 });
112 continue;
113 }
114
115 return Err(Diagnostic::error(&format!(
117 "Function `{}` argument number {} with type `{}` don't match expected type `{}`",
118 function_name,
119 index,
120 argument_type.literal(),
121 parameter_type.literal()
122 ))
123 .with_location(location)
124 .as_boxed());
125 }
126
127 let last_optional_param_index = min_arguments_count + optional_parameters_count;
129 for index in min_arguments_count..last_optional_param_index {
130 if index >= arguments_count {
131 return Ok(());
132 }
133
134 let parameter_type =
135 resolve_dynamic_data_type(parameters, arguments, parameters.get(index).unwrap());
136 let argument = arguments.get(index).unwrap();
137 let argument_type = argument.expr_type();
138
139 if argument_type.is_undefined() {
141 return Err(Diagnostic::error(&format!(
142 "Function `{}` argument number {} has Undefined type",
143 function_name, index,
144 ))
145 .add_help("Make sure you used a correct field name")
146 .add_help("Check column names for each table from docs website")
147 .with_location(location)
148 .as_boxed());
149 }
150
151 if parameter_type.equals(&argument_type) {
153 continue;
154 }
155
156 if parameter_type.has_implicit_cast_from(argument) {
158 arguments[index] = Box::new(CastExpr {
159 value: argument.clone(),
160 result_type: parameter_type.clone(),
161 });
162 continue;
163 }
164
165 return Err(Diagnostic::error(&format!(
167 "Function `{}` argument number {} with type `{}` don't match expected type `{}`",
168 function_name,
169 index,
170 argument_type.literal(),
171 parameter_type.literal()
172 ))
173 .with_location(location)
174 .as_boxed());
175 }
176
177 if has_varargs_parameter {
179 let varargs_type =
180 resolve_dynamic_data_type(parameters, arguments, parameters.last().unwrap());
181 for index in last_optional_param_index..arguments_count {
182 let argument = arguments.get(index).unwrap();
183 let argument_type = argument.expr_type();
184
185 if argument_type.is_undefined() {
187 return Err(Diagnostic::error(&format!(
188 "Function `{}` argument number {} has Undefined type",
189 function_name, index,
190 ))
191 .add_help("Make sure you used a correct field name")
192 .add_help("Check column names for each table from docs website")
193 .with_location(location)
194 .as_boxed());
195 }
196
197 if varargs_type.equals(&argument_type) {
199 continue;
200 }
201
202 if varargs_type.has_implicit_cast_from(argument) {
204 arguments[index] = Box::new(CastExpr {
205 value: argument.clone(),
206 result_type: varargs_type.clone(),
207 });
208 continue;
209 }
210
211 return Err(Diagnostic::error(&format!(
212 "Function `{}` argument number {} with type `{}` don't match expected type `{}`",
213 function_name,
214 index,
215 &argument_type.literal(),
216 &varargs_type.literal()
217 ))
218 .with_location(location)
219 .as_boxed());
220 }
221 }
222
223 Ok(())
224}
225
226pub fn type_check_and_classify_selected_fields(
230 env: &mut Environment,
231 selected_tables: &Vec<String>,
232 selected_columns: &Vec<String>,
233 location: SourceLocation,
234) -> Result<Vec<TableSelection>, Box<Diagnostic>> {
235 let mut table_selections: Vec<TableSelection> = vec![];
236 let mut table_index: HashMap<String, usize> = HashMap::new();
237 for (index, table) in selected_tables.iter().enumerate() {
238 table_selections.push(TableSelection {
239 table_name: table.to_string(),
240 columns_names: vec![],
241 });
242 table_index.insert(table.to_string(), index);
243 }
244
245 for selected_column in selected_columns {
246 let mut is_column_resolved = false;
247 for table in selected_tables {
248 let table_columns = env.schema.tables_fields_names.get(table.as_str()).unwrap();
249
250 if table_columns.contains(&selected_column.as_str()) {
252 is_column_resolved = true;
253 let table_selection_index = *table_index.get(table).unwrap();
254 let selection = &mut table_selections[table_selection_index];
255 selection.columns_names.push(selected_column.to_string());
256 continue;
257 }
258 }
259
260 if !is_column_resolved {
261 if let Some(data_type) = env.resolve_type(selected_column) {
263 if !data_type.is_undefined() {
264 if table_selections.is_empty() {
265 table_selections.push(TableSelection {
266 table_name: selected_tables
267 .first()
268 .unwrap_or(&"".to_string())
269 .to_string(),
270 columns_names: vec![selected_column.to_string()],
271 });
272 } else {
273 table_selections[0]
274 .columns_names
275 .push(selected_column.to_string());
276 }
277 continue;
278 }
279 }
280
281 return Err(Diagnostic::error(&format!(
282 "Column `{}` not exists in any of the selected tables",
283 selected_column
284 ))
285 .add_help("Check the documentations to see available fields for each tables")
286 .with_location(location)
287 .as_boxed());
288 }
289 }
290
291 Ok(table_selections)
292}
293
294pub fn type_check_projection_symbols(
297 env: &mut Environment,
298 selected_tables: &[String],
299 projection_names: &[String],
300 projection_locations: &[SourceLocation],
301) -> Result<(), Box<Diagnostic>> {
302 for (index, selected_column) in projection_names.iter().enumerate() {
303 let mut is_column_resolved = false;
304 for table in selected_tables {
305 let table_columns = env.schema.tables_fields_names.get(table.as_str()).unwrap();
306 if table_columns.contains(&selected_column.as_str()) {
307 is_column_resolved = true;
308 break;
309 }
310 }
311
312 if !is_column_resolved {
313 return Err(Diagnostic::error(&format!(
314 "Column `{}` not exists in any of the selected tables",
315 selected_column
316 ))
317 .add_help("Check the documentations to see available fields for each tables")
318 .with_location(projection_locations[index])
319 .as_boxed());
320 }
321 }
322
323 Ok(())
324}
325
326#[allow(clippy::borrowed_box)]
328pub fn resolve_dynamic_data_type(
329 parameters: &[Box<dyn DataType>],
330 arguments: &[Box<dyn Expr>],
331 data_type: &Box<dyn DataType>,
332) -> Box<dyn DataType> {
333 if let Some(dynamic_type) = data_type.as_any().downcast_ref::<DynamicType>() {
335 let mut resolved_data_type = (dynamic_type.function)(parameters);
336
337 if !arguments.is_empty() && (resolved_data_type.is_variant() || resolved_data_type.is_any())
340 {
341 let mut arguments_types: Vec<Box<dyn DataType>> = Vec::with_capacity(arguments.len());
342 for argument in arguments {
343 arguments_types.push(argument.expr_type());
344 }
345 resolved_data_type = (dynamic_type.function)(&arguments_types);
346 }
347
348 return resolved_data_type;
349 }
350
351 if let Some(varargs) = data_type.as_any().downcast_ref::<VarargsType>() {
353 if varargs
354 .base
355 .as_any()
356 .downcast_ref::<DynamicType>()
357 .is_some()
358 {
359 let base = resolve_dynamic_data_type(parameters, arguments, &varargs.base);
360 return Box::new(VarargsType { base });
361 }
362 }
363
364 data_type.clone()
365}