steep 1.6.0 → 1.7.0.dev.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/ruby-windows.yml +2 -1
- data/.github/workflows/ruby.yml +9 -10
- data/.gitignore +0 -1
- data/Gemfile +1 -1
- data/Gemfile.lock +30 -28
- data/bin/output_test.rb +1 -0
- data/doc/narrowing.md +195 -0
- data/gemfile_steep/Gemfile.lock +30 -15
- data/guides/src/getting-started/getting-started.md +10 -11
- data/lib/steep/ast/ignore.rb +148 -0
- data/lib/steep/cli.rb +6 -1
- data/lib/steep/diagnostic/ruby.rb +16 -0
- data/lib/steep/drivers/utils/driver_helper.rb +22 -11
- data/lib/steep/node_helper.rb +12 -0
- data/lib/steep/project/dsl.rb +18 -21
- data/lib/steep/project/options.rb +39 -2
- data/lib/steep/project.rb +11 -7
- data/lib/steep/server/change_buffer.rb +2 -2
- data/lib/steep/server/interaction_worker.rb +61 -11
- data/lib/steep/server/worker_process.rb +3 -1
- data/lib/steep/services/completion_provider.rb +39 -8
- data/lib/steep/services/file_loader.rb +3 -2
- data/lib/steep/services/signature_help_provider.rb +9 -8
- data/lib/steep/services/type_check_service.rb +36 -8
- data/lib/steep/source/ignore_ranges.rb +69 -0
- data/lib/steep/source.rb +10 -4
- data/lib/steep/type_construction.rb +6 -147
- data/lib/steep/type_inference/case_when.rb +301 -0
- data/lib/steep/version.rb +1 -1
- data/lib/steep.rb +14 -1
- data/rbs_collection.steep.lock.yaml +8 -6
- data/sig/shims/parser.rbs +10 -0
- data/sig/shims/yaml.rbs +4 -0
- data/sig/steep/ast/ignore.rbs +66 -0
- data/sig/steep/diagnostic/ruby.rbs +8 -0
- data/sig/steep/node_helper.rbs +2 -0
- data/sig/steep/project/dsl.rbs +8 -4
- data/sig/steep/project/options.rbs +21 -1
- data/sig/steep/project.rbs +3 -3
- data/sig/steep/server/change_buffer.rbs +6 -4
- data/sig/steep/server/interaction_worker.rbs +10 -0
- data/sig/steep/server/worker_process.rbs +4 -4
- data/sig/steep/services/completion_provider.rbs +14 -1
- data/sig/steep/services/type_check_service.rbs +6 -1
- data/sig/steep/source/ignore_ranges.rbs +38 -0
- data/sig/steep/source.rbs +5 -2
- data/sig/steep/subtyping/check.rbs +2 -2
- data/sig/steep/type_construction.rbs +1 -26
- data/sig/steep/type_inference/branch.rbs +15 -0
- data/sig/steep/type_inference/case_when.rbs +130 -0
- data/sig/steep.rbs +9 -0
- data/steep.gemspec +1 -1
- metadata +12 -258
- data/smoke/alias/Steepfile +0 -6
- data/smoke/alias/a.rb +0 -16
- data/smoke/alias/a.rbs +0 -10
- data/smoke/alias/b.rb +0 -6
- data/smoke/alias/c.rb +0 -8
- data/smoke/alias/test_expectations.yml +0 -96
- data/smoke/and/Steepfile +0 -6
- data/smoke/and/a.rb +0 -8
- data/smoke/and/test_expectations.yml +0 -29
- data/smoke/array/Steepfile +0 -6
- data/smoke/array/a.rb +0 -18
- data/smoke/array/b.rb +0 -12
- data/smoke/array/c.rb +0 -6
- data/smoke/array/test_expectations.yml +0 -103
- data/smoke/block/Steepfile +0 -6
- data/smoke/block/a.rb +0 -10
- data/smoke/block/a.rbs +0 -6
- data/smoke/block/b.rb +0 -13
- data/smoke/block/c.rb +0 -9
- data/smoke/block/c.rbs +0 -3
- data/smoke/block/d.rb +0 -11
- data/smoke/block/e.rb +0 -12
- data/smoke/block/e.rbs +0 -4
- data/smoke/block/test_expectations.yml +0 -133
- data/smoke/case/Steepfile +0 -6
- data/smoke/case/a.rb +0 -18
- data/smoke/case/test_expectations.yml +0 -47
- data/smoke/class/Steepfile +0 -6
- data/smoke/class/a.rb +0 -25
- data/smoke/class/a.rbs +0 -23
- data/smoke/class/b.rb +0 -5
- data/smoke/class/c.rb +0 -9
- data/smoke/class/f.rb +0 -10
- data/smoke/class/g.rb +0 -6
- data/smoke/class/h.rb +0 -19
- data/smoke/class/h.rbs +0 -6
- data/smoke/class/i.rb +0 -14
- data/smoke/class/i.rbs +0 -9
- data/smoke/class/test_expectations.yml +0 -117
- data/smoke/compact/Steepfile +0 -6
- data/smoke/compact/a.rb +0 -2
- data/smoke/compact/a.rbs +0 -5
- data/smoke/compact/b.rb +0 -2
- data/smoke/compact/test_expectations.yml +0 -18
- data/smoke/const/Steepfile +0 -6
- data/smoke/const/a.rb +0 -27
- data/smoke/const/b.rb +0 -7
- data/smoke/const/b.rbs +0 -5
- data/smoke/const/test_expectations.yml +0 -134
- data/smoke/diagnostics/Steepfile +0 -6
- data/smoke/diagnostics/a.rbs +0 -22
- data/smoke/diagnostics/argument_type_mismatch.rb +0 -1
- data/smoke/diagnostics/block_body_type_mismatch.rb +0 -1
- data/smoke/diagnostics/block_type_mismatch.rb +0 -3
- data/smoke/diagnostics/break_type_mismatch.rb +0 -1
- data/smoke/diagnostics/different_method_parameter_kind.rb +0 -9
- data/smoke/diagnostics/else_on_exhaustive_case.rb +0 -12
- data/smoke/diagnostics/incompatible_annotation.rb +0 -6
- data/smoke/diagnostics/incompatible_argument.rb +0 -1
- data/smoke/diagnostics/incompatible_assignment.rb +0 -8
- data/smoke/diagnostics/method_arity_mismatch.rb +0 -11
- data/smoke/diagnostics/method_body_type_mismatch.rb +0 -6
- data/smoke/diagnostics/method_definition_missing.rb +0 -2
- data/smoke/diagnostics/method_parameter_mismatch.rb +0 -10
- data/smoke/diagnostics/method_return_type_annotation_mismatch.rb +0 -7
- data/smoke/diagnostics/missing_keyword.rb +0 -1
- data/smoke/diagnostics/no_method.rb +0 -1
- data/smoke/diagnostics/proc_type_expected.rb +0 -3
- data/smoke/diagnostics/required_block_missing.rb +0 -1
- data/smoke/diagnostics/return_type_mismatch.rb +0 -6
- data/smoke/diagnostics/test_expectations.yml +0 -591
- data/smoke/diagnostics/unexpected_block_given.rb +0 -1
- data/smoke/diagnostics/unexpected_dynamic_method.rb +0 -3
- data/smoke/diagnostics/unexpected_jump.rb +0 -4
- data/smoke/diagnostics/unexpected_jump_value.rb +0 -3
- data/smoke/diagnostics/unexpected_keyword.rb +0 -1
- data/smoke/diagnostics/unexpected_splat.rb +0 -1
- data/smoke/diagnostics/unexpected_yield.rb +0 -6
- data/smoke/diagnostics/unknown_constant_assigned.rb +0 -7
- data/smoke/diagnostics/unresolved_overloading.rb +0 -1
- data/smoke/diagnostics/unsupported_syntax.rb +0 -2
- data/smoke/diagnostics-rbs/Steepfile +0 -8
- data/smoke/diagnostics-rbs/duplicated-method-definition.rbs +0 -20
- data/smoke/diagnostics-rbs/generic-parameter-mismatch.rbs +0 -7
- data/smoke/diagnostics-rbs/inherit-module.rbs +0 -2
- data/smoke/diagnostics-rbs/invalid-method-overload.rbs +0 -3
- data/smoke/diagnostics-rbs/invalid-type-application.rbs +0 -7
- data/smoke/diagnostics-rbs/invalid_variance_annotation.rbs +0 -3
- data/smoke/diagnostics-rbs/mixin-class-error.rbs +0 -6
- data/smoke/diagnostics-rbs/nonregular-type-alias.rbs +0 -3
- data/smoke/diagnostics-rbs/recursive-alias.rbs +0 -5
- data/smoke/diagnostics-rbs/recursive-class.rbs +0 -8
- data/smoke/diagnostics-rbs/recursive-type-alias.rbs +0 -3
- data/smoke/diagnostics-rbs/superclass-mismatch.rbs +0 -7
- data/smoke/diagnostics-rbs/test_expectations.yml +0 -300
- data/smoke/diagnostics-rbs/unknown-method-alias.rbs +0 -3
- data/smoke/diagnostics-rbs/unknown-type-name-2.rbs +0 -5
- data/smoke/diagnostics-rbs/unknown-type-name.rbs +0 -13
- data/smoke/diagnostics-rbs-duplicated/Steepfile +0 -6
- data/smoke/diagnostics-rbs-duplicated/a.rbs +0 -5
- data/smoke/diagnostics-rbs-duplicated/test_expectations.yml +0 -13
- data/smoke/diagnostics-ruby-unsat/Steepfile +0 -6
- data/smoke/diagnostics-ruby-unsat/a.rbs +0 -3
- data/smoke/diagnostics-ruby-unsat/test_expectations.yml +0 -27
- data/smoke/diagnostics-ruby-unsat/unsatisfiable_constraint.rb +0 -6
- data/smoke/dstr/Steepfile +0 -6
- data/smoke/dstr/a.rb +0 -5
- data/smoke/dstr/test_expectations.yml +0 -13
- data/smoke/ensure/Steepfile +0 -6
- data/smoke/ensure/a.rb +0 -18
- data/smoke/ensure/test_expectations.yml +0 -62
- data/smoke/enumerator/Steepfile +0 -6
- data/smoke/enumerator/a.rb +0 -6
- data/smoke/enumerator/b.rb +0 -17
- data/smoke/enumerator/test_expectations.yml +0 -47
- data/smoke/extension/Steepfile +0 -6
- data/smoke/extension/a.rb +0 -10
- data/smoke/extension/a.rbs +0 -13
- data/smoke/extension/b.rb +0 -10
- data/smoke/extension/c.rb +0 -9
- data/smoke/extension/d.rb +0 -2
- data/smoke/extension/e.rb +0 -2
- data/smoke/extension/e.rbs +0 -7
- data/smoke/extension/f.rb +0 -2
- data/smoke/extension/f.rbs +0 -3
- data/smoke/extension/test_expectations.yml +0 -73
- data/smoke/hash/Steepfile +0 -6
- data/smoke/hash/a.rb +0 -17
- data/smoke/hash/a.rbs +0 -8
- data/smoke/hash/b.rb +0 -6
- data/smoke/hash/c.rb +0 -15
- data/smoke/hash/d.rb +0 -5
- data/smoke/hash/e.rb +0 -1
- data/smoke/hash/e.rbs +0 -3
- data/smoke/hash/f.rb +0 -11
- data/smoke/hash/test_expectations.yml +0 -81
- data/smoke/hello/Steepfile +0 -6
- data/smoke/hello/hello.rb +0 -11
- data/smoke/hello/hello.rbs +0 -7
- data/smoke/hello/test_expectations.yml +0 -25
- data/smoke/if/Steepfile +0 -6
- data/smoke/if/a.rb +0 -20
- data/smoke/if/test_expectations.yml +0 -34
- data/smoke/implements/Steepfile +0 -6
- data/smoke/implements/a.rb +0 -12
- data/smoke/implements/a.rbs +0 -6
- data/smoke/implements/b.rb +0 -13
- data/smoke/implements/b.rbs +0 -12
- data/smoke/implements/test_expectations.yml +0 -23
- data/smoke/initialize/Steepfile +0 -6
- data/smoke/initialize/a.rb +0 -12
- data/smoke/initialize/a.rbs +0 -3
- data/smoke/initialize/test_expectations.yml +0 -1
- data/smoke/integer/Steepfile +0 -6
- data/smoke/integer/a.rb +0 -26
- data/smoke/integer/test_expectations.yml +0 -110
- data/smoke/interface/Steepfile +0 -6
- data/smoke/interface/a.rb +0 -12
- data/smoke/interface/a.rbs +0 -12
- data/smoke/interface/test_expectations.yml +0 -23
- data/smoke/kwbegin/Steepfile +0 -6
- data/smoke/kwbegin/a.rb +0 -7
- data/smoke/kwbegin/test_expectations.yml +0 -17
- data/smoke/lambda/Steepfile +0 -6
- data/smoke/lambda/a.rb +0 -10
- data/smoke/lambda/test_expectations.yml +0 -17
- data/smoke/literal/Steepfile +0 -6
- data/smoke/literal/a.rb +0 -11
- data/smoke/literal/b.rb +0 -7
- data/smoke/literal/literal_methods.rbs +0 -4
- data/smoke/literal/test_expectations.yml +0 -106
- data/smoke/map/Steepfile +0 -6
- data/smoke/map/a.rb +0 -5
- data/smoke/map/test_expectations.yml +0 -1
- data/smoke/method/Steepfile +0 -6
- data/smoke/method/a.rb +0 -21
- data/smoke/method/a.rbs +0 -4
- data/smoke/method/b.rb +0 -25
- data/smoke/method/c.rb +0 -5
- data/smoke/method/d.rb +0 -1
- data/smoke/method/d.rbs +0 -3
- data/smoke/method/test_expectations.yml +0 -121
- data/smoke/module/Steepfile +0 -6
- data/smoke/module/a.rb +0 -19
- data/smoke/module/a.rbs +0 -16
- data/smoke/module/b.rb +0 -6
- data/smoke/module/c.rb +0 -22
- data/smoke/module/d.rb +0 -4
- data/smoke/module/e.rb +0 -13
- data/smoke/module/f.rb +0 -11
- data/smoke/module/test_expectations.yml +0 -75
- data/smoke/regexp/Steepfile +0 -6
- data/smoke/regexp/a.rb +0 -109
- data/smoke/regexp/b.rb +0 -79
- data/smoke/regexp/test_expectations.yml +0 -615
- data/smoke/regression/Steepfile +0 -6
- data/smoke/regression/array.rb +0 -7
- data/smoke/regression/block_param_split.rb +0 -7
- data/smoke/regression/block_param_split.rbs +0 -3
- data/smoke/regression/empty_yield.rb +0 -5
- data/smoke/regression/empty_yield.rbs +0 -3
- data/smoke/regression/enumerator_product.rb +0 -1
- data/smoke/regression/fun.rb +0 -8
- data/smoke/regression/fun.rbs +0 -4
- data/smoke/regression/hash.rb +0 -7
- data/smoke/regression/hello world.rb +0 -1
- data/smoke/regression/issue_328.rb +0 -1
- data/smoke/regression/issue_328.rbs +0 -0
- data/smoke/regression/issue_332.rb +0 -11
- data/smoke/regression/issue_332.rbs +0 -19
- data/smoke/regression/issue_372.rb +0 -8
- data/smoke/regression/issue_372.rbs +0 -4
- data/smoke/regression/lambda.rb +0 -3
- data/smoke/regression/masgn.rb +0 -4
- data/smoke/regression/poly_new.rb +0 -2
- data/smoke/regression/poly_new.rbs +0 -4
- data/smoke/regression/range.rb +0 -5
- data/smoke/regression/set_divide.rb +0 -12
- data/smoke/regression/test_expectations.yml +0 -120
- data/smoke/regression/thread.rb +0 -7
- data/smoke/rescue/Steepfile +0 -6
- data/smoke/rescue/a.rb +0 -48
- data/smoke/rescue/test_expectations.yml +0 -79
- data/smoke/self/Steepfile +0 -6
- data/smoke/self/a.rb +0 -21
- data/smoke/self/a.rbs +0 -4
- data/smoke/self/test_expectations.yml +0 -23
- data/smoke/skip/Steepfile +0 -6
- data/smoke/skip/skip.rb +0 -13
- data/smoke/skip/test_expectations.yml +0 -23
- data/smoke/stdout/Steepfile +0 -6
- data/smoke/stdout/a.rb +0 -8
- data/smoke/stdout/a.rbs +0 -7
- data/smoke/stdout/test_expectations.yml +0 -1
- data/smoke/super/Steepfile +0 -6
- data/smoke/super/a.rb +0 -30
- data/smoke/super/a.rbs +0 -10
- data/smoke/super/test_expectations.yml +0 -69
- data/smoke/toplevel/Steepfile +0 -6
- data/smoke/toplevel/a.rb +0 -3
- data/smoke/toplevel/a.rbs +0 -3
- data/smoke/toplevel/test_expectations.yml +0 -15
- data/smoke/tsort/Steepfile +0 -7
- data/smoke/tsort/a.rb +0 -12
- data/smoke/tsort/test_expectations.yml +0 -1
- data/smoke/type_case/Steepfile +0 -6
- data/smoke/type_case/a.rb +0 -24
- data/smoke/type_case/test_expectations.yml +0 -58
- data/smoke/unexpected/Steepfile +0 -6
- data/smoke/unexpected/test_expectations.yml +0 -13
- data/smoke/unexpected/unexpected.rbs +0 -3
- data/smoke/yield/Steepfile +0 -6
- data/smoke/yield/a.rb +0 -15
- data/smoke/yield/b.rb +0 -6
- data/smoke/yield/test_expectations.yml +0 -88
@@ -0,0 +1,301 @@
|
|
1
|
+
module Steep
|
2
|
+
module TypeInference
|
3
|
+
class CaseWhen
|
4
|
+
class WhenPatterns
|
5
|
+
include NodeHelper
|
6
|
+
|
7
|
+
attr_reader :logic, :initial_constr, :unreachable_clause, :pattern_results
|
8
|
+
|
9
|
+
def initialize(logic, initial_constr, unreachable_clause, assignment_node)
|
10
|
+
@logic = logic
|
11
|
+
@initial_constr = initial_constr
|
12
|
+
@unreachable_clause = unreachable_clause
|
13
|
+
@assignment_node = assignment_node
|
14
|
+
|
15
|
+
@pattern_results = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_pattern(pat)
|
19
|
+
test_node = pat.updated(:send, [pat, :===, assignment_node])
|
20
|
+
|
21
|
+
latest_constr, unreachable_pattern = latest_result
|
22
|
+
|
23
|
+
type, constr = yield(test_node, latest_constr, unreachable_pattern)
|
24
|
+
truthy_result, falsy_result = logic.eval(env: latest_constr.context.type_env, node: test_node)
|
25
|
+
|
26
|
+
pattern_results << [pat, truthy_result, falsy_result]
|
27
|
+
end
|
28
|
+
|
29
|
+
def latest_result
|
30
|
+
if (_, truthy, falsy = pattern_results.last)
|
31
|
+
[
|
32
|
+
initial_constr.update_type_env { falsy.env },
|
33
|
+
falsy.unreachable
|
34
|
+
]
|
35
|
+
else
|
36
|
+
[initial_constr, unreachable_clause]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def body_result
|
41
|
+
raise if pattern_results.empty?
|
42
|
+
|
43
|
+
type_envs = pattern_results.map {|_, truthy, _| truthy.env }
|
44
|
+
env = initial_constr.context.type_env.join(*type_envs)
|
45
|
+
|
46
|
+
env = yield(env) || env
|
47
|
+
|
48
|
+
[
|
49
|
+
initial_constr.update_type_env { env },
|
50
|
+
unreachable_clause || pattern_results.all? {|_, truthy, _| truthy.unreachable }
|
51
|
+
]
|
52
|
+
end
|
53
|
+
|
54
|
+
def falsy_result
|
55
|
+
(_, _, falsy = pattern_results.last) or raise
|
56
|
+
|
57
|
+
[
|
58
|
+
initial_constr.update_type_env { falsy.env },
|
59
|
+
unreachable_clause || falsy.unreachable
|
60
|
+
]
|
61
|
+
end
|
62
|
+
|
63
|
+
def assignment_node()
|
64
|
+
clone_node(@assignment_node)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
include NodeHelper
|
69
|
+
extend NodeHelper
|
70
|
+
|
71
|
+
def self.type_check(constr, node, logic, hint:, condition:)
|
72
|
+
case_when = new(node, logic) do |condition_node|
|
73
|
+
constr.synthesize(condition_node)
|
74
|
+
end
|
75
|
+
|
76
|
+
case_when.when_clauses() do |when_pats, patterns, body_node, loc|
|
77
|
+
patterns.each do |pat|
|
78
|
+
when_pats.add_pattern(pat) {|test, constr| constr.synthesize(test) }
|
79
|
+
end
|
80
|
+
|
81
|
+
body_constr, body_unreachable = when_pats.body_result() do |env|
|
82
|
+
case_when.propagate_value_node_type(env)
|
83
|
+
end
|
84
|
+
|
85
|
+
if body_node
|
86
|
+
body_constr = body_constr.for_branch(body_node)
|
87
|
+
type, body_constr = body_constr.synthesize(body_node, hint: hint, condition: condition)
|
88
|
+
else
|
89
|
+
type = AST::Builtin.nil_type
|
90
|
+
end
|
91
|
+
|
92
|
+
body_result = LogicTypeInterpreter::Result.new(
|
93
|
+
type: type,
|
94
|
+
env: body_constr.context.type_env,
|
95
|
+
unreachable: body_unreachable
|
96
|
+
)
|
97
|
+
|
98
|
+
falsy_constr, falsy_unreachable = when_pats.falsy_result
|
99
|
+
next_result = LogicTypeInterpreter::Result.new(
|
100
|
+
type: AST::Builtin.any_type, # Unused for falsy pattern
|
101
|
+
env: falsy_constr.context.type_env,
|
102
|
+
unreachable: falsy_unreachable
|
103
|
+
)
|
104
|
+
|
105
|
+
[body_result, next_result]
|
106
|
+
end
|
107
|
+
|
108
|
+
case_when.else_clause do |else_node, constr|
|
109
|
+
constr.synthesize(else_node, hint: hint, condition: condition)
|
110
|
+
end
|
111
|
+
|
112
|
+
case_when.result()
|
113
|
+
end
|
114
|
+
|
115
|
+
attr_reader :location, :node, :condition_node, :when_nodes, :else_node
|
116
|
+
attr_reader :initial_constr, :logic, :clause_results, :else_result
|
117
|
+
attr_reader :assignment_node, :value_node, :var_name
|
118
|
+
|
119
|
+
def initialize(node, logic)
|
120
|
+
@node = node
|
121
|
+
|
122
|
+
condition_node, when_nodes, else_node, location = deconstruct_case_node!(node)
|
123
|
+
condition_node or raise "CaseWhen works for case-when syntax with condition node"
|
124
|
+
|
125
|
+
@condition_node = condition_node
|
126
|
+
@when_nodes = when_nodes
|
127
|
+
@else_node = else_node
|
128
|
+
@location = location
|
129
|
+
@logic = logic
|
130
|
+
@clause_results = []
|
131
|
+
|
132
|
+
type, constr = yield(condition_node)
|
133
|
+
|
134
|
+
@var_name = "__case_when:#{SecureRandom.alphanumeric(5)}__".to_sym
|
135
|
+
@value_node, @assignment_node = rewrite_condition_node(var_name, condition_node)
|
136
|
+
|
137
|
+
@initial_constr = constr.update_type_env do |env|
|
138
|
+
env.merge(local_variable_types: { var_name => [type, nil] })
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def when_clauses()
|
143
|
+
when_nodes.each do |when_node|
|
144
|
+
clause_constr, unreachable = latest_result
|
145
|
+
|
146
|
+
patterns, body, loc = deconstruct_when_node!(when_node)
|
147
|
+
|
148
|
+
when_pats = WhenPatterns.new(
|
149
|
+
logic,
|
150
|
+
clause_constr,
|
151
|
+
unreachable,
|
152
|
+
assignment_node
|
153
|
+
)
|
154
|
+
|
155
|
+
body_result, next_result = yield(
|
156
|
+
when_pats,
|
157
|
+
patterns,
|
158
|
+
body,
|
159
|
+
loc
|
160
|
+
)
|
161
|
+
|
162
|
+
if body_result.unreachable
|
163
|
+
if body_result.type.is_a?(AST::Types::Any) || initial_constr.no_subtyping?(sub_type: body_result.type, super_type: AST::Builtin.bottom_type)
|
164
|
+
typing.add_error(
|
165
|
+
Diagnostic::Ruby::UnreachableValueBranch.new(
|
166
|
+
node: when_node,
|
167
|
+
type: body_result.type,
|
168
|
+
location: loc.keyword
|
169
|
+
)
|
170
|
+
)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
clause_results << [body_result, next_result]
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def else_clause()
|
179
|
+
unless else_loc = has_else_clause?
|
180
|
+
return
|
181
|
+
end
|
182
|
+
|
183
|
+
constr, unreachable = latest_result
|
184
|
+
|
185
|
+
constr = constr.update_type_env do |env|
|
186
|
+
propagate_value_node_type(env) || env
|
187
|
+
end
|
188
|
+
|
189
|
+
@else_result =
|
190
|
+
if else_node
|
191
|
+
yield(else_node, constr)
|
192
|
+
else
|
193
|
+
TypeConstruction::Pair.new(type: AST::Builtin.nil_type, constr: constr)
|
194
|
+
end
|
195
|
+
|
196
|
+
else_result or raise
|
197
|
+
|
198
|
+
if unreachable
|
199
|
+
if else_result.type.is_a?(AST::Types::Any) || initial_constr.no_subtyping?(sub_type: else_result.type, super_type: AST::Builtin.bottom_type)
|
200
|
+
typing.add_error(
|
201
|
+
Diagnostic::Ruby::UnreachableValueBranch.new(
|
202
|
+
node: else_node || node,
|
203
|
+
type: else_result.type,
|
204
|
+
location: else_loc
|
205
|
+
)
|
206
|
+
)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def latest_result
|
212
|
+
if (_, falsy_result = clause_results.last)
|
213
|
+
[
|
214
|
+
initial_constr.update_type_env { falsy_result.env },
|
215
|
+
falsy_result.unreachable
|
216
|
+
]
|
217
|
+
else
|
218
|
+
[initial_constr, false]
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def result
|
223
|
+
results = clause_results.filter_map do |body, _|
|
224
|
+
unless body.unreachable
|
225
|
+
body
|
226
|
+
end
|
227
|
+
end
|
228
|
+
next_constr, next_clause_unreachable = latest_result
|
229
|
+
|
230
|
+
unless next_clause_unreachable
|
231
|
+
if else_result
|
232
|
+
results << LogicTypeInterpreter::Result.new(
|
233
|
+
type: else_result.type,
|
234
|
+
env: else_result.context.type_env,
|
235
|
+
unreachable: false # Unused
|
236
|
+
)
|
237
|
+
else
|
238
|
+
results << LogicTypeInterpreter::Result.new(
|
239
|
+
type: AST::Builtin.nil_type,
|
240
|
+
env: next_constr.context.type_env,
|
241
|
+
unreachable: false
|
242
|
+
)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
types = results.map {|result| result.type }
|
247
|
+
envs = results.map {|result| result.env }
|
248
|
+
|
249
|
+
[
|
250
|
+
types,
|
251
|
+
envs
|
252
|
+
]
|
253
|
+
end
|
254
|
+
|
255
|
+
def has_else_clause?
|
256
|
+
location.else
|
257
|
+
end
|
258
|
+
|
259
|
+
def typing
|
260
|
+
logic.typing
|
261
|
+
end
|
262
|
+
|
263
|
+
def rewrite_condition_node(var_name, node)
|
264
|
+
case node.type
|
265
|
+
when :lvasgn
|
266
|
+
name, rhs = node.children
|
267
|
+
value, rhs = rewrite_condition_node(var_name, rhs)
|
268
|
+
[value, node.updated(nil, [name, rhs])]
|
269
|
+
when :lvar
|
270
|
+
name, = node.children
|
271
|
+
[
|
272
|
+
nil,
|
273
|
+
node.updated(:lvasgn, [name, node.updated(:lvar, [var_name])])
|
274
|
+
]
|
275
|
+
when :begin
|
276
|
+
*children, last = node.children
|
277
|
+
value_node, last = rewrite_condition_node(var_name, last)
|
278
|
+
[
|
279
|
+
value_node,
|
280
|
+
node.updated(nil, children.push(last))
|
281
|
+
]
|
282
|
+
else
|
283
|
+
[
|
284
|
+
node,
|
285
|
+
node.updated(:lvar, [var_name])
|
286
|
+
]
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def propagate_value_node_type(env)
|
291
|
+
if value_node
|
292
|
+
if (call = initial_constr.typing.method_calls[value_node]).is_a?(MethodCall::Typed)
|
293
|
+
if env[value_node]
|
294
|
+
env.merge(pure_method_calls: { value_node => [call, env[var_name]] })
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
data/lib/steep/version.rb
CHANGED
data/lib/steep.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require "steep/version"
|
2
2
|
|
3
3
|
require "pathname"
|
4
|
-
require "parser/
|
4
|
+
require "parser/ruby33"
|
5
5
|
require "active_support"
|
6
6
|
require "active_support/core_ext/object/try"
|
7
7
|
require "active_support/core_ext/string/inflections"
|
@@ -52,6 +52,7 @@ require "steep/ast/node/type_assertion"
|
|
52
52
|
require "steep/ast/node/type_application"
|
53
53
|
require "steep/ast/builtin"
|
54
54
|
require "steep/ast/types/factory"
|
55
|
+
require "steep/ast/ignore"
|
55
56
|
|
56
57
|
require "steep/range_extension"
|
57
58
|
|
@@ -76,6 +77,7 @@ require "steep/diagnostic/signature"
|
|
76
77
|
require "steep/diagnostic/lsp_formatter"
|
77
78
|
require "steep/signature/validator"
|
78
79
|
require "steep/source"
|
80
|
+
require "steep/source/ignore_ranges"
|
79
81
|
require "steep/annotation_parser"
|
80
82
|
require "steep/typing"
|
81
83
|
require "steep/module_helper"
|
@@ -91,6 +93,7 @@ require "steep/type_inference/type_env_builder"
|
|
91
93
|
require "steep/type_inference/logic_type_interpreter"
|
92
94
|
require "steep/type_inference/multiple_assignment"
|
93
95
|
require "steep/type_inference/method_call"
|
96
|
+
require "steep/type_inference/case_when"
|
94
97
|
|
95
98
|
require "steep/index/rbs_index"
|
96
99
|
require "steep/index/signature_symbol_provider"
|
@@ -152,6 +155,10 @@ module Steep
|
|
152
155
|
@logger || raise
|
153
156
|
end
|
154
157
|
|
158
|
+
def self.ui_logger
|
159
|
+
@ui_logger || raise
|
160
|
+
end
|
161
|
+
|
155
162
|
def self.new_logger(output, prev_level)
|
156
163
|
ActiveSupport::TaggedLogging.new(Logger.new(output)).tap do |logger|
|
157
164
|
logger.push_tags "Steep #{VERSION}"
|
@@ -165,12 +172,18 @@ module Steep
|
|
165
172
|
|
166
173
|
def self.log_output=(output)
|
167
174
|
@log_output = output
|
175
|
+
|
168
176
|
prev_level = @logger&.level
|
169
177
|
@logger = new_logger(output, prev_level)
|
178
|
+
|
179
|
+
prev_level = @ui_logger&.level
|
180
|
+
@ui_logger = new_logger(output, prev_level)
|
181
|
+
|
170
182
|
output
|
171
183
|
end
|
172
184
|
|
173
185
|
@logger = nil
|
186
|
+
@ui_logger = nil
|
174
187
|
self.log_output = STDERR
|
175
188
|
|
176
189
|
def self.measure(message, level: :warn)
|
@@ -1,10 +1,4 @@
|
|
1
1
|
---
|
2
|
-
sources:
|
3
|
-
- type: git
|
4
|
-
name: ruby/gem_rbs_collection
|
5
|
-
revision: c42c09528dd99252db98f0744181a6de54ec2f55
|
6
|
-
remote: https://github.com/ruby/gem_rbs_collection.git
|
7
|
-
repo_dir: gems
|
8
2
|
path: ".gem_rbs_collection"
|
9
3
|
gems:
|
10
4
|
- name: activesupport
|
@@ -15,6 +9,14 @@ gems:
|
|
15
9
|
revision: c42c09528dd99252db98f0744181a6de54ec2f55
|
16
10
|
remote: https://github.com/ruby/gem_rbs_collection.git
|
17
11
|
repo_dir: gems
|
12
|
+
- name: base64
|
13
|
+
version: '0'
|
14
|
+
source:
|
15
|
+
type: stdlib
|
16
|
+
- name: bigdecimal
|
17
|
+
version: '0'
|
18
|
+
source:
|
19
|
+
type: stdlib
|
18
20
|
- name: concurrent-ruby
|
19
21
|
version: '1.1'
|
20
22
|
source:
|
data/sig/shims/parser.rbs
CHANGED
@@ -35,6 +35,16 @@ module Parser
|
|
35
35
|
attr_reader diagnostics: untyped
|
36
36
|
end
|
37
37
|
|
38
|
+
class Ruby33
|
39
|
+
def initialize: (untyped builder) -> void
|
40
|
+
|
41
|
+
def parse: (Source::Buffer) -> AST
|
42
|
+
|
43
|
+
def parse_with_comments: (Source::Buffer) -> [AST::Node, Array[Source::Comment]]
|
44
|
+
|
45
|
+
attr_reader diagnostics: untyped
|
46
|
+
end
|
47
|
+
|
38
48
|
module Source
|
39
49
|
class Buffer
|
40
50
|
def initialize: (String file, Integer lineno, source: String) -> void
|
data/sig/shims/yaml.rbs
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
use Parser::Source::Comment, RBS::Buffer, RBS::Location
|
2
|
+
|
3
|
+
module Steep
|
4
|
+
module AST
|
5
|
+
module Ignore
|
6
|
+
class BufferScanner
|
7
|
+
attr_reader scanner: StringScanner
|
8
|
+
|
9
|
+
attr_reader location: Location[untyped, untyped]
|
10
|
+
|
11
|
+
def initialize: (Location[untyped, untyped]) -> void
|
12
|
+
|
13
|
+
def offset: () -> Integer
|
14
|
+
|
15
|
+
def charpos: () -> Integer
|
16
|
+
|
17
|
+
def scan: (Regexp) -> Location[bot, bot]?
|
18
|
+
|
19
|
+
def skip: (Regexp) -> void
|
20
|
+
|
21
|
+
def eos?: () -> bool
|
22
|
+
end
|
23
|
+
|
24
|
+
type t = IgnoreStart | IgnoreEnd | IgnoreLine
|
25
|
+
|
26
|
+
def self.parse: (Comment, Buffer) -> t?
|
27
|
+
|
28
|
+
class IgnoreStart
|
29
|
+
attr_reader comment: Comment
|
30
|
+
|
31
|
+
attr_reader location: Location[bot, bot]
|
32
|
+
|
33
|
+
def initialize: (Comment, Location[bot, bot]) -> void
|
34
|
+
|
35
|
+
def line: () -> Integer
|
36
|
+
end
|
37
|
+
|
38
|
+
class IgnoreEnd
|
39
|
+
attr_reader comment: Comment
|
40
|
+
|
41
|
+
attr_reader location: Location[bot, bot]
|
42
|
+
|
43
|
+
def initialize: (Comment, Location[bot, bot]) -> void
|
44
|
+
|
45
|
+
def line: () -> Integer
|
46
|
+
end
|
47
|
+
|
48
|
+
class IgnoreLine
|
49
|
+
type diagnostic = Location[:name, :following_comma]
|
50
|
+
type diagnostics = Array[diagnostic]
|
51
|
+
|
52
|
+
attr_reader comment: Comment
|
53
|
+
|
54
|
+
attr_reader location: Location[:keyword, bot]
|
55
|
+
|
56
|
+
attr_reader raw_diagnostics: diagnostics
|
57
|
+
|
58
|
+
def initialize: (Comment, diagnostics, Location[:keyword, bot]) -> void
|
59
|
+
|
60
|
+
def line: () -> Integer
|
61
|
+
|
62
|
+
def ignored_diagnostics: () -> (:all | Array[String])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -628,6 +628,14 @@ module Steep
|
|
628
628
|
def initialize: (error: Signature::Base, node: Parser::AST::Node, location: location) -> void
|
629
629
|
end
|
630
630
|
|
631
|
+
# steep:ignore comment is invalid
|
632
|
+
#
|
633
|
+
class InvalidIgnoreComment < Base
|
634
|
+
attr_reader comment: Parser::Source::Comment
|
635
|
+
|
636
|
+
def initialize: (comment: Parser::Source::Comment) -> void
|
637
|
+
end
|
638
|
+
|
631
639
|
# Argument forwarding `...` cannot be done safely, because of
|
632
640
|
#
|
633
641
|
# 1. The arguments are incompatible, or
|
data/sig/steep/node_helper.rbs
CHANGED
@@ -74,5 +74,7 @@ module Steep
|
|
74
74
|
# If the next node is a `block` or `numblock` that is associated to the *sendish node*, it is the block node.
|
75
75
|
#
|
76
76
|
def deconstruct_sendish_and_block_nodes: (*Parser::AST::Node) -> [Parser::AST::Node, Parser::AST::Node?]?
|
77
|
+
|
78
|
+
def clone_node: (Node) -> Node
|
77
79
|
end
|
78
80
|
end
|
data/sig/steep/project/dsl.rbs
CHANGED
@@ -24,9 +24,13 @@ module Steep
|
|
24
24
|
|
25
25
|
attr_reader project: Project?
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
# Attribute to keep track of collection configuration
|
28
|
+
#
|
29
|
+
# * `Pathname` means loading the configuration from the path
|
30
|
+
# * `nil` means no configuration is given
|
31
|
+
# * `false` means rbs-collection is disabled
|
32
|
+
#
|
33
|
+
attr_reader collection_config_path: Pathname | nil | false
|
30
34
|
|
31
35
|
def project!: () -> Project
|
32
36
|
|
@@ -86,7 +90,7 @@ module Steep
|
|
86
90
|
def disable_collection: () -> void
|
87
91
|
end
|
88
92
|
|
89
|
-
attr_reader project:
|
93
|
+
attr_reader project: Project
|
90
94
|
|
91
95
|
@@templates: Hash[Symbol, TargetDSL]
|
92
96
|
|
@@ -19,9 +19,29 @@ module Steep
|
|
19
19
|
|
20
20
|
attr_accessor paths: PathOptions
|
21
21
|
|
22
|
-
attr_accessor
|
22
|
+
attr_accessor collection_config_path: Pathname?
|
23
23
|
|
24
24
|
def initialize: () -> void
|
25
|
+
|
26
|
+
# Returns path of lockfile
|
27
|
+
%a{pure} def collection_lock_path: () -> Pathname?
|
28
|
+
|
29
|
+
# Returns `Lockfile` instance if it can be loaded
|
30
|
+
#
|
31
|
+
%a{pure} def collection_lock: () -> RBS::Collection::Config::Lockfile?
|
32
|
+
|
33
|
+
@collection_lock: RBS::Collection::Config::Lockfile | Pathname | YAML::SyntaxError | RBS::Collection::Config::CollectionNotAvailable | nil
|
34
|
+
|
35
|
+
# Load collection configuration
|
36
|
+
#
|
37
|
+
# * Returns `Lockfile` instance if successfully loaded
|
38
|
+
# * Returns `nil` if collection is disabled
|
39
|
+
# * Returns `Pathname` if a file is missing
|
40
|
+
# * Returns `YAML::SyntaxError` or `CollectionNotAvailable` if an error is raised
|
41
|
+
#
|
42
|
+
# It keeps the last result unless `force: true` is specified.
|
43
|
+
#
|
44
|
+
def load_collection_lock: (?force: bool) -> (RBS::Collection::Config::Lockfile | Pathname | YAML::SyntaxError | RBS::Collection::Config::CollectionNotAvailable | nil)
|
25
45
|
end
|
26
46
|
end
|
27
47
|
end
|
data/sig/steep/project.rbs
CHANGED
@@ -2,11 +2,11 @@ module Steep
|
|
2
2
|
class Project
|
3
3
|
attr_reader targets: Array[Target]
|
4
4
|
|
5
|
-
attr_reader steepfile_path: Pathname
|
5
|
+
attr_reader steepfile_path: Pathname?
|
6
6
|
|
7
|
-
|
7
|
+
attr_reader base_dir: Pathname
|
8
8
|
|
9
|
-
def base_dir:
|
9
|
+
def initialize: (steepfile_path: Pathname?, ?base_dir: Pathname?) -> void
|
10
10
|
|
11
11
|
def relative_path: (Pathname path) -> Pathname
|
12
12
|
|
@@ -1,13 +1,15 @@
|
|
1
1
|
module Steep
|
2
2
|
module Server
|
3
|
-
module ChangeBuffer : _WithProject
|
3
|
+
module ChangeBuffer : _WithProject, _WithMutex
|
4
4
|
interface _WithProject
|
5
5
|
def project: () -> Project
|
6
6
|
end
|
7
7
|
|
8
|
-
|
8
|
+
interface _WithMutex
|
9
|
+
def mutex: () -> Mutex
|
10
|
+
end
|
9
11
|
|
10
|
-
|
12
|
+
type changes = Hash[Pathname, Array[Services::ContentChange]]
|
11
13
|
|
12
14
|
attr_reader buffered_changes: changes
|
13
15
|
|
@@ -29,7 +31,7 @@ module Steep
|
|
29
31
|
def collect_changes: (untyped request) -> void
|
30
32
|
|
31
33
|
# Reset the content of `uri` to `text`
|
32
|
-
#
|
34
|
+
#
|
33
35
|
def reset_change: (uri: String, text: String) -> void
|
34
36
|
end
|
35
37
|
end
|
@@ -58,10 +58,20 @@ module Steep
|
|
58
58
|
|
59
59
|
attr_reader service: Services::TypeCheckService
|
60
60
|
|
61
|
+
attr_reader mutex: Mutex
|
62
|
+
|
61
63
|
def initialize: (project: Project, reader: Reader, writer: Writer, ?queue: Queue) -> void
|
62
64
|
|
63
65
|
def handle_job: (job) -> void
|
64
66
|
|
67
|
+
@last_job_mutex: Mutex
|
68
|
+
|
69
|
+
@last_job: job?
|
70
|
+
|
71
|
+
def process_latest_job: [T] (job) { () -> T } -> T?
|
72
|
+
|
73
|
+
def queue_job: (job) -> void
|
74
|
+
|
65
75
|
type lsp_request = { id: String, method: String, params: untyped }
|
66
76
|
|
67
77
|
def handle_request: (lsp_request) -> void
|
@@ -49,7 +49,7 @@ module Steep
|
|
49
49
|
def self.start_worker: (
|
50
50
|
worker_type `type`,
|
51
51
|
name: String,
|
52
|
-
steepfile: Pathname
|
52
|
+
steepfile: Pathname?,
|
53
53
|
steep_command: String?,
|
54
54
|
?patterns: Array[String],
|
55
55
|
?delay_shutdown: bool,
|
@@ -59,7 +59,7 @@ module Steep
|
|
59
59
|
def self.fork_worker: (
|
60
60
|
worker_type `type`,
|
61
61
|
name: String,
|
62
|
-
steepfile: Pathname
|
62
|
+
steepfile: Pathname?,
|
63
63
|
patterns: Array[String],
|
64
64
|
delay_shutdown: bool,
|
65
65
|
index: [Integer, Integer]?
|
@@ -68,7 +68,7 @@ module Steep
|
|
68
68
|
def self.spawn_worker: (
|
69
69
|
worker_type `type`,
|
70
70
|
name: String,
|
71
|
-
steepfile: Pathname
|
71
|
+
steepfile: Pathname?,
|
72
72
|
steep_command: ::String,
|
73
73
|
patterns: Array[String],
|
74
74
|
delay_shutdown: bool,
|
@@ -76,7 +76,7 @@ module Steep
|
|
76
76
|
) -> WorkerProcess
|
77
77
|
|
78
78
|
def self.start_typecheck_workers: (
|
79
|
-
steepfile: Pathname
|
79
|
+
steepfile: Pathname?,
|
80
80
|
args: Array[String],
|
81
81
|
steep_command: ::String?,
|
82
82
|
?count: Integer,
|