steep 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -1
- data/lib/steep/ast/types/proc.rb +1 -1
- data/lib/steep/cli.rb +25 -4
- data/lib/steep/drivers/annotations.rb +14 -3
- data/lib/steep/drivers/check.rb +71 -75
- data/lib/steep/drivers/print_interface.rb +56 -25
- data/lib/steep/drivers/scaffold.rb +7 -4
- data/lib/steep/drivers/utils/each_signature.rb +6 -41
- data/lib/steep/drivers/validate.rb +35 -10
- data/lib/steep/drivers/watch.rb +188 -0
- data/lib/steep/errors.rb +2 -2
- data/lib/steep/project/file.rb +119 -0
- data/lib/steep/project/listener.rb +53 -0
- data/lib/steep/project/options.rb +13 -0
- data/lib/steep/project.rb +231 -0
- data/lib/steep/type_construction.rb +56 -29
- data/lib/steep/version.rb +1 -1
- data/lib/steep.rb +7 -1
- data/sig/project.rbi +106 -0
- data/smoke/hash/f.rb +11 -0
- data/stdlib/builtin.rbi +3 -0
- data/steep.gemspec +3 -1
- metadata +39 -7
- data/lib/steep/drivers/utils/validator.rb +0 -110
- data/sig/signature.rbi +0 -146
- data/sig/types.rbi +0 -43
@@ -0,0 +1,188 @@
|
|
1
|
+
module Steep
|
2
|
+
module Drivers
|
3
|
+
class Watch
|
4
|
+
class Options
|
5
|
+
attr_accessor :fallback_any_is_error
|
6
|
+
attr_accessor :allow_missing_definitions
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
self.fallback_any_is_error = false
|
10
|
+
self.allow_missing_definitions = true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :source_dirs
|
15
|
+
attr_reader :signature_dirs
|
16
|
+
attr_reader :stdout
|
17
|
+
attr_reader :stderr
|
18
|
+
attr_reader :options
|
19
|
+
attr_reader :queue
|
20
|
+
|
21
|
+
include Utils::EachSignature
|
22
|
+
|
23
|
+
def initialize(source_dirs:, signature_dirs:, stdout:, stderr:)
|
24
|
+
@source_dirs = source_dirs
|
25
|
+
@signature_dirs = signature_dirs
|
26
|
+
@stdout = stdout
|
27
|
+
@stderr = stderr
|
28
|
+
@options = Options.new
|
29
|
+
@queue = Thread::Queue.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def project_options
|
33
|
+
Project::Options.new.tap do |opt|
|
34
|
+
opt.fallback_any_is_error = options.fallback_any_is_error
|
35
|
+
opt.allow_missing_definitions = options.allow_missing_definitions
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def source_listener
|
40
|
+
@source_listener ||= yield_self do
|
41
|
+
Listen.to(*source_dirs.map(&:to_s), only: /\.rb$/) do |modified, added, removed|
|
42
|
+
queue << [:source, modified, added, removed]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def signature_listener
|
48
|
+
@signature_listener ||= yield_self do
|
49
|
+
Listen.to(*signature_dirs.map(&:to_s), only: /\.rbi$/) do |modified, added, removed|
|
50
|
+
queue << [:signature, modified, added, removed]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def type_check_thread(project)
|
56
|
+
Thread.new do
|
57
|
+
until queue.closed?
|
58
|
+
begin
|
59
|
+
events = []
|
60
|
+
events << queue.deq
|
61
|
+
until queue.empty?
|
62
|
+
events << queue.deq(nonblock: true)
|
63
|
+
end
|
64
|
+
|
65
|
+
events.compact.each do |name, modified, added, removed|
|
66
|
+
case name
|
67
|
+
when :source
|
68
|
+
(modified + added).each do |name|
|
69
|
+
path = Pathname(name).relative_path_from(Pathname.pwd)
|
70
|
+
file = project.source_files[path] || Project::SourceFile.new(path: path, options: project_options)
|
71
|
+
file.content = path.read
|
72
|
+
project.source_files[path] = file
|
73
|
+
end
|
74
|
+
|
75
|
+
removed.each do |name|
|
76
|
+
path = Pathname(name).relative_path_from(Pathname.pwd)
|
77
|
+
project.source_files.delete(path)
|
78
|
+
end
|
79
|
+
|
80
|
+
when :signature
|
81
|
+
(modified + added).each do |name|
|
82
|
+
path = Pathname(name).relative_path_from(Pathname.pwd)
|
83
|
+
file = project.signature_files[path] || Project::SignatureFile.new(path: path)
|
84
|
+
file.content = path.read
|
85
|
+
project.signature_files[path] = file
|
86
|
+
end
|
87
|
+
|
88
|
+
removed.each do |name|
|
89
|
+
path = Pathname(name).relative_path_from(Pathname.pwd)
|
90
|
+
project.signature_files.delete(path)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
begin
|
96
|
+
project.type_check
|
97
|
+
rescue Racc::ParseError => exn
|
98
|
+
stderr.puts exn.message
|
99
|
+
project.clear
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
rescue ClosedQueueError
|
104
|
+
# nop
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class WatchListener < Project::NullListener
|
109
|
+
attr_reader :stdout
|
110
|
+
attr_reader :stderr
|
111
|
+
|
112
|
+
def initialize(stdout:, stderr:, verbose:)
|
113
|
+
@stdout = stdout
|
114
|
+
@stderr = stderr
|
115
|
+
end
|
116
|
+
|
117
|
+
def check(project:)
|
118
|
+
yield.tap do
|
119
|
+
if project.success?
|
120
|
+
if project.has_type_error?
|
121
|
+
stdout.puts "Detected #{project.errors.size} errors... 🔥"
|
122
|
+
else
|
123
|
+
stdout.puts "No error detected. 🎉"
|
124
|
+
end
|
125
|
+
else
|
126
|
+
stdout.puts "Type checking failed... 🔥"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def type_check_source(project:, file:)
|
132
|
+
yield.tap do
|
133
|
+
case
|
134
|
+
when file.source.is_a?(Source) && file.errors
|
135
|
+
file.errors.each do |error|
|
136
|
+
error.print_to stdout
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def load_signature(project:)
|
143
|
+
# @type var project: Project
|
144
|
+
yield.tap do
|
145
|
+
case sig = project.signature
|
146
|
+
when Project::SignatureHasError
|
147
|
+
when Project::SignatureHasSyntaxError
|
148
|
+
sig.errors.each do |path, exn|
|
149
|
+
stdout.puts "#{path} has a syntax error: #{exn.inspect}"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def run(block: true)
|
157
|
+
project = Project.new(WatchListener.new(stdout: stdout, stderr: stderr, verbose: false))
|
158
|
+
|
159
|
+
source_dirs.each do |path|
|
160
|
+
each_file_in_path(".rb", path) do |file_path|
|
161
|
+
file = Project::SourceFile.new(path: file_path, options: options)
|
162
|
+
file.content = file_path.read
|
163
|
+
project.source_files[file_path] = file
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
signature_dirs.each do |path|
|
168
|
+
each_file_in_path(".rbi", path) do |file_path|
|
169
|
+
file = Project::SignatureFile.new(path: file_path)
|
170
|
+
file.content = file_path.read
|
171
|
+
project.signature_files[file_path] = file
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
project.type_check
|
176
|
+
|
177
|
+
source_listener.start
|
178
|
+
signature_listener.start
|
179
|
+
t = type_check_thread(project)
|
180
|
+
|
181
|
+
binding.pry(quiet: true) if block
|
182
|
+
|
183
|
+
queue.close
|
184
|
+
t.join
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
data/lib/steep/errors.rb
CHANGED
@@ -518,7 +518,7 @@ module Steep
|
|
518
518
|
end
|
519
519
|
|
520
520
|
def to_s
|
521
|
-
"#{location_to_str}: UnexpectedKeyword: #{unexpected_keywords.join(", ")}"
|
521
|
+
"#{location_to_str}: UnexpectedKeyword: #{unexpected_keywords.to_a.join(", ")}"
|
522
522
|
end
|
523
523
|
end
|
524
524
|
|
@@ -531,7 +531,7 @@ module Steep
|
|
531
531
|
end
|
532
532
|
|
533
533
|
def to_s
|
534
|
-
"#{location_to_str}: MissingKeyword: #{missing_keywords.join(", ")}"
|
534
|
+
"#{location_to_str}: MissingKeyword: #{missing_keywords.to_a.join(", ")}"
|
535
535
|
end
|
536
536
|
end
|
537
537
|
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module Steep
|
2
|
+
class Project
|
3
|
+
class SourceFile
|
4
|
+
attr_reader :options
|
5
|
+
attr_reader :path
|
6
|
+
attr_reader :content
|
7
|
+
attr_reader :content_updated_at
|
8
|
+
|
9
|
+
attr_reader :source
|
10
|
+
attr_reader :typing
|
11
|
+
attr_reader :last_type_checked_at
|
12
|
+
|
13
|
+
def initialize(path:, options:)
|
14
|
+
@path = path
|
15
|
+
@options = options
|
16
|
+
self.content = ""
|
17
|
+
end
|
18
|
+
|
19
|
+
def content=(content)
|
20
|
+
@content_updated_at = Time.now
|
21
|
+
@content = content
|
22
|
+
end
|
23
|
+
|
24
|
+
def requires_type_check?
|
25
|
+
if last = last_type_checked_at
|
26
|
+
last < content_updated_at
|
27
|
+
else
|
28
|
+
true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def invalidate
|
33
|
+
@source = nil
|
34
|
+
@typing = nil
|
35
|
+
@last_type_checked_at = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def parse
|
39
|
+
_ = @source =
|
40
|
+
begin
|
41
|
+
Source.parse(content, path: path.to_s, labeling: ASTUtils::Labeling.new)
|
42
|
+
rescue ::Parser::SyntaxError => exn
|
43
|
+
Steep.logger.warn { "Syntax error on #{path}: #{exn.inspect}" }
|
44
|
+
exn
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def errors
|
49
|
+
typing&.errors&.reject do |error|
|
50
|
+
case
|
51
|
+
when error.is_a?(Errors::FallbackAny)
|
52
|
+
!options.fallback_any_is_error
|
53
|
+
when error.is_a?(Errors::MethodDefinitionMissing)
|
54
|
+
options.allow_missing_definitions
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def type_check(check)
|
60
|
+
case source = self.source
|
61
|
+
when Source
|
62
|
+
@typing = Typing.new
|
63
|
+
|
64
|
+
annotations = source.annotations(block: source.node, builder: check.builder, current_module: AST::Namespace.root)
|
65
|
+
|
66
|
+
const_env = TypeInference::ConstantEnv.new(builder: check.builder, context: nil)
|
67
|
+
type_env = TypeInference::TypeEnv.build(annotations: annotations,
|
68
|
+
subtyping: check,
|
69
|
+
const_env: const_env,
|
70
|
+
signatures: check.builder.signatures)
|
71
|
+
|
72
|
+
construction = TypeConstruction.new(
|
73
|
+
checker: check,
|
74
|
+
annotations: annotations,
|
75
|
+
source: source,
|
76
|
+
self_type: AST::Builtin::Object.instance_type,
|
77
|
+
block_context: nil,
|
78
|
+
module_context: TypeConstruction::ModuleContext.new(
|
79
|
+
instance_type: nil,
|
80
|
+
module_type: nil,
|
81
|
+
implement_name: nil,
|
82
|
+
current_namespace: AST::Namespace.root,
|
83
|
+
const_env: const_env,
|
84
|
+
class_name: nil
|
85
|
+
),
|
86
|
+
method_context: nil,
|
87
|
+
typing: typing,
|
88
|
+
break_context: nil,
|
89
|
+
type_env: type_env
|
90
|
+
)
|
91
|
+
|
92
|
+
construction.synthesize(source.node)
|
93
|
+
|
94
|
+
@last_type_checked_at = Time.now
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class SignatureFile
|
100
|
+
attr_reader :path
|
101
|
+
attr_reader :content
|
102
|
+
attr_reader :content_updated_at
|
103
|
+
|
104
|
+
def initialize(path:)
|
105
|
+
@path = path
|
106
|
+
self.content = ""
|
107
|
+
end
|
108
|
+
|
109
|
+
def parse
|
110
|
+
Parser.parse_signature(content, name: path)
|
111
|
+
end
|
112
|
+
|
113
|
+
def content=(content)
|
114
|
+
@content_updated_at = Time.now
|
115
|
+
@content = content
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Steep
|
2
|
+
class Project
|
3
|
+
class NullListener
|
4
|
+
def parse_signature(project:, file:)
|
5
|
+
yield
|
6
|
+
end
|
7
|
+
|
8
|
+
def parse_source(project:, file:)
|
9
|
+
yield
|
10
|
+
end
|
11
|
+
|
12
|
+
def check(project:)
|
13
|
+
yield
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate_signature(project:)
|
17
|
+
yield
|
18
|
+
end
|
19
|
+
|
20
|
+
def type_check_source(project:, file:)
|
21
|
+
yield
|
22
|
+
end
|
23
|
+
|
24
|
+
def clear_project(project:)
|
25
|
+
yield
|
26
|
+
end
|
27
|
+
|
28
|
+
def load_signature(project:)
|
29
|
+
yield
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class SyntaxErrorRaisingListener < NullListener
|
34
|
+
def load_signature(project:)
|
35
|
+
yield.tap do
|
36
|
+
case signature = project.signature
|
37
|
+
when SignatureHasSyntaxError
|
38
|
+
raise signature.errors.values[0]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def parse_source(project:, file:)
|
44
|
+
yield.tap do
|
45
|
+
case source = file.source
|
46
|
+
when ::Parser::SyntaxError
|
47
|
+
raise source
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,231 @@
|
|
1
|
+
module Steep
|
2
|
+
class Project
|
3
|
+
class SignatureLoaded
|
4
|
+
attr_reader :check
|
5
|
+
attr_reader :loaded_at
|
6
|
+
attr_reader :file_paths
|
7
|
+
|
8
|
+
def initialize(check:, loaded_at:, file_paths:)
|
9
|
+
@check = check
|
10
|
+
@loaded_at = loaded_at
|
11
|
+
@file_paths = file_paths
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class SignatureHasSyntaxError
|
16
|
+
attr_reader :errors
|
17
|
+
|
18
|
+
def initialize(errors:)
|
19
|
+
@errors = errors
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class SignatureHasError
|
24
|
+
attr_reader :errors
|
25
|
+
|
26
|
+
def initialize(errors:)
|
27
|
+
@errors = errors
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_reader :source_files
|
32
|
+
attr_reader :signature_files
|
33
|
+
attr_reader :listener
|
34
|
+
|
35
|
+
attr_reader :signature
|
36
|
+
|
37
|
+
def initialize(listener = nil)
|
38
|
+
@listener = listener || NullListener.new
|
39
|
+
@source_files = {}
|
40
|
+
@signature_files = {}
|
41
|
+
end
|
42
|
+
|
43
|
+
def clear
|
44
|
+
listener.clear_project project: self do
|
45
|
+
@signature = nil
|
46
|
+
source_files.each_value do |file|
|
47
|
+
file.invalidate
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def type_check(force_signatures: false, force_sources: false)
|
53
|
+
listener.check(project: self) do
|
54
|
+
should_reload_signature = force_signatures || signature_updated?
|
55
|
+
reload_signature if should_reload_signature
|
56
|
+
|
57
|
+
case sig = signature
|
58
|
+
when SignatureLoaded
|
59
|
+
each_updated_source(force: force_sources || should_reload_signature) do |file|
|
60
|
+
file.invalidate
|
61
|
+
|
62
|
+
listener.parse_source(project: self, file: file) do
|
63
|
+
file.parse()
|
64
|
+
end
|
65
|
+
|
66
|
+
listener.type_check_source(project: self, file: file) do
|
67
|
+
file.type_check(sig.check)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def success?
|
75
|
+
signature.is_a?(SignatureLoaded) &&
|
76
|
+
source_files.all? {|_, file| file.source.is_a?(Source) && file.typing }
|
77
|
+
end
|
78
|
+
|
79
|
+
def has_type_error?
|
80
|
+
source_files.any? do |_, file|
|
81
|
+
file.errors&.any?
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def errors
|
86
|
+
source_files.flat_map do |_, file|
|
87
|
+
file.errors || []
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# @type method each_updated_source: (?force: bool) ?{ (SourceFile) -> any } -> any
|
92
|
+
def each_updated_source(force: false)
|
93
|
+
if block_given?
|
94
|
+
source_files.each_value do |file|
|
95
|
+
if force || file.requires_type_check?
|
96
|
+
yield file
|
97
|
+
end
|
98
|
+
end
|
99
|
+
else
|
100
|
+
enum_for :each_updated_source, force: force
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def signature_updated?
|
105
|
+
case sig = signature
|
106
|
+
when SignatureLoaded
|
107
|
+
signature_files.keys != sig.file_paths ||
|
108
|
+
signature_files.any? {|_, file| file.content_updated_at >= sig.loaded_at }
|
109
|
+
else
|
110
|
+
true
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def reload_signature
|
115
|
+
@signature = nil
|
116
|
+
|
117
|
+
env = AST::Signature::Env.new
|
118
|
+
builder = Interface::Builder.new(signatures: env)
|
119
|
+
check = Subtyping::Check.new(builder: builder)
|
120
|
+
|
121
|
+
# @type var syntax_errors: Hash<Pathname, any>
|
122
|
+
syntax_errors = {}
|
123
|
+
|
124
|
+
listener.load_signature(project: self) do
|
125
|
+
signature_files.each_value do |file|
|
126
|
+
sigs = listener.parse_signature(project: self, file: file) do
|
127
|
+
file.parse
|
128
|
+
end
|
129
|
+
|
130
|
+
sigs.each do |sig|
|
131
|
+
env.add sig
|
132
|
+
end
|
133
|
+
rescue Racc::ParseError => exn
|
134
|
+
Steep.logger.warn { "Syntax error on #{file.path}: #{exn.inspect}" }
|
135
|
+
syntax_errors[file.path] = exn
|
136
|
+
end
|
137
|
+
|
138
|
+
if syntax_errors.empty?
|
139
|
+
listener.validate_signature(project: self) do
|
140
|
+
errors = validate_signature(check)
|
141
|
+
@signature = if errors.empty?
|
142
|
+
SignatureLoaded.new(check: check, loaded_at: Time.now, file_paths: signature_files.keys)
|
143
|
+
else
|
144
|
+
SignatureHasError.new(errors: errors)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
else
|
148
|
+
@signature = SignatureHasSyntaxError.new(errors: syntax_errors)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def validate_signature(check)
|
154
|
+
errors = []
|
155
|
+
|
156
|
+
builder = check.builder
|
157
|
+
|
158
|
+
check.builder.signatures.each do |sig|
|
159
|
+
Steep.logger.debug { "Validating signature: #{sig.inspect}" }
|
160
|
+
|
161
|
+
case sig
|
162
|
+
when AST::Signature::Interface
|
163
|
+
yield_self do
|
164
|
+
instance_interface = builder.build_interface(sig.name)
|
165
|
+
|
166
|
+
args = instance_interface.params.map {|var| AST::Types::Var.fresh(var) }
|
167
|
+
instance_type = AST::Types::Name::Interface.new(name: sig.name, args: args)
|
168
|
+
|
169
|
+
instance_interface.instantiate(type: instance_type,
|
170
|
+
args: args,
|
171
|
+
instance_type: instance_type,
|
172
|
+
module_type: nil).validate(check)
|
173
|
+
end
|
174
|
+
|
175
|
+
when AST::Signature::Module
|
176
|
+
yield_self do
|
177
|
+
instance_interface = builder.build_instance(sig.name)
|
178
|
+
instance_args = instance_interface.params.map {|var| AST::Types::Var.fresh(var) }
|
179
|
+
|
180
|
+
module_interface = builder.build_module(sig.name)
|
181
|
+
module_args = module_interface.params.map {|var| AST::Types::Var.fresh(var) }
|
182
|
+
|
183
|
+
instance_type = AST::Types::Name::Instance.new(name: sig.name, args: instance_args)
|
184
|
+
module_type = AST::Types::Name::Module.new(name: sig.name)
|
185
|
+
|
186
|
+
Steep.logger.debug { "Validating instance methods..." }
|
187
|
+
instance_interface.instantiate(type: instance_type,
|
188
|
+
args: instance_args,
|
189
|
+
instance_type: instance_type,
|
190
|
+
module_type: module_type).validate(check)
|
191
|
+
|
192
|
+
Steep.logger.debug { "Validating class methods..." }
|
193
|
+
module_interface.instantiate(type: module_type,
|
194
|
+
args: module_args,
|
195
|
+
instance_type: instance_type,
|
196
|
+
module_type: module_type).validate(check)
|
197
|
+
end
|
198
|
+
|
199
|
+
when AST::Signature::Class
|
200
|
+
yield_self do
|
201
|
+
instance_interface = builder.build_instance(sig.name)
|
202
|
+
instance_args = instance_interface.params.map {|var| AST::Types::Var.fresh(var) }
|
203
|
+
|
204
|
+
module_interface = builder.build_class(sig.name, constructor: true)
|
205
|
+
module_args = module_interface.params.map {|var| AST::Types::Var.fresh(var) }
|
206
|
+
|
207
|
+
instance_type = AST::Types::Name::Instance.new(name: sig.name, args: instance_args)
|
208
|
+
module_type = AST::Types::Name::Class.new(name: sig.name, constructor: true)
|
209
|
+
|
210
|
+
Steep.logger.debug { "Validating instance methods..." }
|
211
|
+
instance_interface.instantiate(type: instance_type,
|
212
|
+
args: instance_args,
|
213
|
+
instance_type: instance_type,
|
214
|
+
module_type: module_type).validate(check)
|
215
|
+
|
216
|
+
Steep.logger.debug { "Validating class methods..." }
|
217
|
+
module_interface.instantiate(type: module_type,
|
218
|
+
args: module_args,
|
219
|
+
instance_type: instance_type,
|
220
|
+
module_type: module_type).validate(check)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
rescue => exn
|
225
|
+
errors << exn
|
226
|
+
end
|
227
|
+
|
228
|
+
errors
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|