Skip to content

Commit f0546bc

Browse files
committed
Merge remote-tracking branch 'origin/omni-java' into codeflash/optimize-pr1199-2026-03-02T05.57.20
2 parents 673406e + 96b99ea commit f0546bc

11 files changed

Lines changed: 1034 additions & 95 deletions

File tree

codeflash/languages/java/discovery.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,11 @@ def _should_include_method(
129129
True if the method should be included.
130130
131131
"""
132+
# Skip methods that belong to an inner/nested class — they cannot be reliably
133+
# instrumented or tested in isolation (see discussion in discovery module).
134+
if method.is_class_nested:
135+
return False
136+
132137
# Skip abstract methods (no implementation to optimize)
133138
if method.is_abstract:
134139
return False

codeflash/languages/java/parser.py

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ class JavaMethodNode:
5252
class_name: str | None
5353
source_text: str
5454
javadoc_start_line: int | None = None # Line where Javadoc comment starts
55+
formal_parameters_text: str | None = None # Raw formal parameters "(Type name, ...)" for matching
56+
is_class_nested: bool = False # True when the enclosing class is itself nested inside another class
5557

5658

5759
@dataclass
@@ -182,6 +184,104 @@ def find_methods(
182184

183185
return methods
184186

187+
def find_constructors(self, source: str, class_name: str | None = None) -> list[JavaMethodNode]:
188+
"""Find all constructor definitions in source code.
189+
190+
Args:
191+
source: The source code to analyze.
192+
class_name: Optional class name to filter constructors.
193+
194+
Returns:
195+
List of JavaMethodNode objects describing found constructors.
196+
The ``name`` field of each node is the constructor name (i.e. the class name).
197+
198+
"""
199+
source_bytes = source.encode("utf8")
200+
tree = self.parse(source_bytes)
201+
constructors: list[JavaMethodNode] = []
202+
self._walk_tree_for_constructors(
203+
tree.root_node, source_bytes, constructors, current_class=None, target_class=class_name
204+
)
205+
return constructors
206+
207+
def _walk_tree_for_constructors(
208+
self,
209+
node: Node,
210+
source_bytes: bytes,
211+
constructors: list[JavaMethodNode],
212+
current_class: str | None,
213+
target_class: str | None,
214+
) -> None:
215+
"""Recursively walk the tree to find constructor declarations."""
216+
new_class = current_class
217+
type_declarations = ("class_declaration", "interface_declaration", "enum_declaration")
218+
if node.type in type_declarations:
219+
name_node = node.child_by_field_name("name")
220+
if name_node:
221+
new_class = self.get_node_text(name_node, source_bytes)
222+
223+
if node.type == "constructor_declaration":
224+
constructor_info = self._extract_constructor_info(node, source_bytes, new_class)
225+
if constructor_info:
226+
if target_class is None or constructor_info.class_name == target_class:
227+
constructors.append(constructor_info)
228+
229+
for child in node.children:
230+
self._walk_tree_for_constructors(
231+
child,
232+
source_bytes,
233+
constructors,
234+
current_class=new_class if node.type in type_declarations else current_class,
235+
target_class=target_class,
236+
)
237+
238+
def _extract_constructor_info(
239+
self, node: Node, source_bytes: bytes, current_class: str | None
240+
) -> JavaMethodNode | None:
241+
"""Extract constructor information from a constructor_declaration node."""
242+
name_node = node.child_by_field_name("name")
243+
if not name_node:
244+
return None
245+
name = self.get_node_text(name_node, source_bytes)
246+
247+
is_public = False
248+
is_private = False
249+
is_protected = False
250+
for child in node.children:
251+
if child.type == "modifiers":
252+
modifier_text = self.get_node_text(child, source_bytes)
253+
is_public = "public" in modifier_text
254+
is_private = "private" in modifier_text
255+
is_protected = "protected" in modifier_text
256+
break
257+
258+
# Extract formal parameters text for signature matching
259+
params_node = node.child_by_field_name("parameters")
260+
formal_parameters_text = self.get_node_text(params_node, source_bytes) if params_node else "()"
261+
262+
source_text = self.get_node_text(node, source_bytes)
263+
javadoc_start_line = self._find_preceding_javadoc(node, source_bytes)
264+
265+
return JavaMethodNode(
266+
name=name,
267+
node=node,
268+
start_line=node.start_point[0] + 1,
269+
end_line=node.end_point[0] + 1,
270+
start_col=node.start_point[1],
271+
end_col=node.end_point[1],
272+
is_static=False,
273+
is_public=is_public,
274+
is_private=is_private,
275+
is_protected=is_protected,
276+
is_abstract=False,
277+
is_synchronized=False,
278+
return_type=None,
279+
class_name=current_class,
280+
source_text=source_text,
281+
javadoc_start_line=javadoc_start_line,
282+
formal_parameters_text=formal_parameters_text,
283+
)
284+
185285
def _walk_tree_for_methods(
186286
self,
187287
node: Node,
@@ -190,6 +290,7 @@ def _walk_tree_for_methods(
190290
include_private: bool,
191291
include_static: bool,
192292
current_class: str | None,
293+
class_depth: int = 0,
193294
) -> None:
194295
"""Recursively walk the tree to find method definitions."""
195296
new_class = current_class
@@ -205,6 +306,10 @@ def _walk_tree_for_methods(
205306
method_info = self._extract_method_info(node, source_bytes, current_class)
206307

207308
if method_info:
309+
# A method is nested when its enclosing class is itself inside another
310+
# class (class_depth >= 2: depth 1 = outermost class, depth 2+ = nested).
311+
method_info.is_class_nested = class_depth >= 2
312+
208313
# Apply filters
209314
should_include = True
210315

@@ -217,7 +322,7 @@ def _walk_tree_for_methods(
217322
if should_include:
218323
methods.append(method_info)
219324

220-
# Recurse into children
325+
# Recurse into children, incrementing depth when entering a type declaration
221326
for child in node.children:
222327
self._walk_tree_for_methods(
223328
child,
@@ -226,6 +331,7 @@ def _walk_tree_for_methods(
226331
include_private=include_private,
227332
include_static=include_static,
228333
current_class=new_class if node.type in type_declarations else current_class,
334+
class_depth=class_depth + 1 if node.type in type_declarations else class_depth,
229335
)
230336

231337
def _extract_method_info(self, node: Node, source_bytes: bytes, current_class: str | None) -> JavaMethodNode | None:

0 commit comments

Comments
 (0)