Skip to content

Commit 580c18e

Browse files
committed
Prevent dangerous re-cloning inside loop
1 parent 7509f8c commit 580c18e

File tree

4 files changed

+56
-0
lines changed

4 files changed

+56
-0
lines changed

src/analyzer/expr/binop/assignment_analyzer.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,41 @@ pub(crate) fn analyze(
182182
if let (Some(var_id), Some(existing_var_type), Bop::Eq(None)) =
183183
(&var_id, &existing_var_type, binop)
184184
{
185+
if context.inside_loop && !context.inside_assignment_op {
186+
if let Some(assign_value) = assign_value {
187+
if let aast::Expr_::Clone(cloned_expr) = &assign_value.2 {
188+
if let aast::Expr_::Lvar(cloned_var) = &cloned_expr.2 {
189+
if cloned_var.name() == var_id {
190+
let mut origin_node_ids = vec![];
191+
192+
for parent_node in &existing_var_type.parent_nodes {
193+
origin_node_ids.extend(
194+
analysis_data.data_flow_graph.get_origin_node_ids(
195+
&parent_node.id,
196+
&[],
197+
false,
198+
),
199+
);
200+
}
201+
202+
if origin_node_ids.len() > 1 {
203+
analysis_data.maybe_add_issue(
204+
Issue::new(
205+
IssueKind::CloneInsideLoop,
206+
format!("Overwriting an object {} outside the loop with a clone likely not intended", var_id),
207+
statements_analyzer.get_hpos(pos),
208+
&context.function_context.calling_functionlike_id,
209+
),
210+
statements_analyzer.get_config(),
211+
statements_analyzer.get_file_path_actual(),
212+
)
213+
}
214+
}
215+
}
216+
}
217+
}
218+
}
219+
185220
if context.inside_loop
186221
&& !context.inside_assignment_op
187222
&& context.for_loop_init_bounds.0 > 0
@@ -197,6 +232,14 @@ pub(crate) fn analyze(
197232
));
198233
}
199234

235+
if let Some(assign_value) = assign_value {
236+
if let aast::Expr_::Clone(cloned_expr) = &assign_value.2 {
237+
if let aast::Expr_::Lvar(cloned_var) = &cloned_expr.2 {
238+
if cloned_var.name() == var_id {}
239+
}
240+
}
241+
}
242+
200243
origin_node_ids.retain(|id| {
201244
if let Some(node) = analysis_data.data_flow_graph.get_node(id) {
202245
match (&id, &node.kind) {

src/code_info/issue.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub enum IssueKind {
1818
BannedFunction,
1919
ExtendFinalClass,
2020
CannotInferGenericParam,
21+
CloneInsideLoop,
2122
CustomIssue(Box<String>),
2223
DuplicateEnumValue,
2324
EmptyBlock,
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
final class A {
2+
public function __construct(public int $num) {}
3+
}
4+
5+
function foo(A $a, vec<int> $nums): void {
6+
foreach ($nums as $num) {
7+
$a = clone $a;
8+
$a->num += $num;
9+
echo $a->num;
10+
}
11+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ERROR: CloneInsideLoop - input.hack:7:6 - Overwriting an object $a outside the loop with a clone likely not intended

0 commit comments

Comments
 (0)