Packages
metastatic
0.3.2
0.26.0
0.25.0
0.24.1
0.24.0
0.23.0
0.22.2
0.22.1
0.22.0
0.21.3
0.21.2
0.21.1
0.21.0
0.20.3
0.20.2
0.20.1
0.20.0
0.19.0
0.18.0
0.17.0
0.16.0
0.15.1
0.15.0
0.14.2
0.14.1
0.14.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.0
0.11.0
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.2
0.9.1
0.9.0
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.1
0.7.0
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.2
0.4.1
0.4.0
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.0
0.1.3
0.1.2
0.1.1
0.1.0
Cross-language code meta-model library using unified MetaAST representation. Parse, transform, and translate code across Python, Elixir, Ruby, Erlang, Haskell, and more via a shared three-tuple AST format.
Current section
Files
Jump to
Current section
Files
lib/metastatic/analysis/complexity/function_metrics.ex
defmodule Metastatic.Analysis.Complexity.FunctionMetrics do
@moduledoc """
Function-level complexity metrics.
Note: MetaAST currently doesn't have a function_definition construct.
For Phase 4, we analyze the entire document as a single "function".
Future enhancement: Add function_definition to MetaAST M2.2 Extended layer
for per-function analysis.
## Metrics
- **Statement count**: Number of executable statements
- **Return points**: Number of early_return nodes
- **Variable count**: Number of distinct variables used
- **Parameter count**: 0 (not applicable without function_definition)
## Examples
iex> ast = {:assignment, {:variable, "x"}, {:literal, :integer, 5}}
iex> metrics = Metastatic.Analysis.Complexity.FunctionMetrics.calculate(ast)
iex> metrics.statement_count
1
iex> metrics.variable_count
1
"""
alias Metastatic.AST
@type t :: %{
statement_count: non_neg_integer(),
return_points: non_neg_integer(),
variable_count: non_neg_integer(),
parameter_count: non_neg_integer()
}
@doc """
Calculates function-level metrics for a MetaAST node.
## Examples
iex> ast = {:early_return, {:variable, "x"}}
iex> metrics = Metastatic.Analysis.Complexity.FunctionMetrics.calculate(ast)
iex> metrics.return_points
1
"""
@spec calculate(AST.meta_ast()) :: t()
def calculate(ast) do
statement_count = count_statements(ast)
return_points = count_returns(ast)
variables = AST.variables(ast)
variable_count = MapSet.size(variables)
%{
statement_count: statement_count,
return_points: return_points,
variable_count: variable_count,
parameter_count: 0
}
end
# Private implementation
defp count_statements(ast) do
walk_statements(ast, 0)
end
defp walk_statements({:assignment, _, _}, count), do: count + 1
defp walk_statements({:inline_match, _, _}, count), do: count + 1
defp walk_statements({:function_call, _, _}, count), do: count + 1
defp walk_statements({:early_return, _}, count), do: count + 1
defp walk_statements({:conditional, _, then_br, else_br}, count) do
count = count + 1
count = walk_statements(then_br, count)
walk_statements(else_br, count)
end
defp walk_statements({:loop, :while, _, body}, count) do
count = count + 1
walk_statements(body, count)
end
defp walk_statements({:loop, _, _, _, body}, count) do
count = count + 1
walk_statements(body, count)
end
defp walk_statements({:exception_handling, try_block, catches, else_block}, count) do
count = count + 1
count = walk_statements(try_block, count)
count =
Enum.reduce(catches, count, fn {_, _, catch_body}, c ->
walk_statements(catch_body, c)
end)
walk_statements(else_block, count)
end
defp walk_statements({:pattern_match, _, branches}, count) do
count = count + 1
Enum.reduce(branches, count, fn
{:match_arm, _pattern, _guard, body}, c -> walk_statements(body, c)
{_, branch}, c -> walk_statements(branch, c)
end)
end
defp walk_statements({:match_arm, _pattern, guard, body}, count) do
count = if guard, do: walk_statements(guard, count), else: count
walk_statements(body, count)
end
defp walk_statements({:lambda, _, body}, count) do
count = count + 1
walk_statements(body, count)
end
defp walk_statements({:block, stmts}, count) when is_list(stmts) do
Enum.reduce(stmts, count, fn stmt, c -> walk_statements(stmt, c) end)
end
# M2.2s: Structural/Organizational Types
defp walk_statements({:container, _type, _name, _metadata, members}, count)
when is_list(members) do
count = count + 1
Enum.reduce(members, count, fn member, c -> walk_statements(member, c) end)
end
defp walk_statements({:function_def, _visibility, _name, params, metadata, body}, count) do
count = count + 1
# Walk parameters
count =
Enum.reduce(params, count, fn
{:pattern, pattern}, c -> walk_statements(pattern, c)
{:default, _name, default}, c -> walk_statements(default, c)
_simple_param, c -> c
end)
# Walk guards if present
count =
case Map.get(metadata, :guards) do
nil -> count
guard -> walk_statements(guard, count)
end
# Walk body
walk_statements(body, count)
end
defp walk_statements({:attribute_access, obj, _attr}, count) do
walk_statements(obj, count)
end
defp walk_statements({:augmented_assignment, _op, _target, value}, count) do
count = count + 1
walk_statements(value, count)
end
defp walk_statements({:property, _name, getter, setter, _metadata}, count) do
count = count + 1
count = if getter, do: walk_statements(getter, count), else: count
if setter, do: walk_statements(setter, count), else: count
end
defp walk_statements({:async_operation, _, body}, count) do
count = count + 1
walk_statements(body, count)
end
# Language-specific: traverse embedded body if present
defp walk_statements({:language_specific, _, _, _, metadata}, count) when is_map(metadata) do
count = count + 1
case Map.get(metadata, :body) do
nil -> count
body -> walk_statements(body, count)
end
end
defp walk_statements({:language_specific, _, _, _}, count), do: count + 1
defp walk_statements({:language_specific, _, _}, count), do: count + 1
defp walk_statements(nil, count), do: count
defp walk_statements(_, count), do: count
defp count_returns(ast) do
walk_returns(ast, 0)
end
defp walk_returns({:early_return, _}, count), do: count + 1
defp walk_returns({:conditional, _, then_br, else_br}, count) do
count = walk_returns(then_br, count)
walk_returns(else_br, count)
end
defp walk_returns({:loop, :while, _, body}, count), do: walk_returns(body, count)
defp walk_returns({:loop, _, _, _, body}, count), do: walk_returns(body, count)
defp walk_returns({:exception_handling, try_block, catches, else_block}, count) do
count = walk_returns(try_block, count)
count =
Enum.reduce(catches, count, fn {_, _, catch_body}, c ->
walk_returns(catch_body, c)
end)
walk_returns(else_block, count)
end
defp walk_returns({:pattern_match, _, branches}, count) do
Enum.reduce(branches, count, fn
{:match_arm, _pattern, _guard, body}, c -> walk_returns(body, c)
{_, branch}, c -> walk_returns(branch, c)
end)
end
defp walk_returns({:match_arm, _pattern, guard, body}, count) do
count = if guard, do: walk_returns(guard, count), else: count
walk_returns(body, count)
end
defp walk_returns({:lambda, _, body}, count), do: walk_returns(body, count)
defp walk_returns({:block, stmts}, count) when is_list(stmts) do
Enum.reduce(stmts, count, fn stmt, c -> walk_returns(stmt, c) end)
end
# M2.2s: Structural/Organizational Types
defp walk_returns({:container, _type, _name, _metadata, members}, count)
when is_list(members) do
Enum.reduce(members, count, fn member, c -> walk_returns(member, c) end)
end
defp walk_returns({:function_def, _visibility, _name, params, metadata, body}, count) do
# Walk parameters
count =
Enum.reduce(params, count, fn
{:pattern, pattern}, c -> walk_returns(pattern, c)
{:default, _name, default}, c -> walk_returns(default, c)
_simple_param, c -> c
end)
# Walk guards if present
count =
case Map.get(metadata, :guards) do
nil -> count
guard -> walk_returns(guard, count)
end
# Walk body
walk_returns(body, count)
end
defp walk_returns({:attribute_access, obj, _attr}, count) do
walk_returns(obj, count)
end
defp walk_returns({:augmented_assignment, _op, _target, value}, count) do
walk_returns(value, count)
end
defp walk_returns({:property, _name, getter, setter, _metadata}, count) do
count = if getter, do: walk_returns(getter, count), else: count
if setter, do: walk_returns(setter, count), else: count
end
defp walk_returns({:async_operation, _, body}, count), do: walk_returns(body, count)
# Language-specific: traverse embedded body if present
defp walk_returns({:language_specific, _, _, _, metadata}, count) when is_map(metadata) do
case Map.get(metadata, :body) do
nil -> count
body -> walk_returns(body, count)
end
end
defp walk_returns({:language_specific, _, _, _}, count), do: count
defp walk_returns({:language_specific, _, _}, count), do: count
defp walk_returns(nil, count), do: count
defp walk_returns(_, count), do: count
end