Skip to main content

luaur_analysis/methods/
lint_format_string_match_string_call.rs

1use crate::functions::emit_warning::emit_warning;
2use crate::records::lint_format_string::LintFormatString;
3use luaur_ast::records::ast_array::AstArray;
4use luaur_ast::records::ast_expr::AstExpr;
5use luaur_ast::records::ast_expr_constant_bool::AstExprConstantBool;
6use luaur_ast::records::ast_expr_constant_string::AstExprConstantString;
7use luaur_ast::records::ast_name::AstName;
8use luaur_ast::records::ast_node::AstNode;
9use luaur_ast::rtti;
10use luaur_config::enums::code::Code;
11use luaur_config::records::lint_warning::LintWarning;
12
13impl LintFormatString {
14    pub fn match_string_call(
15        &mut self,
16        name: AstName,
17        self_expr: *mut AstExpr,
18        args: AstArray<*mut AstExpr>,
19    ) {
20        let is_format = name.operator_eq_c_char(c"format".as_ptr());
21        let is_pack_packsize_unpack = name.operator_eq_c_char(c"pack".as_ptr())
22            || name.operator_eq_c_char(c"packsize".as_ptr())
23            || name.operator_eq_c_char(c"unpack".as_ptr());
24        let is_match_gmatch = name.operator_eq_c_char(c"match".as_ptr())
25            || name.operator_eq_c_char(c"gmatch".as_ptr());
26        let is_find = name.operator_eq_c_char(c"find".as_ptr());
27        let is_gsub = name.operator_eq_c_char(c"gsub".as_ptr());
28
29        if is_format {
30            let fmt_node =
31                unsafe { rtti::ast_node_as::<AstExprConstantString>(self_expr as *mut AstNode) };
32            if !fmt_node.is_null() {
33                let fmt = unsafe { &*fmt_node };
34                let error = self.check_string_format(fmt.value.data, fmt.value.size);
35                if !error.is_null() {
36                    emit_warning(
37                        unsafe { &mut *self.context },
38                        Code::Code_FormatString,
39                        fmt.base.base.location,
40                        format_args!("Invalid format string: {}", unsafe {
41                            core::ffi::CStr::from_ptr(error).to_string_lossy()
42                        }),
43                    );
44                }
45            }
46        } else if is_pack_packsize_unpack {
47            let fmt_node =
48                unsafe { rtti::ast_node_as::<AstExprConstantString>(self_expr as *mut AstNode) };
49            if !fmt_node.is_null() {
50                let fmt = unsafe { &*fmt_node };
51                let is_packsize = name.operator_eq_c_char(c"packsize".as_ptr());
52                let error = self.check_string_pack(fmt.value.data, fmt.value.size, is_packsize);
53                if !error.is_null() {
54                    emit_warning(
55                        unsafe { &mut *self.context },
56                        Code::Code_FormatString,
57                        fmt.base.base.location,
58                        format_args!("Invalid pack format: {}", unsafe {
59                            core::ffi::CStr::from_ptr(error).to_string_lossy()
60                        }),
61                    );
62                }
63            }
64        } else if is_match_gmatch && args.size > 0 {
65            let first_arg = unsafe { *args.data.add(0) };
66            let pat_node =
67                unsafe { rtti::ast_node_as::<AstExprConstantString>(first_arg as *mut AstNode) };
68            if !pat_node.is_null() {
69                let pat = unsafe { &*pat_node };
70                let error =
71                    self.check_string_match(pat.value.data, pat.value.size, core::ptr::null_mut());
72                if !error.is_null() {
73                    emit_warning(
74                        unsafe { &mut *self.context },
75                        Code::Code_FormatString,
76                        pat.base.base.location,
77                        format_args!("Invalid match pattern: {}", unsafe {
78                            core::ffi::CStr::from_ptr(error).to_string_lossy()
79                        }),
80                    );
81                }
82            }
83        } else if is_find && args.size > 0 && args.size <= 2 {
84            let first_arg = unsafe { *args.data.add(0) };
85            let pat_node =
86                unsafe { rtti::ast_node_as::<AstExprConstantString>(first_arg as *mut AstNode) };
87            if !pat_node.is_null() {
88                let pat = unsafe { &*pat_node };
89                let error =
90                    self.check_string_match(pat.value.data, pat.value.size, core::ptr::null_mut());
91                if !error.is_null() {
92                    emit_warning(
93                        unsafe { &mut *self.context },
94                        Code::Code_FormatString,
95                        pat.base.base.location,
96                        format_args!("Invalid match pattern: {}", unsafe {
97                            core::ffi::CStr::from_ptr(error).to_string_lossy()
98                        }),
99                    );
100                }
101            }
102        } else if is_find && args.size >= 3 {
103            let third_arg = unsafe { *args.data.add(2) };
104            let mode =
105                unsafe { rtti::ast_node_as::<AstExprConstantBool>(third_arg as *mut AstNode) };
106            if !mode.is_null() {
107                let mode_val = unsafe { &*mode };
108                if !mode_val.value {
109                    let first_arg = unsafe { *args.data.add(0) };
110                    let pat_node = unsafe {
111                        rtti::ast_node_as::<AstExprConstantString>(first_arg as *mut AstNode)
112                    };
113                    if !pat_node.is_null() {
114                        let pat = unsafe { &*pat_node };
115                        let error = self.check_string_match(
116                            pat.value.data,
117                            pat.value.size,
118                            core::ptr::null_mut(),
119                        );
120                        if !error.is_null() {
121                            emit_warning(
122                                unsafe { &mut *self.context },
123                                Code::Code_FormatString,
124                                pat.base.base.location,
125                                format_args!("Invalid match pattern: {}", unsafe {
126                                    core::ffi::CStr::from_ptr(error).to_string_lossy()
127                                }),
128                            );
129                        }
130                    }
131                }
132            }
133        } else if is_gsub && args.size > 1 {
134            let mut captures = -1;
135
136            let first_arg = unsafe { *args.data.add(0) };
137            let pat_node =
138                unsafe { rtti::ast_node_as::<AstExprConstantString>(first_arg as *mut AstNode) };
139            if !pat_node.is_null() {
140                let pat = unsafe { &*pat_node };
141                let error = self.check_string_match(pat.value.data, pat.value.size, &mut captures);
142                if !error.is_null() {
143                    emit_warning(
144                        unsafe { &mut *self.context },
145                        Code::Code_FormatString,
146                        pat.base.base.location,
147                        format_args!("Invalid match pattern: {}", unsafe {
148                            core::ffi::CStr::from_ptr(error).to_string_lossy()
149                        }),
150                    );
151                }
152            }
153
154            let second_arg = unsafe { *args.data.add(1) };
155            let rep_node =
156                unsafe { rtti::ast_node_as::<AstExprConstantString>(second_arg as *mut AstNode) };
157            if !rep_node.is_null() {
158                let rep = unsafe { &*rep_node };
159                let error = self.check_string_replace(rep.value.data, rep.value.size, captures);
160                if !error.is_null() {
161                    emit_warning(
162                        unsafe { &mut *self.context },
163                        Code::Code_FormatString,
164                        rep.base.base.location,
165                        format_args!("Invalid match replacement: {}", unsafe {
166                            core::ffi::CStr::from_ptr(error).to_string_lossy()
167                        }),
168                    );
169                }
170            }
171        }
172    }
173}