Skip to main content

luaur_analysis/methods/
lint_format_string_match_call.rs

1use crate::functions::emit_warning::emit_warning;
2use crate::functions::is_string::is_string;
3use crate::records::lint_format_string::LintFormatString;
4use luaur_ast::records::ast_array::AstArray;
5use luaur_ast::records::ast_expr::AstExpr;
6use luaur_ast::records::ast_expr_call::AstExprCall;
7use luaur_ast::records::ast_expr_constant_string::AstExprConstantString;
8use luaur_ast::records::ast_expr_global::AstExprGlobal;
9use luaur_ast::records::ast_expr_group::AstExprGroup;
10use luaur_ast::records::ast_expr_index_name::AstExprIndexName;
11use luaur_ast::records::ast_node::AstNode;
12use luaur_ast::rtti;
13use luaur_config::enums::code::Code;
14
15impl LintFormatString {
16    pub fn match_call(&mut self, node: *mut AstExprCall) {
17        let node = unsafe { &*node };
18        let func = unsafe { rtti::ast_node_as::<AstExprIndexName>(node.func as *mut AstNode) };
19        if func.is_null() {
20            return;
21        }
22
23        if node.self_ {
24            let group = unsafe { rtti::ast_node_as::<AstExprGroup>((*func).expr as *mut AstNode) };
25            let self_expr: *mut AstExpr = if !group.is_null() {
26                unsafe { (*group).expr }
27            } else {
28                unsafe { (*func).expr }
29            };
30
31            if rtti::ast_node_is::<AstExprConstantString>(self_expr as *mut AstNode) {
32                self.match_string_call(unsafe { (*func).index }, self_expr, node.args);
33            } else if let Some(type_id) = unsafe { (*self.context).get_type(self_expr) } {
34                if is_string(type_id) {
35                    self.match_string_call(unsafe { (*func).index }, self_expr, node.args);
36                }
37            }
38            return;
39        }
40
41        let lib = unsafe { rtti::ast_node_as::<AstExprGlobal>((*func).expr as *mut AstNode) };
42        if lib.is_null() {
43            return;
44        }
45
46        let lib_name = unsafe { (*lib).name };
47
48        if lib_name.operator_eq_c_char(c"string".as_ptr()) {
49            if node.args.size > 0 {
50                let rest = AstArray {
51                    data: unsafe { node.args.data.add(1) },
52                    size: node.args.size - 1,
53                };
54                let first_arg = unsafe { *node.args.data.add(0) };
55                self.match_string_call(unsafe { (*func).index }, first_arg, rest);
56            }
57        } else if lib_name.operator_eq_c_char(c"os".as_ptr()) {
58            if unsafe { (*func).index }.operator_eq_c_char(c"date".as_ptr()) && node.args.size > 0 {
59                let arg0 = unsafe { *node.args.data.add(0) };
60                let fmt =
61                    unsafe { rtti::ast_node_as::<AstExprConstantString>(arg0 as *mut AstNode) };
62                if !fmt.is_null() {
63                    let error = self.check_date_format(unsafe { (*fmt).value.data }, unsafe {
64                        (*fmt).value.size
65                    });
66                    if !error.is_null() {
67                        let error_str =
68                            unsafe { core::ffi::CStr::from_ptr(error).to_string_lossy() };
69                        emit_warning(
70                            unsafe { &mut *self.context },
71                            Code::Code_FormatString,
72                            unsafe { (*fmt).base.base.location },
73                            format_args!("Invalid date format: {}", error_str),
74                        );
75                    }
76                }
77            }
78        }
79    }
80}