Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 102 additions & 48 deletions src/analyzer/code_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,12 @@ def analyze_file(self, file_path: Path) -> AnalysisResult:
"""Analyze a Python file and return the results."""
with open(file_path, 'r') as f:
content = f.read()

tree = ast.parse(content)

# Perform various analyses
self._analyze_types(tree)
self._analyze_performance(tree)
self._analyze_memory_usage(tree)
self._analyze_hot_paths(tree)
self._analyze_dependencies(tree)
self._analyze_complexity(tree)


# Perform various analyses in a single traversal
self._traverse_tree(tree)

return AnalysisResult(
type_info=self.type_info,
performance_bottlenecks=self.performance_bottlenecks,
Expand All @@ -48,52 +43,111 @@ def analyze_file(self, file_path: Path) -> AnalysisResult:
dependencies=self.dependencies,
complexity=self.complexity
)

def _analyze_types(self, tree: ast.AST) -> None:
"""Analyze and infer types in the code."""
for node in ast.walk(tree):
if isinstance(node, ast.Assign):
self._infer_variable_type(node)
elif isinstance(node, ast.FunctionDef):
self._infer_function_types(node)

def _analyze_performance(self, tree: ast.AST) -> None:
"""Identify performance bottlenecks."""
for node in ast.walk(tree):
if isinstance(node, ast.For):
self._check_loop_performance(node)
elif isinstance(node, ast.Call):
self._check_function_call_performance(node)

def _analyze_memory_usage(self, tree: ast.AST) -> None:
"""Analyze memory usage patterns."""

def _traverse_tree(self, tree: ast.AST) -> None:
"""Walk the AST once and delegate analysis to helper methods."""
for node in ast.walk(tree):
if isinstance(node, ast.List):
self._analyze_list_memory(node)
elif isinstance(node, ast.Dict):
self._analyze_dict_memory(node)

def _analyze_hot_paths(self, tree: ast.AST) -> None:
self._analyze_types(node)
self._analyze_performance(node)
self._analyze_memory_usage(node)
self._analyze_hot_paths(node)
self._analyze_dependencies(node)
self._analyze_complexity(node)

def _analyze_types(self, node: ast.AST) -> None:
"""Analyze and infer types for a single node."""
if isinstance(node, ast.Assign):
self._infer_variable_type(node)
elif isinstance(node, ast.FunctionDef):
self._infer_function_types(node)

def _analyze_performance(self, node: ast.AST) -> None:
"""Identify performance bottlenecks for a single node."""
if isinstance(node, ast.For):
self._check_loop_performance(node)
elif isinstance(node, ast.Call):
self._check_function_call_performance(node)

def _analyze_memory_usage(self, node: ast.AST) -> None:
"""Analyze memory usage patterns for a single node."""
if isinstance(node, ast.List):
self._analyze_list_memory(node)
elif isinstance(node, ast.Dict):
self._analyze_dict_memory(node)

def _analyze_hot_paths(self, node: ast.AST) -> None:
"""Identify frequently executed code paths."""
# Implementation will use static analysis and heuristics
pass
def _analyze_dependencies(self, tree: ast.AST) -> None:

def _analyze_dependencies(self, node: ast.AST) -> None:
"""Build dependency graph of the code."""
for node in ast.walk(tree):
if isinstance(node, ast.Import):
self._add_import_dependency(node)
elif isinstance(node, ast.ImportFrom):
self._add_import_from_dependency(node)

def _analyze_complexity(self, tree: ast.AST) -> None:
"""Calculate code complexity metrics."""
for node in ast.walk(tree):
if isinstance(node, ast.FunctionDef):
self._calculate_function_complexity(node)
if isinstance(node, ast.Import):
self._add_import_dependency(node)
elif isinstance(node, ast.ImportFrom):
self._add_import_from_dependency(node)

def _analyze_complexity(self, node: ast.AST) -> None:
"""Calculate code complexity metrics for a node."""
if isinstance(node, ast.FunctionDef):
self._calculate_function_complexity(node)

def _infer_variable_type(self, node: ast.Assign) -> None:
"""Infer the type of a variable assignment."""
# Handle tuple targets (unpacking assignments) early
Copy link

Copilot AI Jul 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tuple unpacking logic (lines 98-149) is extremely complex and deeply nested, making it difficult to maintain. Consider extracting this into a separate method like _handle_tuple_unpacking(node) to improve readability and testability.

Copilot uses AI. Check for mistakes.
if isinstance(node.targets[0], ast.Tuple):
# Move existing tuple unpacking logic here
if isinstance(node.value, ast.Call):
if isinstance(node.value.func, ast.Name):
func_name = node.value.func.id
if func_name in self.type_info:
return_type = self.type_info[func_name].get('return_type', 'std::tuple<int, int>')
if return_type.startswith('std::tuple<'):
types = return_type[11:-1].split(', ')
for i, target in enumerate(node.targets[0].elts):
if i < len(types):
if isinstance(target, ast.Tuple):
nested_types = types[i][11:-1].split(', ')
for j, nested_target in enumerate(target.elts):
if j < len(nested_types):
self.type_info[nested_target.id] = nested_types[j]
else:
self.type_info[nested_target.id] = 'int'
else:
self.type_info[target.id] = types[i]
else:
self.type_info[target.id] = 'int'
else:
for target in node.targets[0].elts:
if isinstance(target, ast.Name):
self.type_info[target.id] = 'int'
else:
for target in node.targets[0].elts:
if isinstance(target, ast.Tuple):
for nested_target in target.elts:
self.type_info[nested_target.id] = 'int'
elif isinstance(target, ast.Name):
self.type_info[target.id] = 'int'
elif isinstance(node.value, ast.Tuple):
for i, (target, value) in enumerate(zip(node.targets[0].elts, node.value.elts)):
if isinstance(target, ast.Tuple):
if isinstance(value, ast.Tuple):
for j, (nested_target, nested_value) in enumerate(zip(target.elts, value.elts)):
self.type_info[nested_target.id] = self._infer_expression_type(nested_value)
else:
for nested_target in target.elts:
self.type_info[nested_target.id] = 'int'
else:
self.type_info[target.id] = self._infer_expression_type(value)
else:
for target in node.targets[0].elts:
if isinstance(target, ast.Tuple):
for nested_target in target.elts:
self.type_info[nested_target.id] = 'int'
else:
self.type_info[target.id] = 'int'
return

# Basic type inference implementation
if isinstance(node.value, ast.Constant):
if isinstance(node.value.value, (int, float)):
Expand Down
97 changes: 46 additions & 51 deletions src/analyzer/code_analyzer_fixed.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,9 @@ def analyze_file(self, file_path: Path) -> AnalysisResult:

tree = ast.parse(content)

# Perform various analyses
# Perform various analyses in a single traversal
self._analyze_classes(tree) # Analyze classes first to detect inheritance
self._analyze_types(tree)
self._analyze_performance(tree)
self._analyze_memory_usage(tree)
self._analyze_hot_paths(tree)
self._analyze_dependencies(tree)
self._analyze_complexity(tree)
self._traverse_tree(tree)

return AnalysisResult(
type_info=self.type_info,
Expand Down Expand Up @@ -202,61 +197,61 @@ def _analyze_method_attributes(self, class_name: str, node: ast.FunctionDef) ->
if class_name in self.type_info and 'attributes' in self.type_info[class_name]:
self.type_info[class_name]['attributes'][attr_name] = attr_type

def _analyze_types(self, tree: ast.AST) -> None:
"""Analyze and infer types in the code."""
def _traverse_tree(self, tree: ast.AST) -> None:
"""Walk the AST once and delegate analysis to helper methods."""
hot_paths: List[List[str]] = []
for node in ast.walk(tree):
if isinstance(node, ast.Assign):
self._infer_variable_type(node)
elif isinstance(node, ast.FunctionDef) and not self.current_class:
# Only analyze standalone functions here, class methods are handled separately
self._infer_function_types(node)

def _analyze_performance(self, tree: ast.AST) -> None:
"""Identify performance bottlenecks."""
for node in ast.walk(tree):
if isinstance(node, ast.For):
self._check_loop_performance(node)
elif isinstance(node, ast.Call):
self._check_function_call_performance(node)

def _analyze_memory_usage(self, tree: ast.AST) -> None:
"""Analyze memory usage patterns."""
for node in ast.walk(tree):
if isinstance(node, ast.List):
self._analyze_list_memory(node)
elif isinstance(node, ast.Dict):
self._analyze_dict_memory(node)

def _analyze_hot_paths(self, tree: ast.AST) -> None:
"""Identify frequently executed code paths."""
# Basic implementation that marks loops and conditionals
hot_paths = []
for node in ast.walk(tree):
if isinstance(node, (ast.For, ast.While)):
if hasattr(node, 'body') and node.body:
path = [self._get_node_location(stmt) for stmt in node.body]
hot_paths.append(path)
self._analyze_types(node)
self._analyze_performance(node)
self._analyze_memory_usage(node)
if isinstance(node, (ast.For, ast.While)) and hasattr(node, 'body') and node.body:
path = [self._get_node_location(stmt) for stmt in node.body]
hot_paths.append(path)
self._analyze_dependencies(node)
self._analyze_complexity(node)
self.hot_paths = hot_paths

def _analyze_types(self, node: ast.AST) -> None:
"""Analyze and infer types for a single node."""
if isinstance(node, ast.Assign):
self._infer_variable_type(node)
elif isinstance(node, ast.FunctionDef) and not (node.args.args and node.args.args[0].arg == 'self'):
# Only analyze standalone functions here; class methods are handled separately
self._infer_function_types(node)

def _analyze_performance(self, node: ast.AST) -> None:
"""Identify performance bottlenecks for a single node."""
if isinstance(node, ast.For):
self._check_loop_performance(node)
elif isinstance(node, ast.Call):
self._check_function_call_performance(node)

def _analyze_memory_usage(self, node: ast.AST) -> None:
"""Analyze memory usage patterns for a single node."""
if isinstance(node, ast.List):
self._analyze_list_memory(node)
elif isinstance(node, ast.Dict):
self._analyze_dict_memory(node)

# _analyze_hot_paths merged into _traverse_tree

def _get_node_location(self, node: ast.AST) -> str:
"""Get a string representation of a node's location."""
if hasattr(node, 'lineno'):
return f"line_{node.lineno}"
return "unknown_location"

def _analyze_dependencies(self, tree: ast.AST) -> None:
def _analyze_dependencies(self, node: ast.AST) -> None:
"""Build dependency graph of the code."""
for node in ast.walk(tree):
if isinstance(node, ast.Import):
self._add_import_dependency(node)
elif isinstance(node, ast.ImportFrom):
self._add_import_from_dependency(node)
if isinstance(node, ast.Import):
self._add_import_dependency(node)
elif isinstance(node, ast.ImportFrom):
self._add_import_from_dependency(node)

def _analyze_complexity(self, tree: ast.AST) -> None:
"""Calculate code complexity metrics."""
for node in ast.walk(tree):
if isinstance(node, ast.FunctionDef):
self._calculate_function_complexity(node)
def _analyze_complexity(self, node: ast.AST) -> None:
"""Calculate code complexity metrics for a node."""
if isinstance(node, ast.FunctionDef):
self._calculate_function_complexity(node)

def _store_type_for_target(self, target: ast.AST, type_str: str) -> None:
"""Helper method to safely store type information for a target."""
Expand Down
12 changes: 6 additions & 6 deletions src/converter/code_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ def _generate_header(self, analysis_result: Dict) -> str:
namespace pytocpp {

"""
# Add function declarations
for func_name, func_info in analysis_result.get('functions', {}).items():
if func_name.startswith('calculate_'):
# Add function declarations using analyzed type information
for func_name, func_info in getattr(analysis_result, 'type_info', {}).items():
if isinstance(func_info, dict) and func_name.startswith('calculate_'):
# Get return type
return_type = func_info.get('return_type', 'int')
# Get parameter types
Expand All @@ -132,9 +132,9 @@ def _generate_implementation(self, analysis_result: Dict) -> str:
namespace pytocpp {

"""
# Add function implementations
for func_name, func_info in analysis_result.get('functions', {}).items():
if func_name.startswith('calculate_'):
# Add function implementations using analyzed type information
for func_name, func_info in getattr(analysis_result, 'type_info', {}).items():
if isinstance(func_info, dict) and func_name.startswith('calculate_'):
impl += self._generate_function_impl(func_name, func_info)

impl += "} // namespace pytocpp\n"
Expand Down