• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nshkrdotcom / ElixirScope / 54fc5911e9000f1ebc4a810340870a7b2a41e5f6

29 May 2025 01:33AM UTC coverage: 60.215% (-0.009%) from 60.224%
54fc5911e9000f1ebc4a810340870a7b2a41e5f6

push

github

NSHkr
refactor cfg

351 of 544 new or added lines in 10 files covered. (64.52%)

9 existing lines in 3 files now uncovered.

5703 of 9471 relevant lines covered (60.22%)

3704.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.7
/lib/elixir_scope/ast_repository/enhanced/cfg_generator/sequential_handler.ex
1
defmodule ElixirScope.ASTRepository.Enhanced.CFGGenerator.SequentialHandler do
2
  @moduledoc """
3
  Handles sequential AST nodes (blocks, assignments, pipes) for CFG generation.
4
  """
5

6
  alias ElixirScope.ASTRepository.Enhanced.{
7
    CFGNode,
8
    CFGEdge,
9
    # ScopeInfo, # Not directly used here
10
    CFGGenerator.Utils
11
  }
12

13
  @doc """
14
  Processes a block of statements (statement sequence).
15
  `ast_processor_func` is `ASTProcessor.process_ast_node/2`.
16
  """
17
  def handle_statement_sequence(statements, _meta, state, ast_processor_func) do
18
    Enum.reduce(statements, {%{}, [], [], %{}, state}, fn stmt, {nodes, edges, prev_exits, scopes, acc_state} ->
100✔
19
      {stmt_nodes, stmt_edges, stmt_exits, stmt_scopes, new_state} =
300✔
20
        ast_processor_func.(stmt, acc_state)
21

22
      connection_edges = if prev_exits == [] do
300✔
23
        []
24
      else
25
        stmt_entry_nodes = Utils.get_entry_nodes(stmt_nodes)
201✔
26
        if stmt_entry_nodes == [] do
201✔
27
          []
28
        else
29
          Enum.flat_map(prev_exits, fn prev_exit ->
201✔
30
            Enum.map(stmt_entry_nodes, fn stmt_entry ->
202✔
31
              %CFGEdge{
838✔
32
                from_node_id: prev_exit,
33
                to_node_id: stmt_entry,
34
                type: :sequential,
35
                condition: nil,
36
                probability: 1.0,
37
                metadata: %{}
38
              }
39
            end)
40
          end)
41
        end
42
      end
43

44
      merged_nodes = Map.merge(nodes, stmt_nodes)
300✔
45
      merged_edges = edges ++ stmt_edges ++ connection_edges
300✔
46
      merged_scopes = Map.merge(scopes, stmt_scopes)
300✔
47

48
      new_prev_exits = if stmt_exits == [], do: prev_exits, else: stmt_exits
300✔
49

50
      {merged_nodes, merged_edges, new_prev_exits, merged_scopes, new_state}
300✔
51
    end)
52
  end
53

54
  @doc """
55
  Processes an assignment operation.
56
  `ast_processor_func` is `ASTProcessor.process_ast_node/2`.
57
  """
58
  def handle_assignment(pattern, expression, meta, state, ast_processor_func) do
59
    {assign_id, updated_state} = Utils.generate_node_id("assignment", state)
200✔
60

61
    assign_node = %CFGNode{
200✔
62
      id: assign_id,
63
      type: :assignment,
64
      ast_node_id: Utils.get_ast_node_id(meta),
65
      line: Utils.get_line_number(meta),
66
      scope_id: state.current_scope,
200✔
67
      expression: {:=, meta, [pattern, expression]},
68
      predecessors: [],
69
      successors: [],
70
      metadata: %{pattern: pattern, expression: expression}
71
    }
72

73
    {expr_nodes, expr_edges, expr_exits, expr_scopes, expr_state} =
200✔
74
      ast_processor_func.(expression, updated_state)
75

76
    {final_expr_nodes, final_expr_edges, final_expr_exits, final_expr_scopes, final_expr_state} =
200✔
77
      if map_size(expr_nodes) == 0 do
NEW
78
        {expr_node_id, expr_node_state} = Utils.generate_node_id("expression", expr_state)
×
NEW
79
        expr_node = %CFGNode{
×
80
          id: expr_node_id,
81
          type: :expression,
82
          ast_node_id: Utils.get_ast_node_id(meta), # Attempt to get meta from assignment for RHS expression line
83
          line: Utils.get_line_number(meta),
NEW
84
          scope_id: state.current_scope,
×
85
          expression: expression,
86
          predecessors: [],
87
          successors: [],
88
          metadata: %{expression: expression}
89
        }
NEW
90
        {%{expr_node_id => expr_node}, [], [expr_node_id], %{}, expr_node_state}
×
91
      else
92
        {expr_nodes, expr_edges, expr_exits, expr_scopes, expr_state}
200✔
93
      end
94

95
    expr_to_assign_edges = Enum.map(final_expr_exits, fn exit_id ->
200✔
96
      %CFGEdge{
200✔
97
        from_node_id: exit_id,
98
        to_node_id: assign_id,
99
        type: :sequential,
100
        condition: nil,
101
        probability: 1.0,
102
        metadata: %{}
103
      }
104
    end)
105

106
    all_nodes = Map.put(final_expr_nodes, assign_id, assign_node)
200✔
107
    all_edges = final_expr_edges ++ expr_to_assign_edges
200✔
108

109
    {all_nodes, all_edges, [assign_id], final_expr_scopes, final_expr_state}
200✔
110
  end
111

112
  @doc """
113
  Processes a pipe operation.
114
  `ast_processor_func` is `ASTProcessor.process_ast_node/2`.
115
  """
116
  def handle_pipe_operation(left, right, meta, state, ast_processor_func) do
117
    line = Utils.get_line_number(meta)
1,202✔
118
    {pipe_id, updated_state} = Utils.generate_node_id("pipe", state)
1,202✔
119

120
    {left_nodes, left_edges, left_exits, left_scopes, left_state} =
1,202✔
121
      ast_processor_func.(left, updated_state)
122

123
    {right_nodes, right_edges, right_exits, right_scopes, right_state} =
1,202✔
124
      ast_processor_func.(right, left_state)
125

126
    pipe_node = %CFGNode{
1,202✔
127
      id: pipe_id,
128
      type: :pipe_operation,
129
      ast_node_id: Utils.get_ast_node_id(meta),
130
      line: line,
131
      scope_id: state.current_scope,
1,202✔
132
      expression: {:|>, meta, [left, right]},
133
      predecessors: left_exits, # Corrected: pipe node input from left exits
134
      successors: [], # Corrected: pipe node output to right entries (handled by edges below)
135
      metadata: %{left: left, right: right}
136
    }
137

138
    left_to_pipe_edges = Enum.map(left_exits, fn exit_id ->
1,202✔
139
      %CFGEdge{
1,202✔
140
        from_node_id: exit_id,
141
        to_node_id: pipe_id,
142
        type: :sequential,
143
        condition: nil,
144
        probability: 1.0,
145
        metadata: %{pipe_stage: :input}
146
      }
147
    end)
148

149
    # Connect pipe node to the entry points of the right-hand side processing
150
    right_entry_nodes = Utils.get_entry_nodes(right_nodes)
1,202✔
151
    pipe_to_right_edges = Enum.map(right_entry_nodes, fn entry_id ->
1,202✔
152
      %CFGEdge{
1,202✔
153
        from_node_id: pipe_id,
154
        to_node_id: entry_id,
155
        type: :sequential,
156
        condition: nil,
157
        probability: 1.0,
158
        metadata: %{pipe_stage: :output}
159
      }
160
    end)
161

162
    all_nodes = left_nodes
1,202✔
163
    |> Map.merge(right_nodes)
164
    |> Map.put(pipe_id, pipe_node)
165

166
    all_edges = left_edges ++ right_edges ++ left_to_pipe_edges ++ pipe_to_right_edges
1,202✔
167
    all_scopes = Map.merge(left_scopes, right_scopes)
1,202✔
168

169
    # The exits of the pipe operation are the exits of the right-hand side
170
    {all_nodes, all_edges, right_exits, all_scopes, right_state}
1,202✔
171
  end
172
end 
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc