steep 1.6.0 → 1.7.0.dev.2
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/.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/Rakefile +5 -0
- data/bin/output_test.rb +1 -0
- data/doc/narrowing.md +195 -0
- data/gemfile_steep/Gemfile +1 -1
- data/gemfile_steep/Gemfile.lock +31 -16
- 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 +20 -17
- 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/subtyping/check.rb +6 -0
- data/lib/steep/subtyping/result.rb +6 -0
- data/lib/steep/type_construction.rb +6 -151
- 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 +4 -2
- data/sig/steep/subtyping/result.rbs +5 -1
- data/sig/steep/type_construction.rbs +1 -26
- data/sig/steep/type_inference/case_when.rbs +130 -0
- data/sig/steep.rbs +9 -0
- data/steep.gemspec +1 -1
- metadata +11 -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,
|