lambda-graphs 0.1.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. lambda_graphs/__init__.py +18 -0
  2. lambda_graphs/__main__.py +7 -0
  3. lambda_graphs/cli.py +183 -0
  4. lambda_graphs/codeviews/AST/AST.py +120 -0
  5. lambda_graphs/codeviews/AST/AST_driver.py +37 -0
  6. lambda_graphs/codeviews/AST/__init__.py +0 -0
  7. lambda_graphs/codeviews/CFG/CFG.py +42 -0
  8. lambda_graphs/codeviews/CFG/CFG_c.py +1182 -0
  9. lambda_graphs/codeviews/CFG/CFG_cpp.py +5604 -0
  10. lambda_graphs/codeviews/CFG/CFG_driver.py +45 -0
  11. lambda_graphs/codeviews/CFG/CFG_java.py +1722 -0
  12. lambda_graphs/codeviews/CFG/__init__.py +0 -0
  13. lambda_graphs/codeviews/CST/CST_driver.py +70 -0
  14. lambda_graphs/codeviews/CST/__init__.py +0 -0
  15. lambda_graphs/codeviews/DFG/DFG_driver.py +42 -0
  16. lambda_graphs/codeviews/DFG/__init__.py +0 -0
  17. lambda_graphs/codeviews/SDFG/SDFG.py +135 -0
  18. lambda_graphs/codeviews/SDFG/SDFG_c.py +2041 -0
  19. lambda_graphs/codeviews/SDFG/SDFG_cpp.py +4030 -0
  20. lambda_graphs/codeviews/SDFG/SDFG_java.py +1618 -0
  21. lambda_graphs/codeviews/SDFG/__init__.py +0 -0
  22. lambda_graphs/codeviews/__init__.py +0 -0
  23. lambda_graphs/codeviews/combined_graph/__init__.py +0 -0
  24. lambda_graphs/codeviews/combined_graph/combined_driver.py +163 -0
  25. lambda_graphs/tree_parser/__init__.py +0 -0
  26. lambda_graphs/tree_parser/c_parser.py +565 -0
  27. lambda_graphs/tree_parser/cpp_parser.py +424 -0
  28. lambda_graphs/tree_parser/custom_parser.py +66 -0
  29. lambda_graphs/tree_parser/java_parser.py +254 -0
  30. lambda_graphs/tree_parser/parser_driver.py +54 -0
  31. lambda_graphs/utils/DFG_utils.py +44 -0
  32. lambda_graphs/utils/__init__.py +0 -0
  33. lambda_graphs/utils/c_nodes.py +431 -0
  34. lambda_graphs/utils/cpp_nodes.py +1331 -0
  35. lambda_graphs/utils/java_nodes.py +756 -0
  36. lambda_graphs/utils/multi_file_merger.py +444 -0
  37. lambda_graphs/utils/postprocessor.py +83 -0
  38. lambda_graphs/utils/preprocessor.py +68 -0
  39. lambda_graphs/utils/src_parser.py +23 -0
  40. lambda_graphs-0.1.4.dist-info/METADATA +380 -0
  41. lambda_graphs-0.1.4.dist-info/RECORD +45 -0
  42. lambda_graphs-0.1.4.dist-info/WHEEL +5 -0
  43. lambda_graphs-0.1.4.dist-info/entry_points.txt +2 -0
  44. lambda_graphs-0.1.4.dist-info/licenses/LICENSE +21 -0
  45. lambda_graphs-0.1.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,4030 @@
1
+ import copy
2
+ import time
3
+ from collections import defaultdict
4
+
5
+ import networkx as nx
6
+ from deepdiff import DeepDiff
7
+ from loguru import logger
8
+
9
+ from ...utils.cpp_nodes import statement_types
10
+ from ...utils.src_parser import traverse_tree
11
+
12
+ assignment = ["assignment_expression"]
13
+ def_statement = ["init_declarator"]
14
+ declaration_statement = ["declaration"]
15
+ increment_statement = ["update_expression"]
16
+ variable_type = ["identifier", "this", "qualified_identifier"]
17
+ function_calls = ["call_expression"]
18
+ method_calls = ["call_expression"]
19
+ literal_types = [
20
+ "number_literal",
21
+ "string_literal",
22
+ "char_literal",
23
+ "raw_string_literal",
24
+ "true",
25
+ "false",
26
+ "nullptr",
27
+ "null",
28
+ ]
29
+
30
+
31
+ input_functions = [
32
+ "scanf",
33
+ "gets",
34
+ "fgets",
35
+ "getline",
36
+ "fscanf",
37
+ "sscanf",
38
+ "fread",
39
+ "read",
40
+ "recv",
41
+ "recvfrom",
42
+ "getchar",
43
+ "fgetc",
44
+ "cin",
45
+ "std::cin",
46
+ ]
47
+ inner_types = ["declaration", "expression_statement"]
48
+ handled_types = (
49
+ assignment
50
+ + def_statement
51
+ + increment_statement
52
+ + function_calls
53
+ + [
54
+ "function_definition",
55
+ "return_statement",
56
+ "for_statement",
57
+ "for_range_loop",
58
+ "switch_statement",
59
+ ]
60
+ )
61
+
62
+ debug = False
63
+
64
+
65
+ def st(child):
66
+ """Get text from AST node"""
67
+ if child is None:
68
+ return ""
69
+ else:
70
+ return child.text.decode()
71
+
72
+
73
+ def get_index(node, index):
74
+ """Get unique index for AST node"""
75
+ try:
76
+ return index[(node.start_point, node.end_point, node.type)]
77
+ except:
78
+ return None
79
+
80
+
81
+ def read_index(index, idx):
82
+ """Get AST node key from index value"""
83
+ return list(index.keys())[list(index.values()).index(idx)]
84
+
85
+
86
+ def scope_check(parent_scope, child_scope):
87
+ """
88
+ Check if a definition's scope can reach a use's scope.
89
+
90
+ A definition at scope [0, 1, 2] can reach uses at:
91
+ - [0, 1, 2] (same scope)
92
+ - [0, 1, 2, 3] (nested scope)
93
+
94
+ But NOT:
95
+ - [0, 1] (parent scope)
96
+ - [0, 1, 3] (sibling scope)
97
+ """
98
+ for p in parent_scope:
99
+ if p not in child_scope:
100
+ return False
101
+ return True
102
+
103
+
104
+ def set_add(lst, item):
105
+ """Add item to list if not already present (set-like behavior)"""
106
+ for entry in lst:
107
+ if item == entry:
108
+ return
109
+ lst.append(item)
110
+
111
+
112
+ def set_union(first_list, second_list):
113
+ """Union of two lists (set-like)"""
114
+ resulting_list = list(first_list)
115
+ resulting_list.extend(x for x in second_list if x not in resulting_list)
116
+ return resulting_list
117
+
118
+
119
+ def set_difference(first_list, second_list):
120
+ """Difference of two lists (set-like)"""
121
+ return [item for item in first_list if item not in second_list]
122
+
123
+
124
+ def return_first_parent_of_types(node, parent_types, stop_types=None):
125
+ """Find first parent of given types"""
126
+ if stop_types is None:
127
+ stop_types = []
128
+
129
+ if node.type in parent_types:
130
+ return node
131
+
132
+ while node.parent is not None:
133
+ if node.type in stop_types:
134
+ return None
135
+ if node.parent.type in parent_types:
136
+ return node.parent
137
+ node = node.parent
138
+
139
+ return None
140
+
141
+
142
+ def is_node_inside_loop(ast_node):
143
+ """
144
+ Check if an AST node is inside a loop structure.
145
+
146
+ Args:
147
+ ast_node: AST node to check
148
+
149
+ Returns:
150
+ True if node is inside a loop (for/while/do-while), False otherwise
151
+ """
152
+ if ast_node is None:
153
+ return False
154
+
155
+ current = ast_node
156
+ while current.parent is not None:
157
+ if current.parent.type in [
158
+ "for_statement",
159
+ "while_statement",
160
+ "do_statement",
161
+ "for_range_loop",
162
+ ]:
163
+ return True
164
+ current = current.parent
165
+ return False
166
+
167
+
168
+ def get_loop_condition_node(ast_node, parser):
169
+ """
170
+ Get the loop condition CFG node for a node inside a loop body.
171
+
172
+ Args:
173
+ ast_node: AST node inside a loop body
174
+ parser: Parser with index
175
+
176
+ Returns:
177
+ Tuple (is_in_loop_body, loop_condition_id):
178
+ - is_in_loop_body: True if node is inside a loop body
179
+ - loop_condition_id: CFG node ID of the loop condition, or None
180
+ """
181
+ if ast_node is None:
182
+ return False, None
183
+
184
+ current = ast_node
185
+ while current.parent is not None:
186
+ parent_type = current.parent.type
187
+
188
+ if parent_type in [
189
+ "for_statement",
190
+ "while_statement",
191
+ "do_statement",
192
+ "for_range_loop",
193
+ ]:
194
+ loop_condition_id = get_index(current.parent, parser.index)
195
+ return True, loop_condition_id
196
+
197
+ current = current.parent
198
+
199
+ return False, None
200
+
201
+
202
+ def get_variable_type(parser, node):
203
+ """
204
+ Get the type of a variable from parser's symbol table.
205
+
206
+ Args:
207
+ parser: C++ parser with symbol table
208
+ node: AST node representing the variable
209
+
210
+ Returns:
211
+ String representing the type, or None if not found
212
+ """
213
+ var_index = get_index(node, parser.index)
214
+ if var_index is None:
215
+ return None
216
+
217
+ if var_index in parser.declaration_map:
218
+ decl_index = parser.declaration_map[var_index]
219
+ if decl_index in parser.symbol_table.get("data_type", {}):
220
+ return parser.symbol_table["data_type"][decl_index]
221
+
222
+ if var_index in parser.symbol_table.get("data_type", {}):
223
+ return parser.symbol_table["data_type"][var_index]
224
+
225
+ return None
226
+
227
+
228
+ def is_primitive_type(type_string):
229
+ """
230
+ Check if a type string represents a C++ primitive type.
231
+
232
+ Args:
233
+ type_string: String representing the type
234
+
235
+ Returns:
236
+ True if primitive, False otherwise
237
+ """
238
+ if type_string is None:
239
+ return False
240
+
241
+ primitive_types = {
242
+ "int",
243
+ "short",
244
+ "long",
245
+ "char",
246
+ "wchar_t",
247
+ "char8_t",
248
+ "char16_t",
249
+ "char32_t",
250
+ "signed",
251
+ "unsigned",
252
+ "int8_t",
253
+ "int16_t",
254
+ "int32_t",
255
+ "int64_t",
256
+ "uint8_t",
257
+ "uint16_t",
258
+ "uint32_t",
259
+ "uint64_t",
260
+ "int_fast8_t",
261
+ "int_fast16_t",
262
+ "int_fast32_t",
263
+ "int_fast64_t",
264
+ "uint_fast8_t",
265
+ "uint_fast16_t",
266
+ "uint_fast32_t",
267
+ "uint_fast64_t",
268
+ "int_least8_t",
269
+ "int_least16_t",
270
+ "int_least32_t",
271
+ "int_least64_t",
272
+ "uint_least8_t",
273
+ "uint_least16_t",
274
+ "uint_least32_t",
275
+ "uint_least64_t",
276
+ "intmax_t",
277
+ "uintmax_t",
278
+ "intptr_t",
279
+ "uintptr_t",
280
+ "size_t",
281
+ "ssize_t",
282
+ "ptrdiff_t",
283
+ "float",
284
+ "double",
285
+ "bool",
286
+ "void",
287
+ "DWORD",
288
+ "WORD",
289
+ "BYTE",
290
+ }
291
+
292
+ type_clean = (
293
+ type_string.strip().replace("const", "").replace("volatile", "").strip()
294
+ )
295
+
296
+ if type_clean in primitive_types:
297
+ return True
298
+
299
+ tokens = type_clean.split()
300
+ if all(token in primitive_types for token in tokens):
301
+ return True
302
+
303
+ for prim in primitive_types:
304
+ if prim in type_clean:
305
+ if type_clean == prim or type_clean.startswith(prim + " "):
306
+ return True
307
+
308
+ return False
309
+
310
+
311
+ def is_class_or_struct_type(parser, type_string):
312
+ """
313
+ Check if a type string represents a class or struct type.
314
+
315
+ Args:
316
+ parser: C++ parser with symbol table
317
+ type_string: String representing the type
318
+
319
+ Returns:
320
+ True if class/struct, False otherwise
321
+ """
322
+ if type_string is None:
323
+ return False
324
+
325
+ if not is_primitive_type(type_string):
326
+ if hasattr(parser, "records") and type_string in parser.records:
327
+ return True
328
+
329
+ if "std::" in type_string or "::" in type_string:
330
+ return True
331
+
332
+ return True
333
+
334
+ return False
335
+
336
+
337
+ def recursively_get_children_of_types(
338
+ node, st_types, check_list=None, index=None, result=None, stop_types=None
339
+ ):
340
+ """Recursively find child nodes of given types"""
341
+ if isinstance(st_types, str):
342
+ st_types = [st_types]
343
+ if stop_types is None:
344
+ stop_types = []
345
+ if result is None:
346
+ result = []
347
+
348
+ if node.type in stop_types:
349
+ return result
350
+
351
+ if check_list and index:
352
+ result.extend(
353
+ [
354
+ child
355
+ for child in node.children
356
+ if child.type in st_types and get_index(child, index) in check_list
357
+ ]
358
+ )
359
+ else:
360
+ result.extend([child for child in node.children if child.type in st_types])
361
+
362
+ for child in node.named_children:
363
+ if child.type not in stop_types:
364
+ if (
365
+ "qualified_identifier" in st_types
366
+ and child.type == "qualified_identifier"
367
+ ):
368
+ continue
369
+ result = recursively_get_children_of_types(
370
+ child,
371
+ st_types,
372
+ result=result,
373
+ stop_types=stop_types,
374
+ index=index,
375
+ check_list=check_list,
376
+ )
377
+
378
+ return result
379
+
380
+
381
+ class Identifier:
382
+ """Represents a variable at a specific line with scope information"""
383
+
384
+ def __init__(
385
+ self,
386
+ parser,
387
+ node,
388
+ line=None,
389
+ declaration=False,
390
+ full_ref=None,
391
+ method_call=False,
392
+ has_initializer=False,
393
+ is_pointer_modification_at_call_site=False,
394
+ ):
395
+ self.core = st(node)
396
+ self.unresolved_name = st(full_ref) if full_ref else st(node)
397
+ self.base_name = self._resolve_name(node, full_ref, parser)
398
+ self.name = self.base_name
399
+ self.line = line
400
+ self.declaration = declaration
401
+ self.has_initializer = has_initializer
402
+ self.method_call = method_call
403
+ self.is_pointer_modification_at_call_site = is_pointer_modification_at_call_site
404
+ if method_call and node.type == "qualified_identifier":
405
+ self.satisfied = False
406
+ else:
407
+ self.satisfied = method_call
408
+
409
+ class_node = return_first_parent_of_types(
410
+ node, ["class_specifier", "struct_specifier"]
411
+ )
412
+ self.parent_class = None
413
+ self.is_member_access = False
414
+
415
+ if class_node is not None:
416
+ class_name_node = None
417
+ for child in class_node.children:
418
+ if child.type == "type_identifier":
419
+ class_name_node = child
420
+ break
421
+ if class_name_node:
422
+ self.parent_class = st(class_name_node)
423
+
424
+ parent = node.parent
425
+ while parent and parent != class_node:
426
+ if parent.type == "function_definition":
427
+ if full_ref is None or full_ref.type not in [
428
+ "field_expression",
429
+ "pointer_expression",
430
+ ]:
431
+ self.is_member_access = True
432
+ break
433
+ parent = parent.parent
434
+
435
+ variable_index = get_index(node, parser.index)
436
+
437
+ if hasattr(node, "qualified_name"):
438
+ if hasattr(node, "real_node"):
439
+ variable_index = get_index(node.real_node, parser.index)
440
+
441
+ if variable_index is None and node.type == "qualified_identifier":
442
+ innermost = extract_identifier_from_declarator(node)
443
+ if innermost:
444
+ variable_index = get_index(innermost, parser.index)
445
+
446
+ if variable_index and variable_index in parser.symbol_table["scope_map"]:
447
+ self.variable_scope = parser.symbol_table["scope_map"][variable_index]
448
+ if variable_index in parser.declaration_map:
449
+ decl_index = parser.declaration_map[variable_index]
450
+ self.scope = parser.symbol_table["scope_map"].get(decl_index, [0])
451
+ else:
452
+ self.scope = [0]
453
+
454
+ if declaration:
455
+ self.scope = self.variable_scope
456
+ else:
457
+ if line and line in parser.symbol_table.get("scope_map", {}):
458
+ self.variable_scope = parser.symbol_table["scope_map"][line]
459
+ self.scope = self.variable_scope
460
+ elif line:
461
+ self.variable_scope = [0, 11, 12]
462
+ self.scope = [0]
463
+ else:
464
+ self.variable_scope = [0]
465
+ self.scope = [0]
466
+
467
+ if line is not None:
468
+ self.real_line_no = read_index(parser.index, line)[0][0]
469
+
470
+ if self.is_member_access and self.parent_class:
471
+ self.name = f"{self.parent_class}::{self.base_name}"
472
+
473
+ def _resolve_name(self, node, full_ref, parser):
474
+ """Resolve identifier name for C++"""
475
+ if full_ref is None:
476
+ return st(node)
477
+
478
+ if full_ref.type == "field_expression":
479
+ argument = full_ref.child_by_field_name("argument")
480
+ field = full_ref.child_by_field_name("field")
481
+
482
+ if argument:
483
+ arg_text = st(argument)
484
+ field_text = st(field) if field else ""
485
+ return arg_text + "." + field_text
486
+ return st(full_ref)
487
+
488
+ if full_ref.type == "pointer_expression":
489
+ arg = full_ref.child_by_field_name("argument")
490
+ return "*" + st(arg) if arg else st(full_ref)
491
+
492
+ if full_ref.type == "subscript_expression":
493
+ arg = full_ref.child_by_field_name("argument")
494
+ return st(arg) if arg else st(full_ref)
495
+
496
+ if full_ref.type == "unary_expression":
497
+ for child in full_ref.children:
498
+ if child.type == "&":
499
+ arg = full_ref.child_by_field_name("argument")
500
+ return st(arg) if arg else st(full_ref)
501
+
502
+ if full_ref.type == "qualified_identifier":
503
+ qualified_text = st(full_ref)
504
+ if "::" in qualified_text:
505
+ return qualified_text.split("::")[-1]
506
+ return qualified_text
507
+
508
+ if st(node) == "this":
509
+ return "this"
510
+
511
+ return st(node)
512
+
513
+ def __eq__(self, other):
514
+ return (
515
+ self.name == other.name
516
+ and self.line == other.line
517
+ and sorted(self.scope) == sorted(other.scope)
518
+ and self.method_call == other.method_call
519
+ )
520
+
521
+ def __hash__(self):
522
+ return hash((self.name, self.line, str(self.scope), self.method_call))
523
+
524
+ def __str__(self):
525
+ result = [self.name]
526
+ if self.line:
527
+ result += [str(self.real_line_no)]
528
+ result += ["|".join(map(str, self.scope))]
529
+ else:
530
+ result += ["?"]
531
+ if self.method_call:
532
+ result += ["()"]
533
+ return f"{{{','.join(result)}}}"
534
+
535
+
536
+ class Literal:
537
+ """Represents a literal constant (number, string, etc.) as a data flow source"""
538
+
539
+ def __init__(self, parser, node, line=None):
540
+ self.core = st(node)
541
+ self.name = f"LITERAL_{st(node)}"
542
+ self.value = st(node)
543
+ self.line = line
544
+ self.declaration = True
545
+ self.satisfied = False
546
+ self.scope = [0]
547
+ self.variable_scope = [0]
548
+ self.method_call = False
549
+
550
+ if line is not None:
551
+ self.real_line_no = read_index(parser.index, line)[0][0]
552
+
553
+ def __eq__(self, other):
554
+ return self.name == other.name and self.line == other.line
555
+
556
+ def __hash__(self):
557
+ return hash((self.name, self.line))
558
+
559
+ def __str__(self):
560
+ result = [f"Literal({self.value})"]
561
+ if self.line:
562
+ result += [str(self.real_line_no)]
563
+ else:
564
+ result += ["?"]
565
+ return f"{{{','.join(result)}}}"
566
+
567
+
568
+ def get_namespace_for_node(node, parser):
569
+ """
570
+ Get the namespace that contains this node, if any.
571
+
572
+ Returns the namespace name or None if not in a namespace.
573
+ """
574
+ parent = node.parent
575
+ while parent:
576
+ if parent.type == "namespace_definition":
577
+ for child in parent.children:
578
+ if child.type == "namespace_identifier":
579
+ return st(child)
580
+ parent = parent.parent
581
+ return None
582
+
583
+
584
+ def extract_identifier_from_declarator(declarator_node):
585
+ """Extract identifier from declarator (may be wrapped in pointer/array/reference/qualified)"""
586
+ if declarator_node.type == "identifier":
587
+ return declarator_node
588
+ elif declarator_node.type == "qualified_identifier":
589
+ for child in declarator_node.children:
590
+ if child.type in ["identifier", "qualified_identifier"]:
591
+ result = extract_identifier_from_declarator(child)
592
+ if result:
593
+ return result
594
+ return None
595
+ elif declarator_node.type == "pointer_declarator":
596
+ for child in declarator_node.children:
597
+ if child.type in [
598
+ "identifier",
599
+ "pointer_declarator",
600
+ "array_declarator",
601
+ "reference_declarator",
602
+ "qualified_identifier",
603
+ ]:
604
+ return extract_identifier_from_declarator(child)
605
+ elif declarator_node.type == "reference_declarator":
606
+ for child in declarator_node.children:
607
+ if child.type in [
608
+ "identifier",
609
+ "pointer_declarator",
610
+ "array_declarator",
611
+ "reference_declarator",
612
+ "qualified_identifier",
613
+ ]:
614
+ return extract_identifier_from_declarator(child)
615
+ elif declarator_node.type == "array_declarator":
616
+ if declarator_node.children:
617
+ return extract_identifier_from_declarator(declarator_node.children[0])
618
+ elif declarator_node.type == "function_declarator":
619
+ for child in declarator_node.children:
620
+ if child.type in [
621
+ "identifier",
622
+ "pointer_declarator",
623
+ "parenthesized_declarator",
624
+ "qualified_identifier",
625
+ ]:
626
+ return extract_identifier_from_declarator(child)
627
+ elif declarator_node.type == "parenthesized_declarator":
628
+ for child in declarator_node.children:
629
+ if child.type in [
630
+ "identifier",
631
+ "pointer_declarator",
632
+ "array_declarator",
633
+ "reference_declarator",
634
+ "qualified_identifier",
635
+ ]:
636
+ return extract_identifier_from_declarator(child)
637
+ return None
638
+
639
+
640
+ def extract_param_identifier(param_node):
641
+ """Extract identifier from parameter_declaration"""
642
+ for child in param_node.children:
643
+ if child.type == "identifier":
644
+ return child
645
+ elif child.type in [
646
+ "pointer_declarator",
647
+ "array_declarator",
648
+ "function_declarator",
649
+ "reference_declarator",
650
+ ]:
651
+ return extract_identifier_from_declarator(child)
652
+ return None
653
+
654
+
655
+ def is_reference_variable(parser, node):
656
+ if node is None or node.type not in ["identifier"]:
657
+ return False
658
+
659
+ var_index = get_index(node, parser.index)
660
+ if var_index is None:
661
+ return False
662
+
663
+ if var_index not in parser.declaration_map:
664
+ return False
665
+
666
+ decl_index = parser.declaration_map[var_index]
667
+
668
+ def find_node_by_index(root, target_index):
669
+ stack = [root]
670
+ while stack:
671
+ current = stack.pop()
672
+ node_idx = get_index(current, parser.index)
673
+ if node_idx == target_index:
674
+ return current
675
+ for child in current.children:
676
+ stack.append(child)
677
+ return None
678
+
679
+ decl_node = find_node_by_index(parser.tree.root_node, decl_index)
680
+
681
+ if decl_node:
682
+ parent = decl_node.parent
683
+ depth = 0
684
+ while parent and depth < 5:
685
+ if parent.type == "reference_declarator":
686
+ return True
687
+ if parent.type in [
688
+ "parameter_declaration",
689
+ "declaration",
690
+ "function_definition",
691
+ "translation_unit",
692
+ ]:
693
+ break
694
+ depth += 1
695
+ parent = parent.parent
696
+
697
+ return False
698
+
699
+
700
+ def extract_operator_text(assign_node, left_node, right_node):
701
+ left_text = left_node.text
702
+ right_text = right_node.text
703
+ operator_bytes = (
704
+ assign_node.text.split(left_text, 1)[-1].rsplit(right_text, 1)[0].strip()
705
+ )
706
+ return operator_bytes.decode()
707
+
708
+
709
+ def add_entry(
710
+ parser,
711
+ rda_table,
712
+ statement_id,
713
+ used=None,
714
+ defined=None,
715
+ declaration=False,
716
+ core=None,
717
+ method_call=False,
718
+ has_initializer=False,
719
+ is_pointer_modification_at_call_site=False,
720
+ ):
721
+ if statement_id not in rda_table:
722
+ rda_table[statement_id] = defaultdict(list)
723
+
724
+ if not used and not defined:
725
+ return
726
+
727
+ current_node = used or defined
728
+ if core is None:
729
+ core = current_node
730
+
731
+ if current_node.type in literal_types:
732
+ if used:
733
+ set_add(
734
+ rda_table[statement_id]["use"],
735
+ Literal(parser, current_node, statement_id),
736
+ )
737
+ return
738
+
739
+ if hasattr(current_node, "qualified_name"):
740
+ if defined is not None:
741
+ identifier = Identifier(
742
+ parser,
743
+ current_node,
744
+ statement_id,
745
+ full_ref=current_node,
746
+ declaration=declaration,
747
+ method_call=method_call,
748
+ has_initializer=has_initializer,
749
+ )
750
+ identifier.name = current_node.qualified_name
751
+ set_add(rda_table[statement_id]["def"], identifier)
752
+ return
753
+
754
+ if current_node.type == "field_expression":
755
+ argument = current_node.child_by_field_name("argument")
756
+
757
+ is_method_call = (
758
+ current_node.parent and current_node.parent.type == "call_expression"
759
+ )
760
+
761
+ if is_method_call:
762
+ if defined is not None:
763
+ set_add(
764
+ rda_table[statement_id]["def"],
765
+ Identifier(
766
+ parser,
767
+ argument,
768
+ statement_id,
769
+ full_ref=current_node,
770
+ declaration=declaration,
771
+ method_call=True,
772
+ has_initializer=has_initializer,
773
+ ),
774
+ )
775
+ else:
776
+ set_add(
777
+ rda_table[statement_id]["use"],
778
+ Identifier(
779
+ parser, argument, full_ref=current_node, method_call=True
780
+ ),
781
+ )
782
+ return
783
+
784
+ if defined is not None:
785
+ if argument:
786
+ set_add(
787
+ rda_table[statement_id]["def"],
788
+ Identifier(
789
+ parser,
790
+ argument,
791
+ statement_id,
792
+ full_ref=None,
793
+ declaration=declaration,
794
+ method_call=method_call,
795
+ has_initializer=has_initializer,
796
+ ),
797
+ )
798
+ else:
799
+ if argument:
800
+ set_add(
801
+ rda_table[statement_id]["use"],
802
+ Identifier(
803
+ parser, argument, full_ref=None, method_call=method_call
804
+ ),
805
+ )
806
+ return
807
+
808
+ if used and used.type == "unary_expression":
809
+ operator = None
810
+ argument = None
811
+ for child in used.children:
812
+ if child.type == "&":
813
+ operator = child
814
+ elif child.is_named:
815
+ argument = child
816
+
817
+ if operator is not None and argument is not None:
818
+ if argument.type in variable_type:
819
+ arg_index = get_index(argument, parser.index)
820
+ if arg_index and arg_index in parser.symbol_table["scope_map"]:
821
+ set_add(
822
+ rda_table[statement_id]["use"],
823
+ Identifier(parser, argument, full_ref=argument),
824
+ )
825
+ elif arg_index and arg_index in parser.method_map:
826
+ set_add(
827
+ rda_table[statement_id]["use"],
828
+ Identifier(parser, argument, full_ref=argument),
829
+ )
830
+ return
831
+
832
+ if current_node.type == "pointer_expression":
833
+ is_address_of = False
834
+ is_dereference = False
835
+
836
+ if current_node.children:
837
+ operator = current_node.children[0]
838
+ if operator.type == "&":
839
+ is_address_of = True
840
+ elif operator.type == "*":
841
+ is_dereference = True
842
+
843
+ pointer = current_node.child_by_field_name("argument")
844
+
845
+ if is_address_of:
846
+ if pointer and pointer.type in variable_type:
847
+ pointer_index = get_index(pointer, parser.index)
848
+ if pointer_index and pointer_index in parser.symbol_table["scope_map"]:
849
+ set_add(
850
+ rda_table[statement_id]["use"],
851
+ Identifier(parser, pointer, full_ref=pointer),
852
+ )
853
+ return
854
+
855
+ elif is_dereference:
856
+ if defined is not None:
857
+ pointer_index = get_index(pointer, parser.index)
858
+ if pointer_index and pointer_index in parser.symbol_table["scope_map"]:
859
+ set_add(
860
+ rda_table[statement_id]["use"],
861
+ Identifier(parser, pointer, full_ref=pointer),
862
+ )
863
+
864
+ set_add(
865
+ rda_table[statement_id]["def"],
866
+ Identifier(
867
+ parser,
868
+ pointer,
869
+ statement_id,
870
+ full_ref=core,
871
+ declaration=declaration,
872
+ has_initializer=has_initializer,
873
+ ),
874
+ )
875
+ else:
876
+ set_add(
877
+ rda_table[statement_id]["use"],
878
+ Identifier(parser, pointer, full_ref=core),
879
+ )
880
+ return
881
+
882
+ if current_node.type == "subscript_expression":
883
+ array = current_node.child_by_field_name("argument")
884
+ index_expr = current_node.child_by_field_name("index")
885
+
886
+ if index_expr is None:
887
+ for child in current_node.children:
888
+ if child.type == "subscript_argument_list":
889
+ if child.named_children:
890
+ index_expr = child.named_children[0]
891
+ break
892
+
893
+ if defined is not None:
894
+ set_add(
895
+ rda_table[statement_id]["def"],
896
+ Identifier(
897
+ parser,
898
+ array,
899
+ statement_id,
900
+ full_ref=core,
901
+ declaration=declaration,
902
+ has_initializer=has_initializer,
903
+ ),
904
+ )
905
+ set_add(
906
+ rda_table[statement_id]["use"], Identifier(parser, array, full_ref=core)
907
+ )
908
+
909
+ if index_expr:
910
+ if index_expr.type in variable_type:
911
+ index_id = get_index(index_expr, parser.index)
912
+ if index_id and index_id in parser.symbol_table["scope_map"]:
913
+ set_add(
914
+ rda_table[statement_id]["use"],
915
+ Identifier(parser, index_expr, full_ref=index_expr),
916
+ )
917
+ elif index_expr.type in literal_types:
918
+ set_add(
919
+ rda_table[statement_id]["use"],
920
+ Literal(parser, index_expr, statement_id),
921
+ )
922
+ else:
923
+ identifiers_in_index = recursively_get_children_of_types(
924
+ index_expr,
925
+ variable_type + ["field_expression"],
926
+ index=parser.index,
927
+ check_list=parser.symbol_table["scope_map"],
928
+ )
929
+ for identifier in identifiers_in_index:
930
+ set_add(
931
+ rda_table[statement_id]["use"],
932
+ Identifier(parser, identifier, full_ref=identifier),
933
+ )
934
+ literals_in_index = recursively_get_children_of_types(
935
+ index_expr, literal_types, index=parser.index
936
+ )
937
+ for literal in literals_in_index:
938
+ set_add(
939
+ rda_table[statement_id]["use"],
940
+ Literal(parser, literal, statement_id),
941
+ )
942
+ return
943
+
944
+ if current_node.type == "qualified_identifier":
945
+ innermost_id = extract_identifier_from_declarator(current_node)
946
+
947
+ if method_call:
948
+ if defined is not None:
949
+ set_add(
950
+ rda_table[statement_id]["def"],
951
+ Identifier(
952
+ parser,
953
+ current_node,
954
+ statement_id,
955
+ full_ref=current_node,
956
+ declaration=declaration,
957
+ method_call=method_call,
958
+ has_initializer=has_initializer,
959
+ ),
960
+ )
961
+ else:
962
+ set_add(
963
+ rda_table[statement_id]["use"],
964
+ Identifier(
965
+ parser,
966
+ current_node,
967
+ statement_id,
968
+ full_ref=current_node,
969
+ method_call=method_call,
970
+ ),
971
+ )
972
+ return
973
+
974
+ if innermost_id is None:
975
+ return
976
+
977
+ node_index = get_index(innermost_id, parser.index)
978
+ if node_index is None or node_index not in parser.symbol_table["scope_map"]:
979
+ if defined is not None:
980
+ set_add(
981
+ rda_table[statement_id]["def"],
982
+ Identifier(
983
+ parser,
984
+ current_node,
985
+ statement_id,
986
+ full_ref=current_node,
987
+ declaration=declaration,
988
+ method_call=method_call,
989
+ has_initializer=has_initializer,
990
+ ),
991
+ )
992
+ else:
993
+ set_add(
994
+ rda_table[statement_id]["use"],
995
+ Identifier(
996
+ parser,
997
+ current_node,
998
+ statement_id,
999
+ full_ref=current_node,
1000
+ method_call=method_call,
1001
+ ),
1002
+ )
1003
+ return
1004
+
1005
+ if defined is not None:
1006
+ set_add(
1007
+ rda_table[statement_id]["def"],
1008
+ Identifier(
1009
+ parser,
1010
+ innermost_id,
1011
+ statement_id,
1012
+ full_ref=current_node,
1013
+ declaration=declaration,
1014
+ method_call=method_call,
1015
+ has_initializer=has_initializer,
1016
+ ),
1017
+ )
1018
+ else:
1019
+ set_add(
1020
+ rda_table[statement_id]["use"],
1021
+ Identifier(
1022
+ parser, innermost_id, full_ref=current_node, method_call=method_call
1023
+ ),
1024
+ )
1025
+ return
1026
+
1027
+ if current_node.type == "this":
1028
+ if used:
1029
+ set_add(
1030
+ rda_table[statement_id]["use"],
1031
+ Identifier(parser, current_node, full_ref=current_node),
1032
+ )
1033
+ return
1034
+
1035
+ node_index = get_index(current_node, parser.index)
1036
+ if node_index is None or node_index not in parser.symbol_table["scope_map"]:
1037
+ if defined is not None:
1038
+ set_add(
1039
+ rda_table[statement_id]["def"],
1040
+ Identifier(
1041
+ parser,
1042
+ defined,
1043
+ statement_id,
1044
+ full_ref=core,
1045
+ declaration=declaration,
1046
+ method_call=method_call,
1047
+ has_initializer=has_initializer,
1048
+ ),
1049
+ )
1050
+ else:
1051
+ set_add(
1052
+ rda_table[statement_id]["use"],
1053
+ Identifier(
1054
+ parser, used, statement_id, full_ref=core, method_call=method_call
1055
+ ),
1056
+ )
1057
+ return
1058
+
1059
+ if defined is not None:
1060
+ set_add(
1061
+ rda_table[statement_id]["def"],
1062
+ Identifier(
1063
+ parser,
1064
+ defined,
1065
+ statement_id,
1066
+ full_ref=core,
1067
+ declaration=declaration,
1068
+ method_call=method_call,
1069
+ has_initializer=has_initializer,
1070
+ is_pointer_modification_at_call_site=is_pointer_modification_at_call_site,
1071
+ ),
1072
+ )
1073
+ else:
1074
+ set_add(
1075
+ rda_table[statement_id]["use"],
1076
+ Identifier(
1077
+ parser, used, statement_id, full_ref=core, method_call=method_call
1078
+ ),
1079
+ )
1080
+
1081
+
1082
+ def discover_lambdas(parser, CFG_results):
1083
+ lambda_map = {}
1084
+ index = parser.index
1085
+ tree = parser.tree
1086
+ cfg_nodes = CFG_results.graph.nodes
1087
+
1088
+ for node in traverse_tree(tree, ["lambda_expression"]):
1089
+ if node.type != "lambda_expression":
1090
+ continue
1091
+
1092
+ parent = node.parent
1093
+ variable_name = None
1094
+ definition_node_id = None
1095
+
1096
+ if parent and parent.type == "init_declarator":
1097
+ declarator = parent.child_by_field_name("declarator")
1098
+ if declarator and declarator.type == "identifier":
1099
+ variable_name = st(declarator)
1100
+
1101
+ statement = parent.parent
1102
+ if statement:
1103
+ definition_node_id = get_index(statement, index)
1104
+
1105
+ if not variable_name or not definition_node_id:
1106
+ continue
1107
+
1108
+ body_nodes = []
1109
+ for child in node.children:
1110
+ if child.type == "compound_statement":
1111
+ for stmt in child.named_children:
1112
+ stmt_id = get_index(stmt, index)
1113
+ if stmt_id and stmt_id in cfg_nodes:
1114
+ body_nodes.append(stmt_id)
1115
+ break
1116
+
1117
+ captures = []
1118
+ for child in node.children:
1119
+ if child.type == "lambda_capture_specifier":
1120
+ for capture in child.named_children:
1121
+ if capture.type in variable_type:
1122
+ captures.append(st(capture))
1123
+ break
1124
+
1125
+ lambda_node_id = get_index(node, index)
1126
+
1127
+ lambda_map[variable_name] = {
1128
+ "definition_node": definition_node_id,
1129
+ "lambda_node": lambda_node_id,
1130
+ "body_nodes": body_nodes,
1131
+ "captures": captures,
1132
+ }
1133
+
1134
+ if debug:
1135
+ logger.info(
1136
+ f"Discovered lambda: {variable_name} at node {definition_node_id}, "
1137
+ f"body nodes: {body_nodes}, captures: {captures}"
1138
+ )
1139
+
1140
+ return lambda_map
1141
+
1142
+
1143
+ def is_return_value_used(call_expr_statement):
1144
+ if call_expr_statement.type == "declaration":
1145
+ for child in call_expr_statement.children:
1146
+ if child.type == "init_declarator":
1147
+ return True
1148
+ return False
1149
+
1150
+ if call_expr_statement.type == "expression_statement":
1151
+ if len(call_expr_statement.named_children) == 1:
1152
+ child = call_expr_statement.named_children[0]
1153
+ if child.type == "call_expression":
1154
+ return False
1155
+
1156
+ parent = call_expr_statement.parent
1157
+ while parent:
1158
+ parent_type = parent.type
1159
+
1160
+ if parent_type in ["init_declarator", "assignment_expression"]:
1161
+ return True
1162
+
1163
+ if parent_type == "return_statement":
1164
+ return True
1165
+
1166
+ if parent_type == "argument_list":
1167
+ return True
1168
+
1169
+ if parent_type in [
1170
+ "if_statement",
1171
+ "while_statement",
1172
+ "for_statement",
1173
+ "do_statement",
1174
+ "switch_statement",
1175
+ ]:
1176
+ return True
1177
+
1178
+ if parent_type == "expression_statement":
1179
+ return False
1180
+
1181
+ parent = parent.parent
1182
+
1183
+ return False
1184
+
1185
+
1186
+ def collect_function_metadata(parser):
1187
+ """
1188
+ Collect metadata about function definitions.
1189
+
1190
+ Returns two dicts:
1191
+ - metadata_by_name: simple_name -> metadata (may overwrite for same-named functions)
1192
+ - metadata_by_id: func_def_id -> metadata (unique per function)
1193
+
1194
+ Each metadata entry contains:
1195
+ - "params": list of (param_name, is_pointer, is_reference, param_idx)
1196
+ - "node": the function_definition AST node
1197
+ - "func_name": the simple function name
1198
+
1199
+ Note: metadata_by_name is kept for backward compatibility but may lose information
1200
+ when multiple functions have the same name. Use metadata_by_id for precise lookup.
1201
+ """
1202
+ metadata_by_name = {}
1203
+ metadata_by_id = {}
1204
+ index = parser.index
1205
+
1206
+ for node in traverse_tree(parser.tree.root_node):
1207
+ if node.type == "function_definition":
1208
+ func_name = None
1209
+ params = []
1210
+
1211
+ for child in node.named_children:
1212
+ if child.type == "function_declarator":
1213
+ declarator = child.child_by_field_name("declarator")
1214
+ if declarator:
1215
+ if declarator.type in ["identifier", "field_identifier"]:
1216
+ func_name = st(declarator)
1217
+ elif declarator.type in [
1218
+ "pointer_declarator",
1219
+ "reference_declarator",
1220
+ ]:
1221
+ inner = declarator
1222
+ while inner and inner.type in [
1223
+ "pointer_declarator",
1224
+ "reference_declarator",
1225
+ ]:
1226
+ inner_declarator = inner.child_by_field_name(
1227
+ "declarator"
1228
+ )
1229
+ if inner_declarator:
1230
+ inner = inner_declarator
1231
+ else:
1232
+ break
1233
+ if inner and inner.type == "identifier":
1234
+ func_name = st(inner)
1235
+ elif declarator.type == "qualified_identifier":
1236
+ name_node = declarator.child_by_field_name("name")
1237
+ if name_node:
1238
+ func_name = st(name_node)
1239
+
1240
+ param_list = child.child_by_field_name("parameters")
1241
+ if param_list:
1242
+ param_idx = 0
1243
+ for param in param_list.named_children:
1244
+ if param.type == "parameter_declaration":
1245
+ param_name = None
1246
+ is_pointer = False
1247
+ is_reference = False
1248
+
1249
+ for p_child in param.named_children:
1250
+ if p_child.type in [
1251
+ "pointer_declarator",
1252
+ "reference_declarator",
1253
+ ]:
1254
+ if p_child.type == "pointer_declarator":
1255
+ is_pointer = True
1256
+ else:
1257
+ is_reference = True
1258
+ inner = p_child
1259
+ while inner:
1260
+ if inner.type == "identifier":
1261
+ param_name = st(inner)
1262
+ break
1263
+ elif inner.type in [
1264
+ "pointer_declarator",
1265
+ "reference_declarator",
1266
+ ]:
1267
+ inner_decl = inner.child_by_field_name(
1268
+ "declarator"
1269
+ )
1270
+ if inner_decl:
1271
+ inner = inner_decl
1272
+ else:
1273
+ if inner.named_children:
1274
+ for (
1275
+ child
1276
+ ) in inner.named_children:
1277
+ if (
1278
+ child.type
1279
+ == "identifier"
1280
+ ):
1281
+ param_name = st(child)
1282
+ break
1283
+ break
1284
+ else:
1285
+ break
1286
+ elif p_child.type == "identifier":
1287
+ if param_name is None:
1288
+ param_name = st(p_child)
1289
+
1290
+ if param_name:
1291
+ params.append(
1292
+ (
1293
+ param_name,
1294
+ is_pointer,
1295
+ is_reference,
1296
+ param_idx,
1297
+ )
1298
+ )
1299
+ param_idx += 1
1300
+ break
1301
+
1302
+ if func_name:
1303
+ func_def_id = get_index(node, index)
1304
+ meta = {"params": params, "node": node, "func_name": func_name}
1305
+ metadata_by_name[func_name] = meta
1306
+ if func_def_id is not None:
1307
+ metadata_by_id[func_def_id] = meta
1308
+
1309
+ return metadata_by_name, metadata_by_id
1310
+
1311
+
1312
+ def analyze_pointer_modifications(parser, function_metadata):
1313
+ modifications = {}
1314
+
1315
+ for func_name, meta in function_metadata.items():
1316
+ modified_params = set()
1317
+ func_node = meta["node"]
1318
+
1319
+ param_name_to_idx = {}
1320
+ for param_name, is_pointer, is_reference, param_idx in meta["params"]:
1321
+ if is_pointer or is_reference:
1322
+ param_name_to_idx[param_name] = param_idx
1323
+
1324
+ if not param_name_to_idx:
1325
+ modifications[func_name] = modified_params
1326
+ continue
1327
+
1328
+ for node in traverse_tree(func_node):
1329
+ if node.type == "assignment_expression":
1330
+ left = node.child_by_field_name("left")
1331
+ if left:
1332
+ if left.type == "pointer_expression":
1333
+ arg = left.child_by_field_name("argument")
1334
+ if arg and arg.type == "identifier":
1335
+ var_name = st(arg)
1336
+ if var_name in param_name_to_idx:
1337
+ modified_params.add(param_name_to_idx[var_name])
1338
+
1339
+ elif left.type == "subscript_expression":
1340
+ array_arg = left.child_by_field_name("argument")
1341
+ if array_arg and array_arg.type == "identifier":
1342
+ var_name = st(array_arg)
1343
+ if var_name in param_name_to_idx:
1344
+ modified_params.add(param_name_to_idx[var_name])
1345
+
1346
+ elif left.type == "field_expression":
1347
+ obj = left.child_by_field_name("argument")
1348
+ if obj and obj.type == "identifier":
1349
+ var_name = st(obj)
1350
+ if var_name in param_name_to_idx:
1351
+ modified_params.add(param_name_to_idx[var_name])
1352
+
1353
+ elif left.type == "identifier":
1354
+ var_name = st(left)
1355
+ if var_name in param_name_to_idx:
1356
+ modified_params.add(param_name_to_idx[var_name])
1357
+
1358
+ elif node.type == "update_expression":
1359
+ arg = node.child_by_field_name("argument")
1360
+ if arg:
1361
+ if arg.type == "pointer_expression":
1362
+ inner_arg = arg.child_by_field_name("argument")
1363
+ if inner_arg and inner_arg.type == "identifier":
1364
+ var_name = st(inner_arg)
1365
+ if var_name in param_name_to_idx:
1366
+ modified_params.add(param_name_to_idx[var_name])
1367
+ elif arg.type == "subscript_expression":
1368
+ array_arg = arg.child_by_field_name("argument")
1369
+ if array_arg and array_arg.type == "identifier":
1370
+ var_name = st(array_arg)
1371
+ if var_name in param_name_to_idx:
1372
+ modified_params.add(param_name_to_idx[var_name])
1373
+ elif arg.type == "field_expression":
1374
+ obj = arg.child_by_field_name("argument")
1375
+ if obj and obj.type == "identifier":
1376
+ var_name = st(obj)
1377
+ if var_name in param_name_to_idx:
1378
+ modified_params.add(param_name_to_idx[var_name])
1379
+ elif arg.type == "identifier":
1380
+ var_name = st(arg)
1381
+ if var_name in param_name_to_idx:
1382
+ modified_params.add(param_name_to_idx[var_name])
1383
+
1384
+ modifications[func_name] = modified_params
1385
+
1386
+ return modifications
1387
+
1388
+
1389
+ def build_rda_table(
1390
+ parser,
1391
+ CFG_results,
1392
+ lambda_map=None,
1393
+ function_metadata=None,
1394
+ pointer_modifications=None,
1395
+ ):
1396
+ if lambda_map is None:
1397
+ lambda_map = {}
1398
+
1399
+ rda_table = {}
1400
+ index = parser.index
1401
+ tree = parser.tree
1402
+
1403
+ inner_types_local = [
1404
+ "parenthesized_expression",
1405
+ "binary_expression",
1406
+ "unary_expression",
1407
+ ]
1408
+ handled_cases = [
1409
+ "compound_statement",
1410
+ "translation_unit",
1411
+ "class_specifier",
1412
+ "struct_specifier",
1413
+ "namespace_definition",
1414
+ ]
1415
+
1416
+ for root_node in traverse_tree(tree, []):
1417
+ if not root_node.is_named:
1418
+ continue
1419
+
1420
+ if root_node.type == "return_statement":
1421
+ parent_id = get_index(root_node, index)
1422
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
1423
+ continue
1424
+
1425
+ return_expr = (
1426
+ root_node.named_children[0] if root_node.named_children else None
1427
+ )
1428
+ if (
1429
+ return_expr
1430
+ and return_expr.type
1431
+ in variable_type
1432
+ + ["field_expression", "pointer_expression", "subscript_expression"]
1433
+ + literal_types
1434
+ ):
1435
+ add_entry(parser, rda_table, parent_id, used=return_expr)
1436
+ else:
1437
+ vars_used = recursively_get_children_of_types(
1438
+ root_node,
1439
+ variable_type
1440
+ + [
1441
+ "field_expression",
1442
+ "pointer_expression",
1443
+ "subscript_expression",
1444
+ ],
1445
+ index=parser.index,
1446
+ check_list=parser.symbol_table["scope_map"],
1447
+ )
1448
+ for var in vars_used:
1449
+ add_entry(parser, rda_table, parent_id, used=var)
1450
+
1451
+ literals_used = recursively_get_children_of_types(
1452
+ root_node, literal_types, index=parser.index
1453
+ )
1454
+ for literal in literals_used:
1455
+ add_entry(parser, rda_table, parent_id, used=literal)
1456
+
1457
+ elif root_node.type in def_statement:
1458
+ parent_statement = return_first_parent_of_types(
1459
+ root_node, statement_types["node_list_type"]
1460
+ )
1461
+ if parent_statement is None:
1462
+ continue
1463
+
1464
+ parent_id = get_index(parent_statement, index)
1465
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
1466
+ if parent_statement and parent_statement.type in inner_types_local:
1467
+ parent_statement = return_first_parent_of_types(
1468
+ parent_statement, statement_types["node_list_type"]
1469
+ )
1470
+ parent_id = get_index(parent_statement, index)
1471
+ elif parent_statement.type in handled_cases:
1472
+ continue
1473
+ else:
1474
+ continue
1475
+
1476
+ declarator = root_node.child_by_field_name("declarator")
1477
+ if declarator is None and len(root_node.children) > 0:
1478
+ declarator = root_node.children[0]
1479
+
1480
+ var_identifier = extract_identifier_from_declarator(declarator)
1481
+
1482
+ initializer = root_node.child_by_field_name("value")
1483
+ has_initializer = initializer is not None
1484
+
1485
+ if var_identifier:
1486
+ add_entry(
1487
+ parser,
1488
+ rda_table,
1489
+ parent_id,
1490
+ defined=var_identifier,
1491
+ declaration=True,
1492
+ has_initializer=has_initializer,
1493
+ )
1494
+
1495
+ if initializer:
1496
+ if initializer.type == "lambda_expression":
1497
+ for child in initializer.children:
1498
+ if child.type == "lambda_capture_specifier":
1499
+ for capture in child.named_children:
1500
+ if capture.type in variable_type:
1501
+ add_entry(
1502
+ parser, rda_table, parent_id, used=capture
1503
+ )
1504
+ elif (
1505
+ initializer.type
1506
+ in variable_type
1507
+ + [
1508
+ "field_expression",
1509
+ "pointer_expression",
1510
+ "subscript_expression",
1511
+ "unary_expression",
1512
+ ]
1513
+ + literal_types
1514
+ ):
1515
+ add_entry(parser, rda_table, parent_id, used=initializer)
1516
+ else:
1517
+ vars_used = recursively_get_children_of_types(
1518
+ initializer,
1519
+ variable_type + ["field_expression"],
1520
+ index=parser.index,
1521
+ check_list=parser.symbol_table["scope_map"],
1522
+ )
1523
+ for var in vars_used:
1524
+ add_entry(parser, rda_table, parent_id, used=var)
1525
+ literals_used = recursively_get_children_of_types(
1526
+ initializer, literal_types, index=parser.index
1527
+ )
1528
+ for literal in literals_used:
1529
+ add_entry(parser, rda_table, parent_id, used=literal)
1530
+
1531
+ elif root_node.type in declaration_statement:
1532
+ parent_id = get_index(root_node, index)
1533
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
1534
+ continue
1535
+
1536
+ has_init_declarator = any(
1537
+ child.type == "init_declarator" for child in root_node.named_children
1538
+ )
1539
+ if has_init_declarator:
1540
+ continue
1541
+
1542
+ for child in root_node.named_children:
1543
+ if child.type == "identifier":
1544
+ child_id = get_index(child, index)
1545
+ if child_id and child_id in parser.symbol_table["scope_map"]:
1546
+ add_entry(
1547
+ parser,
1548
+ rda_table,
1549
+ parent_id,
1550
+ defined=child,
1551
+ declaration=True,
1552
+ )
1553
+ elif child.type in [
1554
+ "pointer_declarator",
1555
+ "array_declarator",
1556
+ "reference_declarator",
1557
+ ]:
1558
+ var_identifier = extract_identifier_from_declarator(child)
1559
+ if var_identifier:
1560
+ var_id = get_index(var_identifier, index)
1561
+ if var_id and var_id in parser.symbol_table["scope_map"]:
1562
+ add_entry(
1563
+ parser,
1564
+ rda_table,
1565
+ parent_id,
1566
+ defined=var_identifier,
1567
+ declaration=True,
1568
+ )
1569
+
1570
+ elif root_node.type in assignment:
1571
+ parent_statement = return_first_parent_of_types(
1572
+ root_node, statement_types["node_list_type"]
1573
+ )
1574
+ if parent_statement is None:
1575
+ continue
1576
+
1577
+ parent_id = get_index(parent_statement, index)
1578
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
1579
+ if parent_statement and parent_statement.type in inner_types_local:
1580
+ parent_statement = return_first_parent_of_types(
1581
+ parent_statement, statement_types["node_list_type"]
1582
+ )
1583
+ parent_id = get_index(parent_statement, index)
1584
+ elif parent_statement.type in handled_cases:
1585
+ continue
1586
+ else:
1587
+ continue
1588
+
1589
+ left_node = root_node.child_by_field_name("left")
1590
+ right_node = root_node.child_by_field_name("right")
1591
+
1592
+ if left_node is None or right_node is None:
1593
+ continue
1594
+
1595
+ operator_text = extract_operator_text(root_node, left_node, right_node)
1596
+
1597
+ if operator_text != "=":
1598
+ add_entry(parser, rda_table, parent_id, used=left_node)
1599
+ else:
1600
+ if left_node.type == "field_expression":
1601
+ add_entry(parser, rda_table, parent_id, used=left_node)
1602
+ elif left_node.type in variable_type:
1603
+ var_type = get_variable_type(parser, left_node)
1604
+
1605
+ if is_class_or_struct_type(
1606
+ parser, var_type
1607
+ ) or is_reference_variable(parser, left_node):
1608
+ add_entry(parser, rda_table, parent_id, used=left_node)
1609
+ else:
1610
+ left_node_index = get_index(left_node, index)
1611
+ if (
1612
+ left_node_index
1613
+ and left_node_index in parser.symbol_table["scope_map"]
1614
+ ):
1615
+ is_init_declarator = False
1616
+ check_parent = root_node.parent
1617
+ while check_parent:
1618
+ if check_parent.type == "init_declarator":
1619
+ is_init_declarator = True
1620
+ break
1621
+ if check_parent.type in statement_types.get(
1622
+ "node_list_type", []
1623
+ ):
1624
+ break
1625
+ check_parent = check_parent.parent
1626
+
1627
+ if not is_init_declarator:
1628
+ add_entry(parser, rda_table, parent_id, used=left_node)
1629
+
1630
+ add_entry(parser, rda_table, parent_id, defined=left_node)
1631
+
1632
+ if (
1633
+ right_node.type
1634
+ in variable_type
1635
+ + [
1636
+ "field_expression",
1637
+ "pointer_expression",
1638
+ "subscript_expression",
1639
+ "unary_expression",
1640
+ ]
1641
+ + literal_types
1642
+ ):
1643
+ add_entry(parser, rda_table, parent_id, used=right_node)
1644
+ else:
1645
+ vars_used = recursively_get_children_of_types(
1646
+ right_node,
1647
+ variable_type + ["field_expression"],
1648
+ index=parser.index,
1649
+ check_list=parser.symbol_table["scope_map"],
1650
+ )
1651
+ for var in vars_used:
1652
+ add_entry(parser, rda_table, parent_id, used=var)
1653
+ literals_used = recursively_get_children_of_types(
1654
+ right_node, literal_types, index=parser.index
1655
+ )
1656
+ for literal in literals_used:
1657
+ add_entry(parser, rda_table, parent_id, used=literal)
1658
+
1659
+ elif root_node.type in increment_statement:
1660
+ parent_statement = return_first_parent_of_types(
1661
+ root_node, statement_types["node_list_type"]
1662
+ )
1663
+ if parent_statement is None:
1664
+ continue
1665
+
1666
+ parent_id = get_index(parent_statement, index)
1667
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
1668
+ if parent_statement and parent_statement.type in inner_types_local:
1669
+ parent_statement = return_first_parent_of_types(
1670
+ parent_statement, statement_types["node_list_type"]
1671
+ )
1672
+ parent_id = get_index(parent_statement, index)
1673
+ elif parent_statement.type in handled_cases:
1674
+ continue
1675
+ else:
1676
+ continue
1677
+
1678
+ if root_node.type in variable_type + ["field_expression"]:
1679
+ add_entry(parser, rda_table, parent_id, used=root_node)
1680
+ add_entry(parser, rda_table, parent_id, defined=root_node)
1681
+ else:
1682
+ identifiers = recursively_get_children_of_types(
1683
+ root_node,
1684
+ variable_type + ["field_expression"],
1685
+ index=parser.index,
1686
+ check_list=parser.symbol_table["scope_map"],
1687
+ )
1688
+ for identifier in identifiers:
1689
+ add_entry(parser, rda_table, parent_id, used=identifier)
1690
+ add_entry(parser, rda_table, parent_id, defined=identifier)
1691
+
1692
+ elif root_node.type in function_calls:
1693
+ parent_statement = return_first_parent_of_types(
1694
+ root_node, statement_types["node_list_type"]
1695
+ )
1696
+ if parent_statement is None:
1697
+ continue
1698
+
1699
+ parent_id = get_index(parent_statement, index)
1700
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
1701
+ if parent_statement and parent_statement.type in inner_types_local:
1702
+ parent_statement = return_first_parent_of_types(
1703
+ parent_statement, statement_types["node_list_type"]
1704
+ )
1705
+ parent_id = get_index(parent_statement, index)
1706
+ elif parent_statement.type in handled_cases:
1707
+ continue
1708
+ else:
1709
+ continue
1710
+
1711
+ function_node = root_node.child_by_field_name("function")
1712
+ function_name = None
1713
+ method_name_for_lookup = None
1714
+
1715
+ if function_node:
1716
+ function_name = st(function_node)
1717
+
1718
+ if function_node.type == "field_expression":
1719
+ argument = function_node.child_by_field_name("argument")
1720
+ if argument:
1721
+ add_entry(parser, rda_table, parent_id, used=argument)
1722
+ field = function_node.child_by_field_name("field")
1723
+ if field:
1724
+ method_name_for_lookup = st(field)
1725
+ elif function_node.type in variable_type:
1726
+ add_entry(
1727
+ parser,
1728
+ rda_table,
1729
+ parent_id,
1730
+ used=function_node,
1731
+ method_call=True,
1732
+ )
1733
+ elif function_node.type == "qualified_identifier":
1734
+ add_entry(
1735
+ parser,
1736
+ rda_table,
1737
+ parent_id,
1738
+ used=function_node,
1739
+ method_call=True,
1740
+ )
1741
+
1742
+ is_input_function = function_name in input_functions or (
1743
+ function_name and any(inp in function_name for inp in ["cin", "scanf"])
1744
+ )
1745
+
1746
+ is_variadic_macro = function_name in ["va_start", "va_arg", "va_end"]
1747
+
1748
+ args_node = root_node.child_by_field_name("arguments")
1749
+ if args_node:
1750
+ arg_list = list(args_node.named_children)
1751
+ for idx, arg in enumerate(arg_list):
1752
+ if is_variadic_macro:
1753
+ if function_name == "va_start" and idx == 0:
1754
+ if arg.type in variable_type:
1755
+ add_entry(
1756
+ parser,
1757
+ rda_table,
1758
+ parent_id,
1759
+ defined=arg,
1760
+ declaration=False,
1761
+ has_initializer=True,
1762
+ )
1763
+ else:
1764
+ identifiers_defined = recursively_get_children_of_types(
1765
+ arg,
1766
+ variable_type,
1767
+ index=parser.index,
1768
+ check_list=parser.symbol_table["scope_map"],
1769
+ )
1770
+ for identifier in identifiers_defined:
1771
+ add_entry(
1772
+ parser,
1773
+ rda_table,
1774
+ parent_id,
1775
+ defined=identifier,
1776
+ declaration=False,
1777
+ has_initializer=True,
1778
+ )
1779
+ continue
1780
+
1781
+ elif function_name == "va_arg" and idx == 0:
1782
+ if arg.type in variable_type:
1783
+ add_entry(parser, rda_table, parent_id, used=arg)
1784
+ add_entry(
1785
+ parser,
1786
+ rda_table,
1787
+ parent_id,
1788
+ defined=arg,
1789
+ declaration=False,
1790
+ )
1791
+ else:
1792
+ identifiers_used = recursively_get_children_of_types(
1793
+ arg,
1794
+ variable_type,
1795
+ index=parser.index,
1796
+ check_list=parser.symbol_table["scope_map"],
1797
+ )
1798
+ for identifier in identifiers_used:
1799
+ add_entry(
1800
+ parser, rda_table, parent_id, used=identifier
1801
+ )
1802
+ add_entry(
1803
+ parser,
1804
+ rda_table,
1805
+ parent_id,
1806
+ defined=identifier,
1807
+ declaration=False,
1808
+ )
1809
+ continue
1810
+
1811
+ if is_input_function:
1812
+ if arg.type == "unary_expression":
1813
+ has_address_of = any(
1814
+ child.type == "&" for child in arg.children
1815
+ )
1816
+ if has_address_of:
1817
+ inner_arg = arg.child_by_field_name("argument")
1818
+ if inner_arg:
1819
+ if inner_arg.type in variable_type:
1820
+ add_entry(
1821
+ parser,
1822
+ rda_table,
1823
+ parent_id,
1824
+ defined=inner_arg,
1825
+ declaration=False,
1826
+ )
1827
+ elif inner_arg.type in [
1828
+ "field_expression",
1829
+ "subscript_expression",
1830
+ ]:
1831
+ add_entry(
1832
+ parser,
1833
+ rda_table,
1834
+ parent_id,
1835
+ defined=inner_arg,
1836
+ declaration=False,
1837
+ )
1838
+ continue
1839
+ if arg.type in variable_type + ["field_expression"]:
1840
+ add_entry(
1841
+ parser,
1842
+ rda_table,
1843
+ parent_id,
1844
+ defined=arg,
1845
+ declaration=False,
1846
+ )
1847
+ continue
1848
+
1849
+ if not is_input_function and not is_variadic_macro:
1850
+ modifies_params = set()
1851
+ lookup_name = (
1852
+ method_name_for_lookup
1853
+ if method_name_for_lookup
1854
+ else function_name
1855
+ )
1856
+ if (
1857
+ lookup_name
1858
+ and function_metadata
1859
+ and lookup_name in function_metadata
1860
+ ):
1861
+ if (
1862
+ pointer_modifications
1863
+ and lookup_name in pointer_modifications
1864
+ ):
1865
+ modifies_params = pointer_modifications[lookup_name]
1866
+
1867
+ is_modified_param = idx in modifies_params
1868
+
1869
+ if is_modified_param:
1870
+ if arg.type == "pointer_expression":
1871
+ inner_arg = arg.child_by_field_name("argument")
1872
+ if not inner_arg:
1873
+ inner_arg = (
1874
+ arg.named_children[0]
1875
+ if arg.named_children
1876
+ else None
1877
+ )
1878
+
1879
+ if inner_arg:
1880
+ if inner_arg.type in variable_type:
1881
+ add_entry(
1882
+ parser, rda_table, parent_id, used=inner_arg
1883
+ )
1884
+ add_entry(
1885
+ parser,
1886
+ rda_table,
1887
+ parent_id,
1888
+ defined=inner_arg,
1889
+ declaration=False,
1890
+ is_pointer_modification_at_call_site=True,
1891
+ )
1892
+ elif inner_arg.type in [
1893
+ "field_expression",
1894
+ "subscript_expression",
1895
+ ]:
1896
+ add_entry(
1897
+ parser, rda_table, parent_id, used=inner_arg
1898
+ )
1899
+ add_entry(
1900
+ parser,
1901
+ rda_table,
1902
+ parent_id,
1903
+ defined=inner_arg,
1904
+ declaration=False,
1905
+ is_pointer_modification_at_call_site=True,
1906
+ )
1907
+ if inner_arg.type == "subscript_expression":
1908
+ index_expr = inner_arg.child_by_field_name(
1909
+ "index"
1910
+ )
1911
+ if index_expr:
1912
+ vars_in_index = (
1913
+ recursively_get_children_of_types(
1914
+ index_expr,
1915
+ variable_type
1916
+ + ["field_expression"],
1917
+ index=parser.index,
1918
+ check_list=parser.symbol_table[
1919
+ "scope_map"
1920
+ ],
1921
+ )
1922
+ )
1923
+ for var in vars_in_index:
1924
+ add_entry(
1925
+ parser,
1926
+ rda_table,
1927
+ parent_id,
1928
+ used=var,
1929
+ )
1930
+ continue
1931
+ elif arg.type == "unary_expression":
1932
+ has_address_of = any(
1933
+ child.type == "&" for child in arg.children
1934
+ )
1935
+ if has_address_of:
1936
+ inner_arg = arg.child_by_field_name("argument")
1937
+ if inner_arg:
1938
+ if inner_arg.type in variable_type:
1939
+ add_entry(
1940
+ parser,
1941
+ rda_table,
1942
+ parent_id,
1943
+ used=inner_arg,
1944
+ )
1945
+ add_entry(
1946
+ parser,
1947
+ rda_table,
1948
+ parent_id,
1949
+ defined=inner_arg,
1950
+ declaration=False,
1951
+ is_pointer_modification_at_call_site=True,
1952
+ )
1953
+ elif inner_arg.type in [
1954
+ "field_expression",
1955
+ "subscript_expression",
1956
+ ]:
1957
+ add_entry(
1958
+ parser,
1959
+ rda_table,
1960
+ parent_id,
1961
+ used=inner_arg,
1962
+ )
1963
+ add_entry(
1964
+ parser,
1965
+ rda_table,
1966
+ parent_id,
1967
+ defined=inner_arg,
1968
+ declaration=False,
1969
+ is_pointer_modification_at_call_site=True,
1970
+ )
1971
+ continue
1972
+
1973
+ elif arg.type in variable_type + ["field_expression"]:
1974
+ add_entry(parser, rda_table, parent_id, used=arg)
1975
+ add_entry(
1976
+ parser,
1977
+ rda_table,
1978
+ parent_id,
1979
+ defined=arg,
1980
+ declaration=False,
1981
+ is_pointer_modification_at_call_site=True,
1982
+ )
1983
+ continue
1984
+
1985
+ if arg.type in variable_type + ["field_expression"]:
1986
+ add_entry(parser, rda_table, parent_id, used=arg)
1987
+ elif arg.type in literal_types:
1988
+ add_entry(parser, rda_table, parent_id, used=arg)
1989
+ else:
1990
+ identifiers_used = recursively_get_children_of_types(
1991
+ arg,
1992
+ variable_type + ["field_expression"],
1993
+ index=parser.index,
1994
+ check_list=parser.symbol_table["scope_map"],
1995
+ )
1996
+ for identifier in identifiers_used:
1997
+ add_entry(parser, rda_table, parent_id, used=identifier)
1998
+ literals_used = recursively_get_children_of_types(
1999
+ arg, literal_types, index=parser.index
2000
+ )
2001
+ for literal in literals_used:
2002
+ add_entry(parser, rda_table, parent_id, used=literal)
2003
+
2004
+ elif root_node.type == "function_definition":
2005
+ parent_id = get_index(root_node, index)
2006
+ if parent_id is None:
2007
+ continue
2008
+
2009
+ declarator = root_node.child_by_field_name("declarator")
2010
+ if declarator:
2011
+ func_declarator = declarator
2012
+ while func_declarator and func_declarator.type in [
2013
+ "pointer_declarator",
2014
+ "reference_declarator",
2015
+ ]:
2016
+ for child in func_declarator.children:
2017
+ if child.type == "function_declarator":
2018
+ func_declarator = child
2019
+ break
2020
+ else:
2021
+ break
2022
+
2023
+ if func_declarator and func_declarator.type == "function_declarator":
2024
+ func_name_node = func_declarator.child_by_field_name("declarator")
2025
+ if func_name_node and func_name_node.type in variable_type:
2026
+ func_name_idx = get_index(func_name_node, index)
2027
+ if (
2028
+ func_name_idx
2029
+ and func_name_idx in parser.symbol_table["scope_map"]
2030
+ ):
2031
+ namespace_name = get_namespace_for_node(root_node, parser)
2032
+
2033
+ if namespace_name:
2034
+ qualified_name = (
2035
+ f"{namespace_name}::{st(func_name_node)}"
2036
+ )
2037
+
2038
+ class PseudoNode:
2039
+ def __init__(self, name, real_node):
2040
+ self.type = "qualified_function"
2041
+ self.text = name.encode("utf-8")
2042
+ self.qualified_name = name
2043
+ self.parent = (
2044
+ real_node.parent if real_node else None
2045
+ )
2046
+ self.real_node = real_node
2047
+
2048
+ pseudo_node = PseudoNode(qualified_name, func_name_node)
2049
+ add_entry(
2050
+ parser,
2051
+ rda_table,
2052
+ parent_id,
2053
+ defined=pseudo_node,
2054
+ declaration=True,
2055
+ )
2056
+ else:
2057
+ add_entry(
2058
+ parser,
2059
+ rda_table,
2060
+ parent_id,
2061
+ defined=func_name_node,
2062
+ declaration=True,
2063
+ )
2064
+
2065
+ param_list = func_declarator.child_by_field_name("parameters")
2066
+ if param_list:
2067
+ for param in param_list.named_children:
2068
+ if param.type in [
2069
+ "parameter_declaration",
2070
+ "optional_parameter_declaration",
2071
+ ]:
2072
+ param_id = extract_param_identifier(param)
2073
+ if param_id:
2074
+ add_entry(
2075
+ parser,
2076
+ rda_table,
2077
+ parent_id,
2078
+ defined=param_id,
2079
+ declaration=True,
2080
+ has_initializer=True,
2081
+ )
2082
+
2083
+ elif root_node.type == "if_statement":
2084
+ parent_id = get_index(root_node, index)
2085
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
2086
+ continue
2087
+
2088
+ condition = root_node.child_by_field_name("condition")
2089
+ if condition:
2090
+ identifiers_used = recursively_get_children_of_types(
2091
+ condition,
2092
+ variable_type + ["field_expression"],
2093
+ index=parser.index,
2094
+ check_list=parser.symbol_table["scope_map"],
2095
+ )
2096
+ for identifier in identifiers_used:
2097
+ add_entry(parser, rda_table, parent_id, used=identifier)
2098
+
2099
+ elif root_node.type == "while_statement":
2100
+ parent_id = get_index(root_node, index)
2101
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
2102
+ continue
2103
+
2104
+ condition = root_node.child_by_field_name("condition")
2105
+ if condition:
2106
+ identifiers_used = recursively_get_children_of_types(
2107
+ condition,
2108
+ variable_type + ["field_expression"],
2109
+ index=parser.index,
2110
+ check_list=parser.symbol_table["scope_map"],
2111
+ )
2112
+ for identifier in identifiers_used:
2113
+ add_entry(parser, rda_table, parent_id, used=identifier)
2114
+
2115
+ elif root_node.type == "for_statement":
2116
+ parent_id = get_index(root_node, index)
2117
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
2118
+ continue
2119
+
2120
+ condition = root_node.child_by_field_name("condition")
2121
+ if condition:
2122
+ identifiers_used = recursively_get_children_of_types(
2123
+ condition,
2124
+ variable_type + ["field_expression"],
2125
+ index=parser.index,
2126
+ check_list=parser.symbol_table["scope_map"],
2127
+ )
2128
+ for identifier in identifiers_used:
2129
+ add_entry(parser, rda_table, parent_id, used=identifier)
2130
+
2131
+ elif root_node.type == "for_range_loop":
2132
+ parent_id = get_index(root_node, index)
2133
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
2134
+ continue
2135
+
2136
+ declarator = root_node.child_by_field_name("declarator")
2137
+ if declarator:
2138
+ var_id = extract_identifier_from_declarator(declarator)
2139
+ if var_id:
2140
+ add_entry(
2141
+ parser, rda_table, parent_id, defined=var_id, declaration=True
2142
+ )
2143
+
2144
+ range_expr = root_node.child_by_field_name("right")
2145
+ if range_expr:
2146
+ if range_expr.type in variable_type + ["field_expression"]:
2147
+ add_entry(parser, rda_table, parent_id, used=range_expr)
2148
+ else:
2149
+ identifiers_used = recursively_get_children_of_types(
2150
+ range_expr,
2151
+ variable_type + ["field_expression"],
2152
+ index=parser.index,
2153
+ check_list=parser.symbol_table["scope_map"],
2154
+ )
2155
+ for identifier in identifiers_used:
2156
+ add_entry(parser, rda_table, parent_id, used=identifier)
2157
+
2158
+ elif root_node.type == "do_statement":
2159
+ pass
2160
+
2161
+ elif (
2162
+ root_node.type == "parenthesized_expression"
2163
+ and root_node.parent is not None
2164
+ and root_node.parent.type == "do_statement"
2165
+ ):
2166
+ parent_id = get_index(root_node, index)
2167
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
2168
+ continue
2169
+
2170
+ identifiers_used = recursively_get_children_of_types(
2171
+ root_node,
2172
+ variable_type + ["field_expression"],
2173
+ index=parser.index,
2174
+ check_list=parser.symbol_table["scope_map"],
2175
+ )
2176
+ for identifier in identifiers_used:
2177
+ add_entry(parser, rda_table, parent_id, used=identifier)
2178
+
2179
+ elif root_node.type == "switch_statement":
2180
+ parent_id = get_index(root_node, index)
2181
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
2182
+ continue
2183
+
2184
+ condition = root_node.child_by_field_name("condition")
2185
+ if condition:
2186
+ identifiers_used = recursively_get_children_of_types(
2187
+ condition,
2188
+ variable_type + ["field_expression"],
2189
+ index=parser.index,
2190
+ check_list=parser.symbol_table["scope_map"],
2191
+ )
2192
+ for identifier in identifiers_used:
2193
+ add_entry(parser, rda_table, parent_id, used=identifier)
2194
+
2195
+ elif root_node.type == "conditional_expression":
2196
+ parent_statement = return_first_parent_of_types(
2197
+ root_node, statement_types["node_list_type"]
2198
+ )
2199
+ if parent_statement is None:
2200
+ continue
2201
+
2202
+ parent_id = get_index(parent_statement, index)
2203
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
2204
+ if parent_statement and parent_statement.type in inner_types_local:
2205
+ parent_statement = return_first_parent_of_types(
2206
+ parent_statement, statement_types["node_list_type"]
2207
+ )
2208
+ parent_id = get_index(parent_statement, index)
2209
+ elif parent_statement.type in handled_cases:
2210
+ continue
2211
+ else:
2212
+ continue
2213
+
2214
+ condition = root_node.child_by_field_name("condition")
2215
+ if condition:
2216
+ if (
2217
+ condition.type
2218
+ in variable_type
2219
+ + [
2220
+ "field_expression",
2221
+ "pointer_expression",
2222
+ "subscript_expression",
2223
+ "unary_expression",
2224
+ ]
2225
+ + literal_types
2226
+ ):
2227
+ add_entry(parser, rda_table, parent_id, used=condition)
2228
+ else:
2229
+ identifiers_used = recursively_get_children_of_types(
2230
+ condition,
2231
+ variable_type + ["field_expression"],
2232
+ index=parser.index,
2233
+ check_list=parser.symbol_table["scope_map"],
2234
+ )
2235
+ for identifier in identifiers_used:
2236
+ add_entry(parser, rda_table, parent_id, used=identifier)
2237
+ literals_used = recursively_get_children_of_types(
2238
+ condition, literal_types, index=parser.index
2239
+ )
2240
+ for literal in literals_used:
2241
+ add_entry(parser, rda_table, parent_id, used=literal)
2242
+
2243
+ consequence = root_node.child_by_field_name("consequence")
2244
+ if consequence:
2245
+ if consequence.type in variable_type + ["field_expression"]:
2246
+ add_entry(parser, rda_table, parent_id, used=consequence)
2247
+ elif consequence.type in literal_types:
2248
+ add_entry(parser, rda_table, parent_id, used=consequence)
2249
+ else:
2250
+ identifiers_used = recursively_get_children_of_types(
2251
+ consequence,
2252
+ variable_type + ["field_expression"],
2253
+ index=parser.index,
2254
+ check_list=parser.symbol_table["scope_map"],
2255
+ )
2256
+ for identifier in identifiers_used:
2257
+ add_entry(parser, rda_table, parent_id, used=identifier)
2258
+ literals_used = recursively_get_children_of_types(
2259
+ consequence, literal_types, index=parser.index
2260
+ )
2261
+ for literal in literals_used:
2262
+ add_entry(parser, rda_table, parent_id, used=literal)
2263
+
2264
+ alternative = root_node.child_by_field_name("alternative")
2265
+ if alternative:
2266
+ if alternative.type in variable_type + ["field_expression"]:
2267
+ add_entry(parser, rda_table, parent_id, used=alternative)
2268
+ elif alternative.type in literal_types:
2269
+ add_entry(parser, rda_table, parent_id, used=alternative)
2270
+ else:
2271
+ identifiers_used = recursively_get_children_of_types(
2272
+ alternative,
2273
+ variable_type + ["field_expression"],
2274
+ index=parser.index,
2275
+ check_list=parser.symbol_table["scope_map"],
2276
+ )
2277
+ for identifier in identifiers_used:
2278
+ add_entry(parser, rda_table, parent_id, used=identifier)
2279
+ literals_used = recursively_get_children_of_types(
2280
+ alternative, literal_types, index=parser.index
2281
+ )
2282
+ for literal in literals_used:
2283
+ add_entry(parser, rda_table, parent_id, used=literal)
2284
+
2285
+ elif root_node.type == "lambda_expression":
2286
+ parent_id = get_index(root_node, index)
2287
+ if parent_id is None:
2288
+ continue
2289
+
2290
+ for child in root_node.children:
2291
+ if child.type == "lambda_capture_specifier":
2292
+ for capture in child.named_children:
2293
+ if capture.type in variable_type:
2294
+ add_entry(parser, rda_table, parent_id, used=capture)
2295
+
2296
+ elif root_node.type == "catch_clause":
2297
+ parent_id = get_index(root_node, index)
2298
+ if parent_id is None:
2299
+ continue
2300
+
2301
+ for child in root_node.children:
2302
+ if child.type == "parameter_list":
2303
+ for param in child.named_children:
2304
+ if param.type == "parameter_declaration":
2305
+ param_id = extract_param_identifier(param)
2306
+ if param_id:
2307
+ add_entry(
2308
+ parser,
2309
+ rda_table,
2310
+ parent_id,
2311
+ defined=param_id,
2312
+ declaration=True,
2313
+ )
2314
+
2315
+ elif root_node.type == "throw_statement":
2316
+ parent_id = get_index(root_node, index)
2317
+ if parent_id is None:
2318
+ continue
2319
+
2320
+ identifiers_used = recursively_get_children_of_types(
2321
+ root_node,
2322
+ variable_type + ["field_expression"],
2323
+ index=parser.index,
2324
+ check_list=parser.symbol_table["scope_map"],
2325
+ )
2326
+ for identifier in identifiers_used:
2327
+ add_entry(parser, rda_table, parent_id, used=identifier)
2328
+
2329
+ else:
2330
+ if root_node.type not in variable_type:
2331
+ continue
2332
+
2333
+ in_do_while_condition = False
2334
+ temp_parent = root_node.parent
2335
+ while temp_parent is not None:
2336
+ if (
2337
+ temp_parent.type == "parenthesized_expression"
2338
+ and temp_parent.parent is not None
2339
+ and temp_parent.parent.type == "do_statement"
2340
+ ):
2341
+ in_do_while_condition = True
2342
+ break
2343
+ temp_parent = temp_parent.parent
2344
+
2345
+ if in_do_while_condition:
2346
+ continue
2347
+
2348
+ handled_types_local = (
2349
+ def_statement
2350
+ + assignment
2351
+ + increment_statement
2352
+ + function_calls
2353
+ + declaration_statement
2354
+ + [
2355
+ "return_statement",
2356
+ "catch_clause",
2357
+ "throw_statement",
2358
+ "conditional_expression",
2359
+ ]
2360
+ )
2361
+ parent_statement = return_first_parent_of_types(
2362
+ root_node,
2363
+ statement_types["non_control_statement"]
2364
+ + statement_types["control_statement"],
2365
+ stop_types=statement_types.get("statement_holders", [])
2366
+ + handled_types_local,
2367
+ )
2368
+
2369
+ if parent_statement is None:
2370
+ continue
2371
+
2372
+ parent_id = get_index(parent_statement, index)
2373
+ if parent_id is None or parent_id not in CFG_results.graph.nodes:
2374
+ continue
2375
+
2376
+ if parent_statement.type in declaration_statement:
2377
+ continue
2378
+
2379
+ immediate_parent = root_node.parent
2380
+ if immediate_parent and immediate_parent.type == "pointer_expression":
2381
+ continue
2382
+
2383
+ add_entry(parser, rda_table, parent_id, used=root_node)
2384
+
2385
+ return rda_table
2386
+
2387
+
2388
+ def start_rda(index, rda_table, cfg_graph, pre_solve=False):
2389
+ graph = copy.deepcopy(cfg_graph)
2390
+ if pre_solve:
2391
+ remove_edges = []
2392
+ for edge in graph.edges:
2393
+ edge_data = graph.edges[edge]
2394
+ if "label" in edge_data and edge_data["label"] in [
2395
+ "method_call",
2396
+ "method_return",
2397
+ "class_return",
2398
+ "constructor_call",
2399
+ ]:
2400
+ remove_edges.append(edge)
2401
+ graph.remove_edges_from(remove_edges)
2402
+
2403
+ cfg = graph
2404
+ nodes = graph.nodes
2405
+
2406
+ old_result = {}
2407
+ for node in nodes:
2408
+ old_result[node] = {"IN": set(), "OUT": set()}
2409
+
2410
+ new_result = copy.deepcopy(old_result)
2411
+ iteration = 0
2412
+
2413
+ while True:
2414
+ iteration += 1
2415
+
2416
+ for node in nodes:
2417
+ predecessors = [s for (s, t) in cfg.in_edges(node)]
2418
+
2419
+ in_set = set()
2420
+ for pred in predecessors:
2421
+ in_set = in_set.union(old_result[pred]["OUT"])
2422
+
2423
+ new_result[node]["IN"] = in_set
2424
+
2425
+ def_info = rda_table[node]["def"] if node in rda_table else set()
2426
+ names_defined = [d.name for d in def_info]
2427
+
2428
+ surviving_defs = set()
2429
+ for incoming_def in in_set:
2430
+ if incoming_def.name not in names_defined:
2431
+ surviving_defs.add(incoming_def)
2432
+
2433
+ new_result[node]["OUT"] = surviving_defs.union(def_info)
2434
+
2435
+ ddiff = DeepDiff(old_result, new_result, ignore_order=True)
2436
+ if ddiff == {}:
2437
+ if debug:
2438
+ logger.info("RDA: Converged in {} iterations", iteration)
2439
+ break
2440
+
2441
+ old_result = copy.deepcopy(new_result)
2442
+
2443
+ return new_result
2444
+
2445
+
2446
+ def add_edge(final_graph, source, target, attrib=None):
2447
+ if attrib is not None:
2448
+ used_def = attrib.get("used_def", None)
2449
+ edge_type = attrib.get("edge_type", None)
2450
+ dataflow_type = attrib.get("dataflow_type", None)
2451
+
2452
+ for u, v, k, data in final_graph.edges(keys=True, data=True):
2453
+ if (
2454
+ u == source
2455
+ and v == target
2456
+ and data.get("used_def") == used_def
2457
+ and data.get("edge_type") == edge_type
2458
+ and data.get("dataflow_type") == dataflow_type
2459
+ ):
2460
+ return
2461
+
2462
+ final_graph.add_edge(source, target)
2463
+ if attrib is not None:
2464
+ edge_keys = [
2465
+ k for u, v, k in final_graph.edges(keys=True) if u == source and v == target
2466
+ ]
2467
+ if edge_keys:
2468
+ edge_key = max(edge_keys)
2469
+ else:
2470
+ edge_key = 0
2471
+ nx.set_edge_attributes(final_graph, {(source, target, edge_key): attrib})
2472
+
2473
+
2474
+ def name_match_with_fields(use_name, def_name):
2475
+ if use_name == def_name:
2476
+ return True
2477
+
2478
+ if "." in use_name:
2479
+ base_obj = use_name.split(".")[0]
2480
+ if def_name == base_obj:
2481
+ return True
2482
+
2483
+ return False
2484
+
2485
+
2486
+ def get_required_edges_from_def_to_use(
2487
+ index,
2488
+ cfg,
2489
+ rda_solution,
2490
+ rda_table,
2491
+ graph_nodes,
2492
+ processed_edges,
2493
+ properties,
2494
+ lambda_map=None,
2495
+ node_list=None,
2496
+ parser=None,
2497
+ ):
2498
+ if lambda_map is None:
2499
+ lambda_map = {}
2500
+ if node_list is None:
2501
+ node_list = {}
2502
+ final_graph = copy.deepcopy(cfg)
2503
+ final_graph.remove_edges_from(list(final_graph.edges()))
2504
+
2505
+ for node in graph_nodes:
2506
+ if node not in rda_table:
2507
+ continue
2508
+
2509
+ use_info = rda_table[node]["use"]
2510
+
2511
+ for used in use_info:
2512
+ if isinstance(used, Literal):
2513
+ used.satisfied = True
2514
+ continue
2515
+
2516
+ matching_defs = []
2517
+ matching_field_defs = []
2518
+
2519
+ for available_def in rda_solution[node]["IN"]:
2520
+ if parser.src_language != "cpp":
2521
+ if hasattr(available_def, "declaration") and hasattr(
2522
+ available_def, "has_initializer"
2523
+ ):
2524
+ if (
2525
+ available_def.declaration
2526
+ and not available_def.has_initializer
2527
+ ):
2528
+ continue
2529
+
2530
+ if available_def.name == used.name:
2531
+ if scope_check(available_def.scope, used.variable_scope):
2532
+ matching_defs.append(available_def)
2533
+ elif "." in used.name or "." in available_def.name:
2534
+ if name_match_with_fields(used.name, available_def.name):
2535
+ if scope_check(available_def.scope, used.variable_scope):
2536
+ matching_field_defs.append(available_def)
2537
+
2538
+ def_info = rda_table[node]["def"] if node in rda_table else []
2539
+ defines_same_var = any(d.name == used.name for d in def_info)
2540
+
2541
+ if defines_same_var:
2542
+ if matching_defs:
2543
+ node_key = (
2544
+ read_index(index, node) if node in index.values() else None
2545
+ )
2546
+ ast_node = (
2547
+ node_list.get(node_key) if node_list and node_key else None
2548
+ )
2549
+
2550
+ has_loop_carried_def = any(d.line == node for d in matching_defs)
2551
+
2552
+ if (
2553
+ has_loop_carried_def
2554
+ and ast_node
2555
+ and is_node_inside_loop(ast_node)
2556
+ ):
2557
+ add_edge(
2558
+ final_graph,
2559
+ node,
2560
+ node,
2561
+ {
2562
+ "dataflow_type": "loop_carried",
2563
+ "edge_type": "DFG_edge",
2564
+ "color": "#FFA500",
2565
+ "used_def": used.name,
2566
+ },
2567
+ )
2568
+
2569
+ for available_def in matching_defs:
2570
+ if getattr(
2571
+ available_def, "is_pointer_modification_at_call_site", False
2572
+ ):
2573
+ continue
2574
+ if available_def.line != node:
2575
+ add_edge(
2576
+ final_graph,
2577
+ available_def.line,
2578
+ node,
2579
+ {
2580
+ "dataflow_type": "comesFrom",
2581
+ "edge_type": "DFG_edge",
2582
+ "color": "#00A3FF",
2583
+ "used_def": used.name,
2584
+ },
2585
+ )
2586
+ used.satisfied = True
2587
+ elif matching_defs:
2588
+ for available_def in matching_defs:
2589
+ if getattr(
2590
+ available_def, "is_pointer_modification_at_call_site", False
2591
+ ):
2592
+ continue
2593
+ if available_def.line != node:
2594
+ add_edge(
2595
+ final_graph,
2596
+ available_def.line,
2597
+ node,
2598
+ {
2599
+ "dataflow_type": "comesFrom",
2600
+ "edge_type": "DFG_edge",
2601
+ "color": "#00A3FF",
2602
+ "used_def": used.name,
2603
+ },
2604
+ )
2605
+ used.satisfied = True
2606
+ if matching_defs:
2607
+ used.satisfied = True
2608
+ elif matching_field_defs:
2609
+ for available_def in matching_field_defs:
2610
+ if name_match_with_fields(used.name, available_def.name):
2611
+ if scope_check(available_def.scope, used.variable_scope):
2612
+ if available_def.line != node:
2613
+ add_edge(
2614
+ final_graph,
2615
+ available_def.line,
2616
+ node,
2617
+ {
2618
+ "dataflow_type": "comesFrom",
2619
+ "edge_type": "DFG_edge",
2620
+ "color": "#00A3FF",
2621
+ "used_def": used.name,
2622
+ },
2623
+ )
2624
+ used.satisfied = True
2625
+
2626
+ if not used.satisfied:
2627
+ for def_node in graph_nodes:
2628
+ if def_node not in rda_table:
2629
+ continue
2630
+ for definition in rda_table[def_node]["def"]:
2631
+ names_match = False
2632
+
2633
+ if definition.name == used.name:
2634
+ names_match = True
2635
+ else:
2636
+ def_base = (
2637
+ definition.name.split("::")[-1]
2638
+ if "::" in definition.name
2639
+ else definition.name
2640
+ )
2641
+ used_base = (
2642
+ used.name.split("::")[-1]
2643
+ if "::" in used.name
2644
+ else used.name
2645
+ )
2646
+
2647
+ if def_base == used_base:
2648
+ if "::" in used.name:
2649
+ names_match = definition.name == used.name
2650
+ else:
2651
+ names_match = False
2652
+
2653
+ if names_match:
2654
+ node_type = (
2655
+ read_index(index, def_node)[-1]
2656
+ if def_node in index.values()
2657
+ else None
2658
+ )
2659
+ if node_type == "function_definition":
2660
+ func_scope = definition.scope
2661
+ func_line = definition.line
2662
+ namespace_scope = (
2663
+ func_scope[:-1]
2664
+ if len(func_scope) > 1
2665
+ else func_scope
2666
+ )
2667
+
2668
+ return_nodes = []
2669
+
2670
+ for rnode in graph_nodes:
2671
+ rnode_type = (
2672
+ read_index(index, rnode)[-1]
2673
+ if rnode in index.values()
2674
+ else None
2675
+ )
2676
+ if rnode_type == "return_statement":
2677
+ has_return_value = False
2678
+ return_scope = None
2679
+ return_line = None
2680
+
2681
+ if rnode in rda_table and rda_table[rnode].get(
2682
+ "use"
2683
+ ):
2684
+ for ret_use in rda_table[rnode]["use"]:
2685
+ return_scope = ret_use.scope
2686
+ return_line = ret_use.line
2687
+ has_return_value = True
2688
+ break
2689
+
2690
+ if (
2691
+ has_return_value
2692
+ and return_scope
2693
+ and return_line
2694
+ ):
2695
+ namespace_matches = len(
2696
+ return_scope
2697
+ ) == len(namespace_scope) and all(
2698
+ return_scope[i] == namespace_scope[i]
2699
+ for i in range(len(namespace_scope))
2700
+ )
2701
+
2702
+ if (
2703
+ namespace_matches
2704
+ and return_line > func_line
2705
+ ):
2706
+ is_in_this_function = True
2707
+
2708
+ next_func_line = float("inf")
2709
+ for other_node in graph_nodes:
2710
+ other_type = (
2711
+ read_index(index, other_node)[
2712
+ -1
2713
+ ]
2714
+ if other_node in index.values()
2715
+ else None
2716
+ )
2717
+ if (
2718
+ other_type
2719
+ == "function_definition"
2720
+ and other_node != def_node
2721
+ ):
2722
+ if other_node in rda_table:
2723
+ for other_def in rda_table[
2724
+ other_node
2725
+ ].get("def", []):
2726
+ other_scope = (
2727
+ other_def.scope
2728
+ )
2729
+ other_line = (
2730
+ other_def.line
2731
+ )
2732
+ other_namespace = (
2733
+ other_scope[:-1]
2734
+ if len(other_scope)
2735
+ > 1
2736
+ else other_scope
2737
+ )
2738
+ if (
2739
+ other_namespace
2740
+ == namespace_scope
2741
+ and other_line
2742
+ > func_line
2743
+ and other_line
2744
+ < next_func_line
2745
+ ):
2746
+ next_func_line = (
2747
+ other_line
2748
+ )
2749
+
2750
+ if return_line < next_func_line:
2751
+ return_nodes.append(rnode)
2752
+
2753
+ if return_nodes:
2754
+ for ret_node in return_nodes:
2755
+ if ret_node != node:
2756
+ add_edge(
2757
+ final_graph,
2758
+ ret_node,
2759
+ node,
2760
+ {
2761
+ "dataflow_type": "comesFrom",
2762
+ "edge_type": "DFG_edge",
2763
+ "color": "#00A3FF",
2764
+ "used_def": used.name,
2765
+ },
2766
+ )
2767
+ used.satisfied = True
2768
+ break
2769
+
2770
+ if not used.satisfied:
2771
+ for def_node in graph_nodes:
2772
+ if def_node not in rda_table:
2773
+ continue
2774
+ for definition in rda_table[def_node]["def"]:
2775
+ if definition.name == used.name:
2776
+ if definition.scope == [0] and scope_check(
2777
+ definition.scope, used.scope
2778
+ ):
2779
+ if definition.line != node:
2780
+ add_edge(
2781
+ final_graph,
2782
+ definition.line,
2783
+ node,
2784
+ {
2785
+ "dataflow_type": "comesFrom",
2786
+ "edge_type": "DFG_edge",
2787
+ "color": "#00A3FF",
2788
+ "used_def": used.name,
2789
+ },
2790
+ )
2791
+ used.satisfied = True
2792
+ break
2793
+ if used.satisfied:
2794
+ break
2795
+
2796
+ if not used.satisfied and "::" in used.name:
2797
+ qualified_parts = used.name.split("::")
2798
+ var_name = qualified_parts[-1]
2799
+
2800
+ for def_node in graph_nodes:
2801
+ if def_node not in rda_table:
2802
+ continue
2803
+ for definition in rda_table[def_node]["def"]:
2804
+ if definition.name == var_name:
2805
+ if len(definition.scope) >= 2:
2806
+ if definition.line != node:
2807
+ add_edge(
2808
+ final_graph,
2809
+ definition.line,
2810
+ node,
2811
+ {
2812
+ "dataflow_type": "comesFrom",
2813
+ "edge_type": "DFG_edge",
2814
+ "color": "#00A3FF",
2815
+ "used_def": used.name,
2816
+ },
2817
+ )
2818
+ used.satisfied = True
2819
+ break
2820
+ if used.satisfied:
2821
+ break
2822
+
2823
+ if not used.satisfied:
2824
+ for def_node in graph_nodes:
2825
+ if def_node not in rda_table:
2826
+ continue
2827
+ for definition in rda_table[def_node]["def"]:
2828
+ if definition.name == used.name:
2829
+ if len(definition.scope) >= 2 and len(used.scope) >= 2:
2830
+ if (
2831
+ definition.scope[0] == used.scope[0]
2832
+ and definition.scope[1] == used.scope[1]
2833
+ ):
2834
+ if definition.line != node:
2835
+ add_edge(
2836
+ final_graph,
2837
+ definition.line,
2838
+ node,
2839
+ {
2840
+ "dataflow_type": "comesFrom",
2841
+ "edge_type": "DFG_edge",
2842
+ "color": "#00A3FF",
2843
+ "used_def": used.name,
2844
+ },
2845
+ )
2846
+ used.satisfied = True
2847
+ break
2848
+ if used.satisfied:
2849
+ break
2850
+
2851
+ if properties.get("last_def", False):
2852
+ killed_defs = rda_solution[node]["IN"] - rda_solution[node]["OUT"]
2853
+ for killed_def in killed_defs:
2854
+ node_type = read_index(index, node)[-1]
2855
+ def_node_type = read_index(index, killed_def.line)[-1]
2856
+ ignore_types = [
2857
+ "for_statement",
2858
+ "for_range_loop",
2859
+ "while_statement",
2860
+ "if_statement",
2861
+ "switch_statement",
2862
+ ]
2863
+ if node_type not in ignore_types and def_node_type not in ignore_types:
2864
+ add_edge(
2865
+ final_graph,
2866
+ killed_def.line,
2867
+ node,
2868
+ {"color": "orange", "dataflow_type": "lastDef"},
2869
+ )
2870
+
2871
+ for edge in processed_edges:
2872
+ edge_data = cfg.get_edge_data(*edge)
2873
+ if edge_data and len(edge_data) > 0:
2874
+ edge_data = edge_data[0]
2875
+ label = edge_data.get("label", "")
2876
+
2877
+ if label == "constructor_call":
2878
+ source_node = node_list.get(read_index(index, edge[0]))
2879
+ obj_name = "this"
2880
+ if source_node and source_node.type == "declaration":
2881
+ for child in source_node.named_children:
2882
+ if child.type in [
2883
+ "init_declarator",
2884
+ "identifier",
2885
+ "type_identifier",
2886
+ ]:
2887
+ if child.type == "init_declarator":
2888
+ decl = child.child_by_field_name("declarator")
2889
+ if decl and decl.type == "identifier":
2890
+ obj_name = st(decl)
2891
+ elif child.type == "identifier":
2892
+ obj_name = st(child)
2893
+
2894
+ add_edge(
2895
+ final_graph,
2896
+ edge[0],
2897
+ edge[1],
2898
+ {
2899
+ "dataflow_type": "constructor_call",
2900
+ "edge_type": "DFG_edge",
2901
+ "color": "#FF6B6B",
2902
+ "object_name": obj_name,
2903
+ },
2904
+ )
2905
+
2906
+ elif label == "base_constructor_call":
2907
+ add_edge(
2908
+ final_graph,
2909
+ edge[0],
2910
+ edge[1],
2911
+ {
2912
+ "dataflow_type": "base_constructor_call",
2913
+ "edge_type": "DFG_edge",
2914
+ "color": "#FF6B6B",
2915
+ "object_name": "this",
2916
+ },
2917
+ )
2918
+
2919
+ elif label == "scope_exit_destructor":
2920
+ add_edge(
2921
+ final_graph,
2922
+ edge[0],
2923
+ edge[1],
2924
+ {
2925
+ "dataflow_type": "destructor_call",
2926
+ "edge_type": "DFG_edge",
2927
+ "color": "#C44569",
2928
+ "object_name": "this",
2929
+ },
2930
+ )
2931
+
2932
+ elif label == "base_destructor_call":
2933
+ add_edge(
2934
+ final_graph,
2935
+ edge[0],
2936
+ edge[1],
2937
+ {
2938
+ "dataflow_type": "base_destructor_call",
2939
+ "edge_type": "DFG_edge",
2940
+ "color": "#C44569",
2941
+ "object_name": "this",
2942
+ },
2943
+ )
2944
+
2945
+ elif label == "virtual_call":
2946
+ source_node = node_list.get(read_index(index, edge[0]))
2947
+ obj_name = "this"
2948
+
2949
+ if source_node and source_node.type == "expression_statement":
2950
+ call_expr = (
2951
+ source_node.named_children[0]
2952
+ if source_node.named_children
2953
+ else None
2954
+ )
2955
+ if call_expr and call_expr.type == "call_expression":
2956
+ func_node = call_expr.child_by_field_name("function")
2957
+ if func_node and func_node.type == "field_expression":
2958
+ arg_node = func_node.child_by_field_name("argument")
2959
+ if arg_node:
2960
+ obj_name = st(arg_node)
2961
+
2962
+ add_edge(
2963
+ final_graph,
2964
+ edge[0],
2965
+ edge[1],
2966
+ {
2967
+ "dataflow_type": "virtual_dispatch",
2968
+ "edge_type": "DFG_edge",
2969
+ "color": "#4834DF",
2970
+ "object_name": obj_name,
2971
+ },
2972
+ )
2973
+
2974
+ elif label == "method_call":
2975
+ pass
2976
+
2977
+ else:
2978
+ if label in ["method_return", "function_return"]:
2979
+ add_edge(
2980
+ final_graph,
2981
+ edge[0],
2982
+ edge[1],
2983
+ {"dataflow_type": "parameter", "edge_type": "DFG_edge"},
2984
+ )
2985
+
2986
+ if lambda_map:
2987
+ param_to_lambda = {}
2988
+
2989
+ for call_node, func_def_node in processed_edges:
2990
+ if call_node not in rda_table:
2991
+ continue
2992
+
2993
+ uses = rda_table[call_node].get("use", [])
2994
+
2995
+ if func_def_node not in rda_table:
2996
+ continue
2997
+ params = rda_table[func_def_node].get("def", [])
2998
+
2999
+ node_type = (
3000
+ read_index(index, func_def_node)[-1]
3001
+ if func_def_node in index.values()
3002
+ else None
3003
+ )
3004
+ actual_params = (
3005
+ params[1:] if node_type == "function_definition" and params else params
3006
+ )
3007
+
3008
+ for used_var in uses:
3009
+ if not isinstance(used_var, Identifier):
3010
+ continue
3011
+ if used_var.method_call:
3012
+ continue
3013
+
3014
+ if used_var.name in lambda_map:
3015
+ for param in actual_params:
3016
+ if isinstance(param, Identifier) and not param.method_call:
3017
+ param_to_lambda[(param.name, func_def_node)] = used_var.name
3018
+ if debug:
3019
+ logger.info(
3020
+ f"Mapped parameter {param.name} in func {func_def_node} "
3021
+ f"to lambda {used_var.name}"
3022
+ )
3023
+
3024
+ for node in graph_nodes:
3025
+ if node not in rda_table:
3026
+ continue
3027
+
3028
+ uses = rda_table[node].get("use", [])
3029
+
3030
+ for used_var in uses:
3031
+ if not isinstance(used_var, Identifier):
3032
+ continue
3033
+ if not used_var.method_call:
3034
+ continue
3035
+
3036
+ node_type = (
3037
+ read_index(index, node)[-1] if node in index.values() else None
3038
+ )
3039
+
3040
+ reaching_defs = rda_solution[node]["IN"]
3041
+ for def_var in reaching_defs:
3042
+ if not isinstance(def_var, Identifier):
3043
+ continue
3044
+ if def_var.name != used_var.name:
3045
+ continue
3046
+
3047
+ key = (def_var.name, def_var.line)
3048
+ if key in param_to_lambda:
3049
+ lambda_var = param_to_lambda[key]
3050
+ lambda_info = lambda_map[lambda_var]
3051
+
3052
+ for body_node in lambda_info["body_nodes"]:
3053
+ add_edge(
3054
+ final_graph,
3055
+ node,
3056
+ body_node,
3057
+ {
3058
+ "dataflow_type": "lambda_call",
3059
+ "edge_type": "DFG_edge",
3060
+ "color": "#FF6B6B",
3061
+ "lambda_var": lambda_var,
3062
+ },
3063
+ )
3064
+ if debug:
3065
+ logger.info(
3066
+ f"Added lambda call edge: {node} -> {body_node} "
3067
+ f"(calling lambda {lambda_var})"
3068
+ )
3069
+
3070
+ return final_graph
3071
+
3072
+
3073
+ def rda_cfg_map(rda_solution, CFG_results):
3074
+ """Create debug graph showing RDA info on CFG edges"""
3075
+ graph = copy.deepcopy(CFG_results.graph)
3076
+
3077
+ for edge in list(graph.edges):
3078
+ out_set = rda_solution[edge[0]]["OUT"]
3079
+ in_set = rda_solution[edge[1]]["IN"]
3080
+
3081
+ intersection = [d for d in out_set if d in in_set]
3082
+
3083
+ if intersection:
3084
+ edge_data = graph.get_edge_data(*edge)
3085
+ edge_data["rda_info"] = ",".join([str(d) for d in intersection])
3086
+ else:
3087
+ graph.remove_edge(*edge)
3088
+
3089
+ return graph
3090
+
3091
+
3092
+ def collect_call_site_information(parser, function_metadata, cfg_graph):
3093
+ """
3094
+ Collect information about function call sites for interprocedural analysis.
3095
+
3096
+ For each call site, track:
3097
+ - Which function is called
3098
+ - Which arguments are passed by reference (&var)
3099
+ - The mapping of actual arguments to formal parameters
3100
+
3101
+ Returns:
3102
+ List of dicts with call site information:
3103
+ {
3104
+ "call_site_node": AST node,
3105
+ "call_site_id": CFG node ID,
3106
+ "function_name": str,
3107
+ "pass_by_ref_args": [(arg_idx, var_name, var_node), ...]
3108
+ }
3109
+ """
3110
+ call_sites = []
3111
+ index = parser.index
3112
+
3113
+ for node in traverse_tree(parser.tree.root_node):
3114
+ if node.type == "call_expression":
3115
+ function_name = None
3116
+ func_node = node.child_by_field_name("function")
3117
+ if func_node:
3118
+ if func_node.type == "identifier":
3119
+ function_name = st(func_node)
3120
+ elif func_node.type == "qualified_identifier":
3121
+ function_name = st(func_node)
3122
+ elif func_node.type == "field_expression":
3123
+ field_node = func_node.child_by_field_name("field")
3124
+ if field_node and field_node.type == "field_identifier":
3125
+ function_name = st(field_node)
3126
+
3127
+ if not function_name or function_name not in function_metadata:
3128
+ continue
3129
+
3130
+ parent_statement = return_first_parent_of_types(
3131
+ node, statement_types["node_list_type"]
3132
+ )
3133
+ if not parent_statement:
3134
+ continue
3135
+
3136
+ call_site_id = get_index(parent_statement, index)
3137
+ if call_site_id is None or call_site_id not in cfg_graph.nodes:
3138
+ continue
3139
+
3140
+ pass_by_ref_args = []
3141
+ args_node = node.child_by_field_name("arguments")
3142
+ if args_node:
3143
+ func_params = function_metadata[function_name]["params"]
3144
+
3145
+ for arg_idx, arg in enumerate(args_node.named_children):
3146
+ if arg_idx < len(func_params):
3147
+ param_name, is_pointer, is_reference, param_idx = func_params[
3148
+ arg_idx
3149
+ ]
3150
+
3151
+ if is_pointer or is_reference:
3152
+ if arg.type == "pointer_expression":
3153
+ has_ampersand = False
3154
+ arg_node = None
3155
+ for arg_child in arg.children:
3156
+ if arg_child.type == "&":
3157
+ has_ampersand = True
3158
+ elif arg_child.is_named:
3159
+ arg_node = arg_child
3160
+
3161
+ if has_ampersand and arg_node:
3162
+ if arg_node.type in ["identifier", "this"]:
3163
+ var_name = st(arg_node)
3164
+ pass_by_ref_args.append(
3165
+ (arg_idx, var_name, arg_node)
3166
+ )
3167
+ elif is_reference and arg.type in ["identifier", "this"]:
3168
+ var_name = st(arg)
3169
+ pass_by_ref_args.append((arg_idx, var_name, arg))
3170
+ elif is_pointer and arg.type in ["identifier", "this"]:
3171
+ var_name = st(arg)
3172
+ arg_index = get_index(arg, index)
3173
+ if (
3174
+ arg_index
3175
+ and arg_index in parser.symbol_table["scope_map"]
3176
+ ):
3177
+ pass_by_ref_args.append((arg_idx, var_name, arg))
3178
+
3179
+ if pass_by_ref_args:
3180
+ call_sites.append(
3181
+ {
3182
+ "call_site_node": node,
3183
+ "call_site_id": call_site_id,
3184
+ "function_name": function_name,
3185
+ "pass_by_ref_args": pass_by_ref_args,
3186
+ }
3187
+ )
3188
+
3189
+ return call_sites
3190
+
3191
+
3192
+ def find_modification_sites(parser, function_metadata_by_id, pointer_modifications):
3193
+ """
3194
+ Find all modification sites for pointer/reference parameters inside functions.
3195
+
3196
+ For each function, find all statements where a pointer/reference parameter is modified.
3197
+
3198
+ Args:
3199
+ parser: C++ parser
3200
+ function_metadata_by_id: Dict mapping func_def_id -> metadata (from collect_function_metadata)
3201
+ pointer_modifications: Dict mapping func_name -> set of modified param indices
3202
+
3203
+ Returns:
3204
+ Tuple of:
3205
+ - modification_sites_by_name: Dict mapping function_name -> list of (param_idx, modification_node, statement_id)
3206
+ - modification_sites_by_id: Dict mapping func_def_id -> list of (param_idx, modification_node, statement_id)
3207
+ """
3208
+ modification_sites_by_name = {}
3209
+ modification_sites_by_id = {}
3210
+ index = parser.index
3211
+
3212
+ for func_def_id, meta in function_metadata_by_id.items():
3213
+ modifications = []
3214
+ func_node = meta["node"]
3215
+ func_name = meta.get("func_name", "")
3216
+ modified_params = pointer_modifications.get(func_name, set())
3217
+
3218
+ param_name_to_idx = {}
3219
+ for param_name, is_pointer, is_reference, param_idx in meta["params"]:
3220
+ if (is_pointer or is_reference) and param_idx in modified_params:
3221
+ param_name_to_idx[param_name] = param_idx
3222
+
3223
+ if not param_name_to_idx:
3224
+ if func_name:
3225
+ modification_sites_by_name[func_name] = modifications
3226
+ modification_sites_by_id[func_def_id] = modifications
3227
+ continue
3228
+
3229
+ for node in traverse_tree(func_node):
3230
+ modification_param_idx = None
3231
+ mod_node = None
3232
+
3233
+ if node.type == "assignment_expression":
3234
+ left = node.child_by_field_name("left")
3235
+ if left:
3236
+ if left.type == "pointer_expression":
3237
+ arg = left.child_by_field_name("argument")
3238
+ if arg and arg.type in ["identifier", "this"]:
3239
+ var_name = st(arg)
3240
+ if var_name in param_name_to_idx:
3241
+ modification_param_idx = param_name_to_idx[var_name]
3242
+ mod_node = node
3243
+
3244
+ elif left.type == "subscript_expression":
3245
+ array_arg = left.child_by_field_name("argument")
3246
+ if array_arg and array_arg.type in ["identifier", "this"]:
3247
+ var_name = st(array_arg)
3248
+ if var_name in param_name_to_idx:
3249
+ modification_param_idx = param_name_to_idx[var_name]
3250
+ mod_node = node
3251
+
3252
+ elif left.type in ["identifier", "this"]:
3253
+ var_name = st(left)
3254
+ if var_name in param_name_to_idx:
3255
+ modification_param_idx = param_name_to_idx[var_name]
3256
+ mod_node = node
3257
+
3258
+ elif node.type == "update_expression":
3259
+ arg = node.child_by_field_name("argument")
3260
+ if arg:
3261
+ if arg.type == "parenthesized_expression":
3262
+ inner_children = [c for c in arg.named_children if c.is_named]
3263
+ if inner_children:
3264
+ arg = inner_children[0]
3265
+
3266
+ if arg.type == "pointer_expression":
3267
+ inner_arg = arg.child_by_field_name("argument")
3268
+ if inner_arg and inner_arg.type in ["identifier", "this"]:
3269
+ var_name = st(inner_arg)
3270
+ if var_name in param_name_to_idx:
3271
+ modification_param_idx = param_name_to_idx[var_name]
3272
+ mod_node = node
3273
+ elif arg.type == "subscript_expression":
3274
+ array_arg = arg.child_by_field_name("argument")
3275
+ if array_arg and array_arg.type in ["identifier", "this"]:
3276
+ var_name = st(array_arg)
3277
+ if var_name in param_name_to_idx:
3278
+ modification_param_idx = param_name_to_idx[var_name]
3279
+ mod_node = node
3280
+ elif arg.type in ["identifier", "this"]:
3281
+ var_name = st(arg)
3282
+ if var_name in param_name_to_idx:
3283
+ modification_param_idx = param_name_to_idx[var_name]
3284
+ mod_node = node
3285
+
3286
+ if modification_param_idx is not None and mod_node is not None:
3287
+ parent_statement = return_first_parent_of_types(
3288
+ mod_node, statement_types["node_list_type"]
3289
+ )
3290
+ if parent_statement:
3291
+ statement_id = get_index(parent_statement, index)
3292
+ if statement_id is not None:
3293
+ modifications.append(
3294
+ (modification_param_idx, mod_node, statement_id)
3295
+ )
3296
+
3297
+ if func_name:
3298
+ modification_sites_by_name[func_name] = modifications
3299
+ modification_sites_by_id[func_def_id] = modifications
3300
+
3301
+ return modification_sites_by_name, modification_sites_by_id
3302
+
3303
+
3304
+ def get_cfg_call_targets(cfg_graph, call_site_id):
3305
+ """
3306
+ Get the function definition IDs that are called from a given call site,
3307
+ based on CFG edges (method_call, virtual_call, function_call).
3308
+
3309
+ This uses the CFG's resolution of virtual dispatch, which correctly handles
3310
+ pointer_targets to determine the actual concrete type being called.
3311
+
3312
+ Args:
3313
+ cfg_graph: The CFG graph
3314
+ call_site_id: The call site node ID
3315
+
3316
+ Returns:
3317
+ Set of function definition IDs that are actually called from this call site
3318
+ """
3319
+ target_func_ids = set()
3320
+
3321
+ for edge in cfg_graph.out_edges(call_site_id):
3322
+ edge_data = cfg_graph.get_edge_data(*edge)
3323
+ if edge_data and len(edge_data) > 0:
3324
+ edge_data = edge_data[0]
3325
+ label = edge_data.get("label", "")
3326
+
3327
+ if (
3328
+ label.startswith("method_call")
3329
+ or label.startswith("virtual_call")
3330
+ or label.startswith("function_call")
3331
+ ):
3332
+ target_func_ids.add(edge[1])
3333
+
3334
+ return target_func_ids
3335
+
3336
+
3337
+ def add_interprocedural_edges(
3338
+ final_graph,
3339
+ parser,
3340
+ call_sites,
3341
+ modification_sites_by_id,
3342
+ function_metadata,
3343
+ cfg_graph,
3344
+ rda_table,
3345
+ ):
3346
+ """
3347
+ Add interprocedural DFG edges for pass-by-reference.
3348
+
3349
+ For each call site where &var or reference is passed to a pointer/reference parameter:
3350
+ 1. Use CFG edges to determine which function(s) are actually called (handles virtual dispatch)
3351
+ 2. Add edges from modification sites inside the ACTUALLY CALLED function to uses after the call
3352
+
3353
+ This function uses CFG edge information to correctly resolve virtual dispatch,
3354
+ ensuring that only modifications from the concrete type's implementation are connected.
3355
+
3356
+ Args:
3357
+ final_graph: The DFG graph to add edges to
3358
+ parser: C++ parser
3359
+ call_sites: List of call site information from collect_call_site_information()
3360
+ modification_sites_by_id: Dict mapping func_def_id -> list of modifications
3361
+ function_metadata: Dict from collect_function_metadata()
3362
+ cfg_graph: Control flow graph (contains virtual dispatch resolution)
3363
+ rda_table: RDA table with def/use information
3364
+ """
3365
+ index = parser.index
3366
+
3367
+ for call_site_info in call_sites:
3368
+ call_site_id = call_site_info["call_site_id"]
3369
+ function_name = call_site_info["function_name"]
3370
+ pass_by_ref_args = call_site_info["pass_by_ref_args"]
3371
+
3372
+ target_func_ids = get_cfg_call_targets(cfg_graph, call_site_id)
3373
+
3374
+ if not target_func_ids:
3375
+ continue
3376
+
3377
+ for arg_idx, var_name, var_node in pass_by_ref_args:
3378
+ all_param_mods = []
3379
+
3380
+ for func_def_id in target_func_ids:
3381
+ mods = modification_sites_by_id.get(func_def_id, [])
3382
+ for mod_param_idx, mod_node, mod_statement_id in mods:
3383
+ if mod_param_idx == arg_idx:
3384
+ all_param_mods.append(
3385
+ (mod_param_idx, mod_node, mod_statement_id, func_def_id)
3386
+ )
3387
+
3388
+ if not all_param_mods:
3389
+ continue
3390
+
3391
+ reaching_mods = []
3392
+
3393
+ for (
3394
+ mod_param_idx,
3395
+ mod_node,
3396
+ mod_statement_id,
3397
+ func_def_id,
3398
+ ) in all_param_mods:
3399
+ is_killed = False
3400
+
3401
+ func_mods = [
3402
+ (m_idx, m_node, m_id)
3403
+ for m_idx, m_node, m_id, f_id in all_param_mods
3404
+ if f_id == func_def_id and m_idx == mod_param_idx
3405
+ ]
3406
+
3407
+ visited_in_func = set()
3408
+ queue_in_func = [mod_statement_id]
3409
+
3410
+ while queue_in_func:
3411
+ current = queue_in_func.pop(0)
3412
+ if current in visited_in_func:
3413
+ continue
3414
+ visited_in_func.add(current)
3415
+
3416
+ for edge in cfg_graph.out_edges(current):
3417
+ successor = edge[1]
3418
+
3419
+ if successor in visited_in_func:
3420
+ continue
3421
+
3422
+ for other_mod_idx, other_mod_node, other_mod_id in func_mods:
3423
+ if (
3424
+ other_mod_id == successor
3425
+ and other_mod_id != mod_statement_id
3426
+ ):
3427
+ is_killed = True
3428
+ break
3429
+
3430
+ if is_killed:
3431
+ break
3432
+
3433
+ edge_data = cfg_graph.get_edge_data(*edge)
3434
+ if edge_data:
3435
+ edge_label = edge_data.get(0, {}).get("label", "")
3436
+ if "return" not in edge_label:
3437
+ queue_in_func.append(successor)
3438
+
3439
+ if is_killed:
3440
+ break
3441
+
3442
+ if not is_killed:
3443
+ reaching_mods.append((mod_param_idx, mod_node, mod_statement_id))
3444
+
3445
+ for mod_param_idx, mod_node, mod_statement_id in reaching_mods:
3446
+ successors = []
3447
+ visited = set()
3448
+ queue = [call_site_id]
3449
+
3450
+ while queue:
3451
+ current = queue.pop(0)
3452
+ if current in visited:
3453
+ continue
3454
+ visited.add(current)
3455
+
3456
+ uses_var = False
3457
+ defines_var = False
3458
+ if current != call_site_id and current in rda_table:
3459
+ for used in rda_table[current]["use"]:
3460
+ if used.name == var_name:
3461
+ uses_var = True
3462
+ successors.append(current)
3463
+ break
3464
+
3465
+ for defined in rda_table[current]["def"]:
3466
+ if defined.name == var_name:
3467
+ defines_var = True
3468
+ break
3469
+
3470
+ if (
3471
+ current == call_site_id
3472
+ or not uses_var
3473
+ or (uses_var and defines_var)
3474
+ ):
3475
+ for edge in cfg_graph.out_edges(current):
3476
+ if edge[1] not in visited:
3477
+ queue.append(edge[1])
3478
+
3479
+ for use_site in successors:
3480
+ add_edge(
3481
+ final_graph,
3482
+ mod_statement_id,
3483
+ use_site,
3484
+ {
3485
+ "dataflow_type": "comesFrom",
3486
+ "edge_type": "DFG_edge",
3487
+ "color": "#00A3FF",
3488
+ "used_def": var_name,
3489
+ "interprocedural": "modification_to_use",
3490
+ },
3491
+ )
3492
+
3493
+
3494
+ def add_argument_parameter_edges(final_graph, parser, cfg_graph, rda_table):
3495
+ """
3496
+ Add interprocedural DFG edges for argument-to-parameter data flow.
3497
+
3498
+ For each function call:
3499
+ 1. Extract arguments from call site
3500
+ 2. Extract parameters from function definition
3501
+ 3. Create edges from arguments to parameters
3502
+
3503
+ Args:
3504
+ final_graph: The DFG graph to add edges to
3505
+ parser: C++ parser
3506
+ cfg_graph: Control flow graph with function_call edges
3507
+ rda_table: RDA table with def/use information
3508
+ """
3509
+ index = parser.index
3510
+ node_list = {
3511
+ (node.start_point, node.end_point, node.type): node
3512
+ for node in traverse_tree(parser.tree, [])
3513
+ }
3514
+
3515
+ for edge in list(cfg_graph.edges()):
3516
+ edge_data = cfg_graph.get_edge_data(*edge)
3517
+ if edge_data and len(edge_data) > 0:
3518
+ edge_data = edge_data[0]
3519
+ label = edge_data.get("label", "")
3520
+ controlflow_type = edge_data.get("controlflow_type", "")
3521
+
3522
+ is_function_call = label.startswith(
3523
+ "function_call"
3524
+ ) or controlflow_type.startswith("function_call")
3525
+ is_method_call = label.startswith(
3526
+ "method_call"
3527
+ ) or controlflow_type.startswith("method_call")
3528
+
3529
+ if is_function_call or is_method_call:
3530
+ call_site_id = edge[0]
3531
+ func_def_id = edge[1]
3532
+
3533
+ call_site_node = node_list.get(read_index(index, call_site_id))
3534
+ func_def_node = node_list.get(read_index(index, func_def_id))
3535
+
3536
+ if not (call_site_node and func_def_node):
3537
+ continue
3538
+
3539
+ if func_def_node.type != "function_definition":
3540
+ continue
3541
+
3542
+ call_expr = None
3543
+ for child in traverse_tree(call_site_node, []):
3544
+ if child.type == "call_expression":
3545
+ call_expr = child
3546
+ break
3547
+
3548
+ if not call_expr:
3549
+ continue
3550
+
3551
+ args_node = call_expr.child_by_field_name("arguments")
3552
+ if not args_node or not args_node.named_children:
3553
+ continue
3554
+
3555
+ arguments = args_node.named_children
3556
+
3557
+ declarator = func_def_node.child_by_field_name("declarator")
3558
+ if not declarator:
3559
+ continue
3560
+
3561
+ func_declarator = declarator
3562
+ while func_declarator and func_declarator.type in [
3563
+ "pointer_declarator",
3564
+ "reference_declarator",
3565
+ ]:
3566
+ for child in func_declarator.children:
3567
+ if child.type == "function_declarator":
3568
+ func_declarator = child
3569
+ break
3570
+ else:
3571
+ break
3572
+
3573
+ if not func_declarator or func_declarator.type != "function_declarator":
3574
+ continue
3575
+
3576
+ params_node = func_declarator.child_by_field_name("parameters")
3577
+ if not params_node or not params_node.named_children:
3578
+ continue
3579
+
3580
+ parameters = [
3581
+ p
3582
+ for p in params_node.named_children
3583
+ if p.type == "parameter_declaration"
3584
+ ]
3585
+
3586
+ for idx, (arg, param) in enumerate(zip(arguments, parameters)):
3587
+ param_declarator = param.child_by_field_name("declarator")
3588
+ if not param_declarator:
3589
+ continue
3590
+
3591
+ is_pass_by_ref_or_ptr = False
3592
+ if param_declarator.type in [
3593
+ "pointer_declarator",
3594
+ "reference_declarator",
3595
+ "array_declarator",
3596
+ ]:
3597
+ is_pass_by_ref_or_ptr = True
3598
+
3599
+ if not is_pass_by_ref_or_ptr:
3600
+ continue
3601
+
3602
+ param_id = param_declarator
3603
+ while param_id and param_id.type not in ["identifier"]:
3604
+ if param_id.type in [
3605
+ "pointer_declarator",
3606
+ "reference_declarator",
3607
+ "array_declarator",
3608
+ ]:
3609
+ for child in param_id.named_children:
3610
+ if child.type == "identifier":
3611
+ param_id = child
3612
+ break
3613
+ elif child.type in [
3614
+ "pointer_declarator",
3615
+ "reference_declarator",
3616
+ "function_declarator",
3617
+ "array_declarator",
3618
+ ]:
3619
+ param_id = child
3620
+ break
3621
+ else:
3622
+ break
3623
+ else:
3624
+ break
3625
+
3626
+ if not param_id or param_id.type != "identifier":
3627
+ continue
3628
+
3629
+ param_name = param_id.text.decode("utf-8")
3630
+
3631
+ add_edge(
3632
+ final_graph,
3633
+ call_site_id,
3634
+ func_def_id,
3635
+ {
3636
+ "dataflow_type": "comesFrom",
3637
+ "edge_type": "DFG_edge",
3638
+ "color": "#00A3FF",
3639
+ "used_def": param_name,
3640
+ "interprocedural": "argument_to_parameter",
3641
+ "argument_index": idx,
3642
+ },
3643
+ )
3644
+
3645
+
3646
+ def add_method_member_access_edges(final_graph, parser, cfg_graph, rda_table):
3647
+ """
3648
+ Add interprocedural DFG edges for method member access.
3649
+
3650
+ When an object's method is called (obj.method()), create edges showing:
3651
+ 1. The object flows to the method (as implicit 'this' parameter)
3652
+ 2. Member accesses within the method (this->field) use the object's data
3653
+
3654
+ Args:
3655
+ final_graph: The DFG graph to add edges to
3656
+ parser: C++ parser
3657
+ cfg_graph: Control flow graph with method_call edges
3658
+ rda_table: RDA table with def/use information
3659
+ """
3660
+ index = parser.index
3661
+ node_list = {
3662
+ (node.start_point, node.end_point, node.type): node
3663
+ for node in traverse_tree(parser.tree, [])
3664
+ }
3665
+
3666
+ for edge in list(cfg_graph.edges()):
3667
+ edge_data = cfg_graph.get_edge_data(*edge)
3668
+ if edge_data and len(edge_data) > 0:
3669
+ edge_data = edge_data[0]
3670
+ label = edge_data.get("label", "")
3671
+ object_name = edge_data.get("object_name", "")
3672
+
3673
+ if label in ["method_call", "virtual_call"]:
3674
+ call_site_id = edge[0]
3675
+ method_def_id = edge[1]
3676
+
3677
+ call_site_node = node_list.get(read_index(index, call_site_id))
3678
+ method_def_node = node_list.get(read_index(index, method_def_id))
3679
+
3680
+ if not (call_site_node and method_def_node):
3681
+ continue
3682
+
3683
+ if method_def_node.type != "function_definition":
3684
+ continue
3685
+
3686
+ method_body = method_def_node.child_by_field_name("body")
3687
+ if not method_body:
3688
+ continue
3689
+
3690
+ field_accesses = []
3691
+ for node in traverse_tree(method_body, []):
3692
+ if node.type == "field_expression":
3693
+ argument = node.child_by_field_name("argument")
3694
+ field = node.child_by_field_name("field")
3695
+
3696
+ if argument and field:
3697
+ arg_text = argument.text.decode("utf-8")
3698
+ if arg_text == "this" or arg_text == object_name:
3699
+ parent_stmt = node
3700
+ while (
3701
+ parent_stmt
3702
+ and parent_stmt.type
3703
+ not in statement_types["node_list_type"]
3704
+ ):
3705
+ parent_stmt = parent_stmt.parent
3706
+
3707
+ if parent_stmt:
3708
+ stmt_id = get_index(parent_stmt, index)
3709
+ if stmt_id and stmt_id in cfg_graph.nodes:
3710
+ field_name = field.text.decode("utf-8")
3711
+ field_accesses.append((stmt_id, field_name))
3712
+
3713
+ class_members = set()
3714
+ parent = method_def_node.parent
3715
+ while parent:
3716
+ if parent.type in ["class_specifier", "struct_specifier"]:
3717
+ for node in traverse_tree(parent, []):
3718
+ if node.type == "field_declaration":
3719
+ declarator = node.child_by_field_name("declarator")
3720
+ if declarator:
3721
+ if declarator.type == "identifier":
3722
+ class_members.add(
3723
+ declarator.text.decode("utf-8")
3724
+ )
3725
+ elif declarator.type == "field_identifier":
3726
+ class_members.add(
3727
+ declarator.text.decode("utf-8")
3728
+ )
3729
+ for child in declarator.children:
3730
+ if child.type == "identifier":
3731
+ class_members.add(
3732
+ child.text.decode("utf-8")
3733
+ )
3734
+ break
3735
+ parent = parent.parent
3736
+
3737
+ for node_id in cfg_graph.nodes:
3738
+ node_key = (
3739
+ read_index(index, node_id)
3740
+ if node_id in index.values()
3741
+ else None
3742
+ )
3743
+ if not node_key:
3744
+ continue
3745
+ ast_node = node_list.get(node_key)
3746
+ if not ast_node:
3747
+ continue
3748
+
3749
+ is_in_method = False
3750
+ temp = ast_node
3751
+ while temp:
3752
+ if temp == method_body:
3753
+ is_in_method = True
3754
+ break
3755
+ temp = temp.parent
3756
+
3757
+ if not is_in_method:
3758
+ continue
3759
+
3760
+ if node_id in rda_table:
3761
+ for used in rda_table[node_id].get("use", []):
3762
+ if isinstance(used, Identifier):
3763
+ if used.core in class_members:
3764
+ field_accesses.append((node_id, used.core))
3765
+
3766
+
3767
+ def add_function_return_edges(final_graph, parser, cfg_graph, rda_table):
3768
+ """
3769
+ Add interprocedural DFG edges for function return values.
3770
+
3771
+ For each function call where the return value is used:
3772
+ 1. Find the return statement(s) in the called function
3773
+ 2. Extract the returned expression/variable
3774
+ 3. Find the variable being initialized at the call site
3775
+ 4. Create edge from returned expression to initialized variable
3776
+
3777
+ Args:
3778
+ final_graph: The DFG graph to add edges to
3779
+ parser: C++ parser
3780
+ cfg_graph: Control flow graph with function_return/method_return edges
3781
+ rda_table: RDA table with def/use information
3782
+ """
3783
+ index = parser.index
3784
+ node_list = {
3785
+ (node.start_point, node.end_point, node.type): node
3786
+ for node in traverse_tree(parser.tree, [])
3787
+ }
3788
+
3789
+ for edge in list(cfg_graph.edges()):
3790
+ edge_data = cfg_graph.get_edge_data(*edge)
3791
+ if edge_data and len(edge_data) > 0:
3792
+ edge_data = edge_data[0]
3793
+ label = edge_data.get("label", "")
3794
+
3795
+ if label in ["function_return", "method_return"]:
3796
+ return_node_id = edge[0]
3797
+ call_site_id = edge[1]
3798
+
3799
+ return_statement = node_list.get(read_index(index, return_node_id))
3800
+ call_site_node = node_list.get(read_index(index, call_site_id))
3801
+
3802
+ if not (return_statement and call_site_node):
3803
+ continue
3804
+
3805
+ if (
3806
+ return_statement.type != "return_statement"
3807
+ or not return_statement.named_children
3808
+ ):
3809
+ continue
3810
+
3811
+ if not is_return_value_used(call_site_node):
3812
+ continue
3813
+
3814
+ returned_vars = []
3815
+ if return_node_id in rda_table:
3816
+ for used in rda_table[return_node_id].get("use", []):
3817
+ if isinstance(used, Identifier):
3818
+ returned_vars.append(used.name)
3819
+
3820
+ if not returned_vars:
3821
+ continue
3822
+
3823
+ initialized_vars = []
3824
+ if call_site_id in rda_table:
3825
+ for defined in rda_table[call_site_id].get("def", []):
3826
+ if isinstance(defined, Identifier):
3827
+ initialized_vars.append(defined.name)
3828
+
3829
+ for ret_var in returned_vars:
3830
+ for init_var in initialized_vars:
3831
+ add_edge(
3832
+ final_graph,
3833
+ return_node_id,
3834
+ call_site_id,
3835
+ {
3836
+ "dataflow_type": "comesFrom",
3837
+ "edge_type": "DFG_edge",
3838
+ "color": "#00A3FF",
3839
+ "used_def": init_var,
3840
+ "interprocedural": "return_to_caller",
3841
+ "returned_value": ret_var,
3842
+ },
3843
+ )
3844
+
3845
+
3846
+ def dfg_cpp(properties, CFG_results):
3847
+ """
3848
+ Main driver for generating DFG for C++ programs.
3849
+
3850
+ Combines C procedural features with Java OOP features.
3851
+
3852
+ Args:
3853
+ properties: Configuration dict with DFG options
3854
+ CFG_results: CFG driver results with graph, parser, etc.
3855
+
3856
+ Returns:
3857
+ (final_graph, debug_graph, rda_table, rda_solution)
3858
+ """
3859
+ parser = CFG_results.parser
3860
+ index = parser.index
3861
+ tree = parser.tree
3862
+
3863
+ cfg_graph = copy.deepcopy(CFG_results.graph)
3864
+ node_list = CFG_results.node_list
3865
+
3866
+ cfg_records = (
3867
+ CFG_results.CFG.records
3868
+ if hasattr(CFG_results, "CFG") and hasattr(CFG_results.CFG, "records")
3869
+ else {}
3870
+ )
3871
+ implicit_return_map = cfg_records.get("implicit_return_map", {})
3872
+
3873
+ implicit_return_to_destructor = {
3874
+ ir_id: fn_id for fn_id, ir_id in implicit_return_map.items()
3875
+ }
3876
+
3877
+ processed_edges = []
3878
+
3879
+ called_destructors = set()
3880
+ destructor_chain_edges = []
3881
+ base_destructor_edges = []
3882
+
3883
+ for edge in list(cfg_graph.edges()):
3884
+ edge_data = cfg_graph.get_edge_data(*edge)
3885
+ if edge_data and len(edge_data) > 0:
3886
+ edge_data = edge_data[0]
3887
+ label = edge_data.get("label", "")
3888
+
3889
+ if label == "scope_exit_destructor":
3890
+ called_destructors.add(edge[1])
3891
+ elif label.startswith("destructor_chain|"):
3892
+ destructor_chain_edges.append(edge)
3893
+ elif label == "base_destructor_call":
3894
+ base_destructor_edges.append(edge)
3895
+
3896
+ changed = True
3897
+ while changed:
3898
+ changed = False
3899
+ for edge in destructor_chain_edges:
3900
+ source_ir = edge[0]
3901
+ target_destructor = edge[1]
3902
+ source_destructor = implicit_return_to_destructor.get(source_ir)
3903
+ if source_destructor and source_destructor in called_destructors:
3904
+ if target_destructor not in called_destructors:
3905
+ called_destructors.add(target_destructor)
3906
+ changed = True
3907
+
3908
+ valid_implicit_returns = set()
3909
+ for destructor_id in called_destructors:
3910
+ ir_id = implicit_return_map.get(destructor_id)
3911
+ if ir_id:
3912
+ valid_implicit_returns.add(ir_id)
3913
+
3914
+ for edge in list(cfg_graph.edges()):
3915
+ edge_data = cfg_graph.get_edge_data(*edge)
3916
+ if edge_data and len(edge_data) > 0:
3917
+ edge_data = edge_data[0]
3918
+ label = edge_data.get("label", "")
3919
+
3920
+ if label.startswith("function_call|"):
3921
+ call_statement = node_list.get(read_index(index, edge[0]))
3922
+ function_def = node_list.get(read_index(index, edge[1]))
3923
+
3924
+ if call_statement and function_def:
3925
+ if function_def.type == "function_definition":
3926
+ declarator = function_def.child_by_field_name("declarator")
3927
+ if declarator:
3928
+ params_node = declarator.child_by_field_name("parameters")
3929
+ if params_node and params_node.named_children:
3930
+ processed_edges.append(edge)
3931
+
3932
+ elif label in ["method_return", "function_return"]:
3933
+ return_statement = node_list.get(read_index(index, edge[0]))
3934
+ call_site_node = node_list.get(read_index(index, edge[1]))
3935
+
3936
+ if return_statement and return_statement.type == "return_statement":
3937
+ if return_statement.named_children:
3938
+ if call_site_node and is_return_value_used(call_site_node):
3939
+ processed_edges.append(edge)
3940
+
3941
+ elif label == "constructor_call":
3942
+ processed_edges.append(edge)
3943
+
3944
+ elif label == "base_constructor_call":
3945
+ processed_edges.append(edge)
3946
+
3947
+ elif label == "scope_exit_destructor":
3948
+ processed_edges.append(edge)
3949
+
3950
+ elif label == "base_destructor_call":
3951
+ source_ir = edge[0]
3952
+ if source_ir in valid_implicit_returns:
3953
+ processed_edges.append(edge)
3954
+
3955
+ elif label == "virtual_call":
3956
+ processed_edges.append(edge)
3957
+
3958
+ elif label == "method_call":
3959
+ processed_edges.append(edge)
3960
+
3961
+ start_lambda_time = time.time()
3962
+ lambda_map = discover_lambdas(parser, CFG_results)
3963
+ end_lambda_time = time.time()
3964
+
3965
+ function_metadata_by_name, function_metadata_by_id = collect_function_metadata(
3966
+ parser
3967
+ )
3968
+ pointer_modifications = analyze_pointer_modifications(
3969
+ parser, function_metadata_by_name
3970
+ )
3971
+
3972
+ start_rda_init_time = time.time()
3973
+ rda_table = build_rda_table(
3974
+ parser,
3975
+ CFG_results,
3976
+ lambda_map,
3977
+ function_metadata_by_name,
3978
+ pointer_modifications,
3979
+ )
3980
+ end_rda_init_time = time.time()
3981
+
3982
+ start_rda_time = time.time()
3983
+ rda_solution = start_rda(index, rda_table, cfg_graph)
3984
+ end_rda_time = time.time()
3985
+
3986
+ final_graph = get_required_edges_from_def_to_use(
3987
+ index,
3988
+ cfg_graph,
3989
+ rda_solution,
3990
+ rda_table,
3991
+ cfg_graph.nodes,
3992
+ processed_edges,
3993
+ properties,
3994
+ lambda_map,
3995
+ node_list,
3996
+ parser,
3997
+ )
3998
+
3999
+ call_sites = collect_call_site_information(
4000
+ parser, function_metadata_by_name, cfg_graph
4001
+ )
4002
+ modification_sites_by_name, modification_sites_by_id = find_modification_sites(
4003
+ parser, function_metadata_by_id, pointer_modifications
4004
+ )
4005
+ add_interprocedural_edges(
4006
+ final_graph,
4007
+ parser,
4008
+ call_sites,
4009
+ modification_sites_by_id,
4010
+ function_metadata_by_name,
4011
+ cfg_graph,
4012
+ rda_table,
4013
+ )
4014
+
4015
+ add_argument_parameter_edges(final_graph, parser, cfg_graph, rda_table)
4016
+
4017
+ add_function_return_edges(final_graph, parser, cfg_graph, rda_table)
4018
+
4019
+ add_method_member_access_edges(final_graph, parser, cfg_graph, rda_table)
4020
+
4021
+ if debug:
4022
+ logger.info(
4023
+ "RDA init: {:.3f}s, RDA: {:.3f}s",
4024
+ end_rda_init_time - start_rda_init_time,
4025
+ end_rda_time - start_rda_time,
4026
+ )
4027
+
4028
+ debug_graph = rda_cfg_map(rda_solution, CFG_results)
4029
+
4030
+ return final_graph, debug_graph, rda_table, rda_solution