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

nshkrdotcom / ElixirScope / f6aca62b40bb7398234ae7c2ebcd30112dae0f97

30 May 2025 02:04AM UTC coverage: 57.458% (-0.002%) from 57.46%
f6aca62b40bb7398234ae7c2ebcd30112dae0f97

push

github

NSHkr
refactor project_populator

185 of 249 new or added lines in 11 files covered. (74.3%)

3 existing lines in 2 files now uncovered.

6194 of 10780 relevant lines covered (57.46%)

3134.25 hits per line

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

77.14
/lib/elixir_scope/ast_repository/enhanced/project_populator.ex
1
defmodule ElixirScope.ASTRepository.Enhanced.ProjectPopulator do
2
  @moduledoc """
3
  Enhanced project populator for comprehensive AST analysis and repository population.
4

5
  Provides functionality to:
6
  - Discover and parse all Elixir files in a project
7
  - Generate enhanced module and function data with CFG/DFG/CPG analysis
8
  - Populate the enhanced repository with analyzed data
9
  - Track dependencies and build project-wide analysis
10
  - Support parallel processing for large projects
11
  """
12

13
  require Logger
14

15
  alias ElixirScope.ASTRepository.Enhanced.{
16
    Repository,
17
    EnhancedModuleData,
18
    EnhancedFunctionData,
19
    CFGGenerator,
20
    DFGGenerator,
21
    CPGBuilder
22
  }
23

24
  alias ElixirScope.ASTRepository.Enhanced.ProjectPopulator.{
25
    FileDiscovery,
26
    FileParser,
27
    ModuleAnalyzer,
28
    DependencyAnalyzer,
29
    PerformanceMetrics
30
  }
31

32
  @default_opts [
33
    include_patterns: ["**/*.ex", "**/*.exs"],
34
    exclude_patterns: ["**/deps/**", "**/build/**", "**/_build/**", "**/node_modules/**"],
35
    max_file_size: 1_000_000,  # 1MB
36
    parallel_processing: true,
37
    max_concurrency: System.schedulers_online(),
38
    timeout: 30_000,
39
    generate_cfg: true,
40
    generate_dfg: true,
41
    generate_cpg: false,  # CPG is expensive, opt-in
42
    validate_syntax: true,
43
    track_dependencies: true
44
  ]
45

46
  @doc """
47
  Populates the repository with all Elixir files in a project.
48

49
  Returns {:ok, results} or {:error, reason}
50
  """
51
  def populate_project(repo, project_path, opts \\ []) do
52
    opts = Keyword.merge(@default_opts, opts)
12✔
53

54
    Logger.info("Starting project population for: #{project_path}")
12✔
55
    start_time = System.monotonic_time(:microsecond)
12✔
56

57
    try do
12✔
58
      with {:ok, files} <- FileDiscovery.discover_elixir_files(project_path, opts),
12✔
59
           {:ok, parsed_files} <- FileParser.parse_files(files, opts),
11✔
60
           {:ok, analyzed_modules} <- ModuleAnalyzer.analyze_modules(parsed_files, opts),
11✔
61
           {:ok, dependency_graph} <- DependencyAnalyzer.build_dependency_graph(analyzed_modules, opts) do
11✔
62

63
        # Store modules in repository
64
        try do
11✔
65
          Enum.each(analyzed_modules, fn {_module_name, module_data} ->
11✔
66
            Repository.store_module(repo, module_data)
214✔
67

68
            # Store functions
69
            Enum.each(module_data.functions, fn {_key, function_data} ->
213✔
70
              Repository.store_function(repo, function_data)
1,249✔
71
            end)
72
          end)
73
        rescue
74
          e ->
×
75
            Logger.error("Failed to store modules in repository: #{Exception.message(e)}")
×
NEW
76
            throw({:repository_storage_failed, Exception.message(e)})
×
77
        catch
78
          :exit, reason ->
79
            Logger.error("Repository process exited during storage: #{inspect(reason)}")
1✔
80
            throw({:repository_unavailable, reason})
1✔
81
        end
82

83
        end_time = System.monotonic_time(:microsecond)
10✔
84
        duration = end_time - start_time
10✔
85

86
        results = %{
10✔
87
          project_path: project_path,
88
          files_discovered: length(files),
89
          files_parsed: length(parsed_files),
90
          modules: analyzed_modules,
91
          total_functions: count_total_functions(analyzed_modules),
92
          dependency_graph: dependency_graph,
93
          duration_microseconds: duration,
94
          performance_metrics: PerformanceMetrics.calculate_performance_metrics(parsed_files, analyzed_modules, duration)
95
        }
96

97
        Logger.info("Project population completed: #{length(analyzed_modules)} modules, #{results.total_functions} functions in #{duration / 1000}ms")
10✔
98
        {:ok, results}
99
      else
100
        {:error, reason} = error ->
101
          Logger.error("Project population failed: #{inspect(reason)}")
1✔
102
          error
1✔
103
      end
104
    rescue
UNCOV
105
      e ->
×
UNCOV
106
        Logger.error("Project population crashed: #{Exception.message(e)}")
×
107
        {:error, {:population_crashed, Exception.message(e)}}
108
    catch
NEW
109
      {:repository_storage_failed, message} ->
×
110
        {:error, {:repository_storage_failed, message}}
111
      {:repository_unavailable, reason} ->
1✔
112
        {:error, {:repository_unavailable, reason}}
113
    end
114
  end
115

116
  @doc """
117
  Parses and analyzes a single file (used by Synchronizer).
118
  """
119
  def parse_and_analyze_file(file_path) do
120
    try do
184✔
121
      with {:ok, parsed_file} <- FileParser.parse_single_file(file_path, true, 30_000),
184✔
122
           {:ok, {_module_name, module_data}} <- ModuleAnalyzer.analyze_single_module(parsed_file, true, true, false, 30_000) do
181✔
123
        {:ok, module_data}
124
      else
125
        {:error, reason} -> {:error, reason}
126
      end
127
    rescue
128
      e ->
×
129
        {:error, {:parse_and_analyze_failed, Exception.message(e)}}
130
    end
131
  end
132

133
  @doc """
134
  Discovers all Elixir files in a project directory.
135
  Delegates to FileDiscovery for backward compatibility.
136
  """
137
  def discover_elixir_files(project_path, opts \\ []) do
NEW
138
    FileDiscovery.discover_elixir_files(project_path, opts)
×
139
  end
140

141
  # Utility functions
142

143
  defp count_total_functions(analyzed_modules) do
144
    analyzed_modules
145
    |> Map.values()
146
    |> Enum.map(fn module -> map_size(module.functions) end)
213✔
147
    |> Enum.sum()
10✔
148
  end
149
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

© 2025 Coveralls, Inc