Module: Parlour::TypeLoader

Extended by:
T::Sig
Defined in:
lib/parlour/type_loader.rb

Class Method Summary collapse

Class Method Details

.load_file(filename, generator: nil) ⇒ RbiGenerator::Namespace

Converts Ruby source code into a tree of objects from a file.

Parameters:

  • filename (String)

    The name of the file to load code from.

Returns:



29
30
31
# File 'lib/parlour/type_loader.rb', line 29

def self.load_file(filename, generator: nil)
  load_source(File.read(filename), filename, generator: generator)
end

.load_project(root, inclusions: ['.'], exclusions: [], generator: nil) ⇒ RbiGenerator::Namespace

Loads an entire Sorbet project using Sorbet’s file table, obeying any “typed: ignore” sigils, into a tree of objects.

Files within sorbet/rbi/hidden-definitions are excluded, as they cause merging issues with abstract classes due to sorbet/sorbet#1653.

Parameters:

  • root (String)

    The root of the project; where the “sorbet” directory and “Gemfile” are located.

  • inclusions (Array<String>) (defaults to: ['.'])

    A list of files to include when loading the project, relative to the given root.

  • exclusions (Array<String>) (defaults to: [])

    A list of files to exclude when loading the project, relative to the given root.

Returns:



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/parlour/type_loader.rb', line 54

def self.load_project(root, inclusions: ['.'], exclusions: [], generator: nil)
  expanded_inclusions = inclusions.map { |i| File.expand_path(i, root) }
  expanded_exclusions = exclusions.map { |e| File.expand_path(e, root) }

  stdin, stdout, stderr, wait_thr = T.unsafe(Open3).popen3(
    'bundle exec srb tc -p file-table-json',
    chdir: root
  )

  stdout = T.must(stdout.read)
  if stdout == ''
    raise 'unable to get Sorbet file table; the project may be empty or not have Sorbet initialised'
  end

  file_table_hash = JSON.parse(stdout)
  file_table_entries = file_table_hash['files']

  namespaces = T.let([], T::Array[Parlour::RbiGenerator::Namespace])
  file_table_entries.each do |file_table_entry|
    next if file_table_entry['sigil'] == 'Ignore' ||
      file_table_entry['strict'] == 'Ignore'

    rel_path = file_table_entry['path']
    next if rel_path.start_with?('./sorbet/rbi/hidden-definitions/')
    path = File.expand_path(rel_path, root)

    # Skip this file if it was excluded
    next if !expanded_inclusions.any? { |i| path.start_with?(i) } \
      || expanded_exclusions.any? { |e| path.start_with?(e) }

    # There are some entries which are URLs to stdlib
    next unless File.exist?(path)

    namespaces << load_file(path, generator: generator)
  end

  namespaces.uniq!

  raise 'project is empty' if namespaces.empty?

  first_namespace, *other_namespaces = namespaces
  first_namespace = T.must(first_namespace)
  other_namespaces = T.must(other_namespaces)

  raise 'cannot merge namespaces loaded from a project' \
    unless first_namespace.mergeable?(other_namespaces)
  first_namespace.merge_into_self(other_namespaces)

  ConflictResolver.new.resolve_conflicts(first_namespace) do |n, o|
    raise "conflict of #{o.length} objects: #{n}"
  end

  first_namespace
end

.load_source(source, filename = nil, generator: nil) ⇒ RbiGenerator::Namespace

Converts Ruby source code into a tree of objects.

Parameters:

  • source (String)

    The Ruby source code.

  • filename (String, nil) (defaults to: nil)

    The filename to use when parsing this code. This may be used in error messages, but is optional.

Returns:



20
21
22
# File 'lib/parlour/type_loader.rb', line 20

def self.load_source(source, filename = nil, generator: nil)
  TypeParser.from_source(filename || '(source)', source, generator: generator).parse_all
end