luaur_analysis/methods/
lint_format_string_match_string_call.rs1use 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}