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.
Files changed (314) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +6 -0
  3. data/.github/workflows/ruby-windows.yml +2 -1
  4. data/.github/workflows/ruby.yml +9 -10
  5. data/.gitignore +0 -1
  6. data/Gemfile +1 -1
  7. data/Gemfile.lock +30 -28
  8. data/Rakefile +5 -0
  9. data/bin/output_test.rb +1 -0
  10. data/doc/narrowing.md +195 -0
  11. data/gemfile_steep/Gemfile +1 -1
  12. data/gemfile_steep/Gemfile.lock +31 -16
  13. data/guides/src/getting-started/getting-started.md +10 -11
  14. data/lib/steep/ast/ignore.rb +148 -0
  15. data/lib/steep/cli.rb +6 -1
  16. data/lib/steep/diagnostic/ruby.rb +16 -0
  17. data/lib/steep/drivers/utils/driver_helper.rb +22 -11
  18. data/lib/steep/node_helper.rb +12 -0
  19. data/lib/steep/project/dsl.rb +18 -21
  20. data/lib/steep/project/options.rb +39 -2
  21. data/lib/steep/project.rb +11 -7
  22. data/lib/steep/server/change_buffer.rb +2 -2
  23. data/lib/steep/server/interaction_worker.rb +61 -11
  24. data/lib/steep/server/worker_process.rb +3 -1
  25. data/lib/steep/services/completion_provider.rb +39 -8
  26. data/lib/steep/services/file_loader.rb +3 -2
  27. data/lib/steep/services/signature_help_provider.rb +20 -17
  28. data/lib/steep/services/type_check_service.rb +36 -8
  29. data/lib/steep/source/ignore_ranges.rb +69 -0
  30. data/lib/steep/source.rb +10 -4
  31. data/lib/steep/subtyping/check.rb +6 -0
  32. data/lib/steep/subtyping/result.rb +6 -0
  33. data/lib/steep/type_construction.rb +6 -151
  34. data/lib/steep/type_inference/case_when.rb +301 -0
  35. data/lib/steep/version.rb +1 -1
  36. data/lib/steep.rb +14 -1
  37. data/rbs_collection.steep.lock.yaml +8 -6
  38. data/sig/shims/parser.rbs +10 -0
  39. data/sig/shims/yaml.rbs +4 -0
  40. data/sig/steep/ast/ignore.rbs +66 -0
  41. data/sig/steep/diagnostic/ruby.rbs +8 -0
  42. data/sig/steep/node_helper.rbs +2 -0
  43. data/sig/steep/project/dsl.rbs +8 -4
  44. data/sig/steep/project/options.rbs +21 -1
  45. data/sig/steep/project.rbs +3 -3
  46. data/sig/steep/server/change_buffer.rbs +6 -4
  47. data/sig/steep/server/interaction_worker.rbs +10 -0
  48. data/sig/steep/server/worker_process.rbs +4 -4
  49. data/sig/steep/services/completion_provider.rbs +14 -1
  50. data/sig/steep/services/type_check_service.rbs +6 -1
  51. data/sig/steep/source/ignore_ranges.rbs +38 -0
  52. data/sig/steep/source.rbs +5 -2
  53. data/sig/steep/subtyping/check.rbs +4 -2
  54. data/sig/steep/subtyping/result.rbs +5 -1
  55. data/sig/steep/type_construction.rbs +1 -26
  56. data/sig/steep/type_inference/case_when.rbs +130 -0
  57. data/sig/steep.rbs +9 -0
  58. data/steep.gemspec +1 -1
  59. metadata +11 -258
  60. data/smoke/alias/Steepfile +0 -6
  61. data/smoke/alias/a.rb +0 -16
  62. data/smoke/alias/a.rbs +0 -10
  63. data/smoke/alias/b.rb +0 -6
  64. data/smoke/alias/c.rb +0 -8
  65. data/smoke/alias/test_expectations.yml +0 -96
  66. data/smoke/and/Steepfile +0 -6
  67. data/smoke/and/a.rb +0 -8
  68. data/smoke/and/test_expectations.yml +0 -29
  69. data/smoke/array/Steepfile +0 -6
  70. data/smoke/array/a.rb +0 -18
  71. data/smoke/array/b.rb +0 -12
  72. data/smoke/array/c.rb +0 -6
  73. data/smoke/array/test_expectations.yml +0 -103
  74. data/smoke/block/Steepfile +0 -6
  75. data/smoke/block/a.rb +0 -10
  76. data/smoke/block/a.rbs +0 -6
  77. data/smoke/block/b.rb +0 -13
  78. data/smoke/block/c.rb +0 -9
  79. data/smoke/block/c.rbs +0 -3
  80. data/smoke/block/d.rb +0 -11
  81. data/smoke/block/e.rb +0 -12
  82. data/smoke/block/e.rbs +0 -4
  83. data/smoke/block/test_expectations.yml +0 -133
  84. data/smoke/case/Steepfile +0 -6
  85. data/smoke/case/a.rb +0 -18
  86. data/smoke/case/test_expectations.yml +0 -47
  87. data/smoke/class/Steepfile +0 -6
  88. data/smoke/class/a.rb +0 -25
  89. data/smoke/class/a.rbs +0 -23
  90. data/smoke/class/b.rb +0 -5
  91. data/smoke/class/c.rb +0 -9
  92. data/smoke/class/f.rb +0 -10
  93. data/smoke/class/g.rb +0 -6
  94. data/smoke/class/h.rb +0 -19
  95. data/smoke/class/h.rbs +0 -6
  96. data/smoke/class/i.rb +0 -14
  97. data/smoke/class/i.rbs +0 -9
  98. data/smoke/class/test_expectations.yml +0 -117
  99. data/smoke/compact/Steepfile +0 -6
  100. data/smoke/compact/a.rb +0 -2
  101. data/smoke/compact/a.rbs +0 -5
  102. data/smoke/compact/b.rb +0 -2
  103. data/smoke/compact/test_expectations.yml +0 -18
  104. data/smoke/const/Steepfile +0 -6
  105. data/smoke/const/a.rb +0 -27
  106. data/smoke/const/b.rb +0 -7
  107. data/smoke/const/b.rbs +0 -5
  108. data/smoke/const/test_expectations.yml +0 -134
  109. data/smoke/diagnostics/Steepfile +0 -6
  110. data/smoke/diagnostics/a.rbs +0 -22
  111. data/smoke/diagnostics/argument_type_mismatch.rb +0 -1
  112. data/smoke/diagnostics/block_body_type_mismatch.rb +0 -1
  113. data/smoke/diagnostics/block_type_mismatch.rb +0 -3
  114. data/smoke/diagnostics/break_type_mismatch.rb +0 -1
  115. data/smoke/diagnostics/different_method_parameter_kind.rb +0 -9
  116. data/smoke/diagnostics/else_on_exhaustive_case.rb +0 -12
  117. data/smoke/diagnostics/incompatible_annotation.rb +0 -6
  118. data/smoke/diagnostics/incompatible_argument.rb +0 -1
  119. data/smoke/diagnostics/incompatible_assignment.rb +0 -8
  120. data/smoke/diagnostics/method_arity_mismatch.rb +0 -11
  121. data/smoke/diagnostics/method_body_type_mismatch.rb +0 -6
  122. data/smoke/diagnostics/method_definition_missing.rb +0 -2
  123. data/smoke/diagnostics/method_parameter_mismatch.rb +0 -10
  124. data/smoke/diagnostics/method_return_type_annotation_mismatch.rb +0 -7
  125. data/smoke/diagnostics/missing_keyword.rb +0 -1
  126. data/smoke/diagnostics/no_method.rb +0 -1
  127. data/smoke/diagnostics/proc_type_expected.rb +0 -3
  128. data/smoke/diagnostics/required_block_missing.rb +0 -1
  129. data/smoke/diagnostics/return_type_mismatch.rb +0 -6
  130. data/smoke/diagnostics/test_expectations.yml +0 -591
  131. data/smoke/diagnostics/unexpected_block_given.rb +0 -1
  132. data/smoke/diagnostics/unexpected_dynamic_method.rb +0 -3
  133. data/smoke/diagnostics/unexpected_jump.rb +0 -4
  134. data/smoke/diagnostics/unexpected_jump_value.rb +0 -3
  135. data/smoke/diagnostics/unexpected_keyword.rb +0 -1
  136. data/smoke/diagnostics/unexpected_splat.rb +0 -1
  137. data/smoke/diagnostics/unexpected_yield.rb +0 -6
  138. data/smoke/diagnostics/unknown_constant_assigned.rb +0 -7
  139. data/smoke/diagnostics/unresolved_overloading.rb +0 -1
  140. data/smoke/diagnostics/unsupported_syntax.rb +0 -2
  141. data/smoke/diagnostics-rbs/Steepfile +0 -8
  142. data/smoke/diagnostics-rbs/duplicated-method-definition.rbs +0 -20
  143. data/smoke/diagnostics-rbs/generic-parameter-mismatch.rbs +0 -7
  144. data/smoke/diagnostics-rbs/inherit-module.rbs +0 -2
  145. data/smoke/diagnostics-rbs/invalid-method-overload.rbs +0 -3
  146. data/smoke/diagnostics-rbs/invalid-type-application.rbs +0 -7
  147. data/smoke/diagnostics-rbs/invalid_variance_annotation.rbs +0 -3
  148. data/smoke/diagnostics-rbs/mixin-class-error.rbs +0 -6
  149. data/smoke/diagnostics-rbs/nonregular-type-alias.rbs +0 -3
  150. data/smoke/diagnostics-rbs/recursive-alias.rbs +0 -5
  151. data/smoke/diagnostics-rbs/recursive-class.rbs +0 -8
  152. data/smoke/diagnostics-rbs/recursive-type-alias.rbs +0 -3
  153. data/smoke/diagnostics-rbs/superclass-mismatch.rbs +0 -7
  154. data/smoke/diagnostics-rbs/test_expectations.yml +0 -300
  155. data/smoke/diagnostics-rbs/unknown-method-alias.rbs +0 -3
  156. data/smoke/diagnostics-rbs/unknown-type-name-2.rbs +0 -5
  157. data/smoke/diagnostics-rbs/unknown-type-name.rbs +0 -13
  158. data/smoke/diagnostics-rbs-duplicated/Steepfile +0 -6
  159. data/smoke/diagnostics-rbs-duplicated/a.rbs +0 -5
  160. data/smoke/diagnostics-rbs-duplicated/test_expectations.yml +0 -13
  161. data/smoke/diagnostics-ruby-unsat/Steepfile +0 -6
  162. data/smoke/diagnostics-ruby-unsat/a.rbs +0 -3
  163. data/smoke/diagnostics-ruby-unsat/test_expectations.yml +0 -27
  164. data/smoke/diagnostics-ruby-unsat/unsatisfiable_constraint.rb +0 -6
  165. data/smoke/dstr/Steepfile +0 -6
  166. data/smoke/dstr/a.rb +0 -5
  167. data/smoke/dstr/test_expectations.yml +0 -13
  168. data/smoke/ensure/Steepfile +0 -6
  169. data/smoke/ensure/a.rb +0 -18
  170. data/smoke/ensure/test_expectations.yml +0 -62
  171. data/smoke/enumerator/Steepfile +0 -6
  172. data/smoke/enumerator/a.rb +0 -6
  173. data/smoke/enumerator/b.rb +0 -17
  174. data/smoke/enumerator/test_expectations.yml +0 -47
  175. data/smoke/extension/Steepfile +0 -6
  176. data/smoke/extension/a.rb +0 -10
  177. data/smoke/extension/a.rbs +0 -13
  178. data/smoke/extension/b.rb +0 -10
  179. data/smoke/extension/c.rb +0 -9
  180. data/smoke/extension/d.rb +0 -2
  181. data/smoke/extension/e.rb +0 -2
  182. data/smoke/extension/e.rbs +0 -7
  183. data/smoke/extension/f.rb +0 -2
  184. data/smoke/extension/f.rbs +0 -3
  185. data/smoke/extension/test_expectations.yml +0 -73
  186. data/smoke/hash/Steepfile +0 -6
  187. data/smoke/hash/a.rb +0 -17
  188. data/smoke/hash/a.rbs +0 -8
  189. data/smoke/hash/b.rb +0 -6
  190. data/smoke/hash/c.rb +0 -15
  191. data/smoke/hash/d.rb +0 -5
  192. data/smoke/hash/e.rb +0 -1
  193. data/smoke/hash/e.rbs +0 -3
  194. data/smoke/hash/f.rb +0 -11
  195. data/smoke/hash/test_expectations.yml +0 -81
  196. data/smoke/hello/Steepfile +0 -6
  197. data/smoke/hello/hello.rb +0 -11
  198. data/smoke/hello/hello.rbs +0 -7
  199. data/smoke/hello/test_expectations.yml +0 -25
  200. data/smoke/if/Steepfile +0 -6
  201. data/smoke/if/a.rb +0 -20
  202. data/smoke/if/test_expectations.yml +0 -34
  203. data/smoke/implements/Steepfile +0 -6
  204. data/smoke/implements/a.rb +0 -12
  205. data/smoke/implements/a.rbs +0 -6
  206. data/smoke/implements/b.rb +0 -13
  207. data/smoke/implements/b.rbs +0 -12
  208. data/smoke/implements/test_expectations.yml +0 -23
  209. data/smoke/initialize/Steepfile +0 -6
  210. data/smoke/initialize/a.rb +0 -12
  211. data/smoke/initialize/a.rbs +0 -3
  212. data/smoke/initialize/test_expectations.yml +0 -1
  213. data/smoke/integer/Steepfile +0 -6
  214. data/smoke/integer/a.rb +0 -26
  215. data/smoke/integer/test_expectations.yml +0 -110
  216. data/smoke/interface/Steepfile +0 -6
  217. data/smoke/interface/a.rb +0 -12
  218. data/smoke/interface/a.rbs +0 -12
  219. data/smoke/interface/test_expectations.yml +0 -23
  220. data/smoke/kwbegin/Steepfile +0 -6
  221. data/smoke/kwbegin/a.rb +0 -7
  222. data/smoke/kwbegin/test_expectations.yml +0 -17
  223. data/smoke/lambda/Steepfile +0 -6
  224. data/smoke/lambda/a.rb +0 -10
  225. data/smoke/lambda/test_expectations.yml +0 -17
  226. data/smoke/literal/Steepfile +0 -6
  227. data/smoke/literal/a.rb +0 -11
  228. data/smoke/literal/b.rb +0 -7
  229. data/smoke/literal/literal_methods.rbs +0 -4
  230. data/smoke/literal/test_expectations.yml +0 -106
  231. data/smoke/map/Steepfile +0 -6
  232. data/smoke/map/a.rb +0 -5
  233. data/smoke/map/test_expectations.yml +0 -1
  234. data/smoke/method/Steepfile +0 -6
  235. data/smoke/method/a.rb +0 -21
  236. data/smoke/method/a.rbs +0 -4
  237. data/smoke/method/b.rb +0 -25
  238. data/smoke/method/c.rb +0 -5
  239. data/smoke/method/d.rb +0 -1
  240. data/smoke/method/d.rbs +0 -3
  241. data/smoke/method/test_expectations.yml +0 -121
  242. data/smoke/module/Steepfile +0 -6
  243. data/smoke/module/a.rb +0 -19
  244. data/smoke/module/a.rbs +0 -16
  245. data/smoke/module/b.rb +0 -6
  246. data/smoke/module/c.rb +0 -22
  247. data/smoke/module/d.rb +0 -4
  248. data/smoke/module/e.rb +0 -13
  249. data/smoke/module/f.rb +0 -11
  250. data/smoke/module/test_expectations.yml +0 -75
  251. data/smoke/regexp/Steepfile +0 -6
  252. data/smoke/regexp/a.rb +0 -109
  253. data/smoke/regexp/b.rb +0 -79
  254. data/smoke/regexp/test_expectations.yml +0 -615
  255. data/smoke/regression/Steepfile +0 -6
  256. data/smoke/regression/array.rb +0 -7
  257. data/smoke/regression/block_param_split.rb +0 -7
  258. data/smoke/regression/block_param_split.rbs +0 -3
  259. data/smoke/regression/empty_yield.rb +0 -5
  260. data/smoke/regression/empty_yield.rbs +0 -3
  261. data/smoke/regression/enumerator_product.rb +0 -1
  262. data/smoke/regression/fun.rb +0 -8
  263. data/smoke/regression/fun.rbs +0 -4
  264. data/smoke/regression/hash.rb +0 -7
  265. data/smoke/regression/hello world.rb +0 -1
  266. data/smoke/regression/issue_328.rb +0 -1
  267. data/smoke/regression/issue_328.rbs +0 -0
  268. data/smoke/regression/issue_332.rb +0 -11
  269. data/smoke/regression/issue_332.rbs +0 -19
  270. data/smoke/regression/issue_372.rb +0 -8
  271. data/smoke/regression/issue_372.rbs +0 -4
  272. data/smoke/regression/lambda.rb +0 -3
  273. data/smoke/regression/masgn.rb +0 -4
  274. data/smoke/regression/poly_new.rb +0 -2
  275. data/smoke/regression/poly_new.rbs +0 -4
  276. data/smoke/regression/range.rb +0 -5
  277. data/smoke/regression/set_divide.rb +0 -12
  278. data/smoke/regression/test_expectations.yml +0 -120
  279. data/smoke/regression/thread.rb +0 -7
  280. data/smoke/rescue/Steepfile +0 -6
  281. data/smoke/rescue/a.rb +0 -48
  282. data/smoke/rescue/test_expectations.yml +0 -79
  283. data/smoke/self/Steepfile +0 -6
  284. data/smoke/self/a.rb +0 -21
  285. data/smoke/self/a.rbs +0 -4
  286. data/smoke/self/test_expectations.yml +0 -23
  287. data/smoke/skip/Steepfile +0 -6
  288. data/smoke/skip/skip.rb +0 -13
  289. data/smoke/skip/test_expectations.yml +0 -23
  290. data/smoke/stdout/Steepfile +0 -6
  291. data/smoke/stdout/a.rb +0 -8
  292. data/smoke/stdout/a.rbs +0 -7
  293. data/smoke/stdout/test_expectations.yml +0 -1
  294. data/smoke/super/Steepfile +0 -6
  295. data/smoke/super/a.rb +0 -30
  296. data/smoke/super/a.rbs +0 -10
  297. data/smoke/super/test_expectations.yml +0 -69
  298. data/smoke/toplevel/Steepfile +0 -6
  299. data/smoke/toplevel/a.rb +0 -3
  300. data/smoke/toplevel/a.rbs +0 -3
  301. data/smoke/toplevel/test_expectations.yml +0 -15
  302. data/smoke/tsort/Steepfile +0 -7
  303. data/smoke/tsort/a.rb +0 -12
  304. data/smoke/tsort/test_expectations.yml +0 -1
  305. data/smoke/type_case/Steepfile +0 -6
  306. data/smoke/type_case/a.rb +0 -24
  307. data/smoke/type_case/test_expectations.yml +0 -58
  308. data/smoke/unexpected/Steepfile +0 -6
  309. data/smoke/unexpected/test_expectations.yml +0 -13
  310. data/smoke/unexpected/unexpected.rbs +0 -3
  311. data/smoke/yield/Steepfile +0 -6
  312. data/smoke/yield/a.rb +0 -15
  313. data/smoke/yield/b.rb +0 -6
  314. data/smoke/yield/test_expectations.yml +0 -88
@@ -0,0 +1,148 @@
1
+ module Steep
2
+ module AST
3
+ module Ignore
4
+ class BufferScanner
5
+ attr_reader :scanner, :location
6
+
7
+ def initialize(location)
8
+ @location = location
9
+
10
+ @scanner = StringScanner.new(location.source)
11
+ end
12
+
13
+ def offset
14
+ @location.start_pos
15
+ end
16
+
17
+ def charpos
18
+ scanner.charpos + offset
19
+ end
20
+
21
+ def scan(regexp)
22
+ if matched = scanner.scan(regexp)
23
+ end_pos = charpos()
24
+ begin_pos = end_pos - matched.size
25
+ RBS::Location.new(location.buffer, begin_pos, end_pos)
26
+ end
27
+ end
28
+
29
+ def skip(regexp)
30
+ scanner.skip(regexp)
31
+ end
32
+
33
+ def eos?
34
+ scanner.eos?
35
+ end
36
+ end
37
+
38
+ class IgnoreStart
39
+ attr_reader :comment, :location
40
+
41
+ def initialize(comment, location)
42
+ @comment = comment
43
+ @location = location
44
+ end
45
+
46
+ def line
47
+ location.start_line
48
+ end
49
+ end
50
+
51
+ class IgnoreEnd
52
+ attr_reader :comment, :location
53
+
54
+ def initialize(comment, location)
55
+ @comment = comment
56
+ @location = location
57
+ end
58
+
59
+ def line
60
+ location.start_line
61
+ end
62
+ end
63
+
64
+ class IgnoreLine
65
+ attr_reader :comment, :location, :raw_diagnostics
66
+
67
+ def initialize(comment, diagnostics, location)
68
+ @comment = comment
69
+ @raw_diagnostics = diagnostics
70
+ @location = location
71
+ end
72
+
73
+ def line
74
+ location.start_line
75
+ end
76
+
77
+ def ignored_diagnostics
78
+ if raw_diagnostics.empty?
79
+ return :all
80
+ end
81
+
82
+ if raw_diagnostics.size == 1 && raw_diagnostics[0].source == "all"
83
+ return :all
84
+ end
85
+
86
+ raw_diagnostics.map do |diagnostic|
87
+ name = diagnostic[:name].source
88
+ name.gsub(/\ARuby::/, "")
89
+ end
90
+ end
91
+ end
92
+
93
+ def self.parse(comment, buffer)
94
+ return unless comment.inline?
95
+
96
+ comment_location = RBS::Location.new(buffer, comment.loc.expression.begin_pos, comment.loc.expression.end_pos)
97
+ scanner = BufferScanner.new(comment_location)
98
+
99
+ scanner.scan(/#/)
100
+ scanner.skip(/\s*/)
101
+
102
+ begin_pos = comment.location.expression.begin_pos
103
+ end_pos = comment.location.expression.end_pos
104
+
105
+ case
106
+ when loc = scanner.scan(/steep:ignore:start\b/)
107
+ scanner.skip(/\s*/)
108
+ return unless scanner.eos?
109
+
110
+ IgnoreStart.new(comment, loc)
111
+ when loc = scanner.scan(/steep:ignore:end\b/)
112
+ scanner.skip(/\s*/)
113
+ return unless scanner.eos?
114
+
115
+ IgnoreEnd.new(comment, loc)
116
+ when keyword_loc = scanner.scan(/steep:ignore\b/)
117
+ # @type var diagnostics: IgnoreLine::diagnostics
118
+ diagnostics = []
119
+
120
+ scanner.skip(/\s*/)
121
+
122
+ while true
123
+ name = scanner.scan(/[A-Z]\w*/) or break
124
+ scanner.skip(/\s*/)
125
+ comma = scanner.scan(/,/)
126
+ scanner.skip(/\s*/)
127
+
128
+ diagnostic = RBS::Location.new(buffer, name.start_pos, comma&.end_pos || name.end_pos) #: IgnoreLine::diagnostic
129
+ diagnostic.add_required_child(:name, name.range)
130
+ diagnostic.add_optional_child(:following_comma, comma&.range)
131
+ diagnostics << diagnostic
132
+
133
+ break unless comma
134
+ end
135
+
136
+ return unless scanner.eos?
137
+
138
+ loc = RBS::Location.new(
139
+ buffer,
140
+ keyword_loc.start_pos,
141
+ diagnostics.last&.end_pos || keyword_loc.end_pos
142
+ )
143
+ IgnoreLine.new(comment, diagnostics, loc)
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
data/lib/steep/cli.rb CHANGED
@@ -63,6 +63,7 @@ module Steep
63
63
  def handle_logging_options(opts)
64
64
  opts.on("--log-level=LEVEL", "Specify log level: debug, info, warn, error, fatal") do |level|
65
65
  Steep.logger.level = level
66
+ Steep.ui_logger.level = level
66
67
  end
67
68
 
68
69
  opts.on("--log-output=PATH", "Print logs to given path") do |file|
@@ -71,6 +72,7 @@ module Steep
71
72
 
72
73
  opts.on("--verbose", "Set log level to debug") do
73
74
  Steep.logger.level = Logger::DEBUG
75
+ Steep.ui_logger.level = Logger::DEBUG
74
76
  end
75
77
  end
76
78
 
@@ -129,7 +131,7 @@ module Steep
129
131
  end.parse!(argv)
130
132
 
131
133
  setup_jobs_for_ci(command.jobs_option)
132
-
134
+
133
135
  command.command_line_patterns.push *argv
134
136
  end.run
135
137
  end
@@ -349,6 +351,9 @@ TEMPLATE
349
351
  opts.on("--index=INDEX") {|index| command.index = Integer(index) }
350
352
  end.parse!(argv)
351
353
 
354
+ # Disable any `ui_logger` output in workers
355
+ Steep.ui_logger.level = :fatal
356
+
352
357
  command.commandline_args.push(*argv)
353
358
  end.run
354
359
  end
@@ -997,6 +997,19 @@ module Steep
997
997
  end
998
998
  end
999
999
 
1000
+ class InvalidIgnoreComment < Base
1001
+ attr_reader :comment
1002
+
1003
+ def initialize(comment:)
1004
+ @comment = comment
1005
+ super(node: nil, location: comment.location.expression)
1006
+ end
1007
+
1008
+ def header_line
1009
+ "Invalid ignore comment"
1010
+ end
1011
+ end
1012
+
1000
1013
  ALL = ObjectSpace.each_object(Class).with_object([]) do |klass, array|
1001
1014
  if klass < Base
1002
1015
  array << klass
@@ -1028,6 +1041,7 @@ module Steep
1028
1041
  InsufficientKeywordArguments => :error,
1029
1042
  InsufficientPositionalArguments => :error,
1030
1043
  InsufficientTypeArgument => :hint,
1044
+ InvalidIgnoreComment => :warning,
1031
1045
  MethodArityMismatch => :error,
1032
1046
  MethodBodyTypeMismatch => :error,
1033
1047
  MethodDefinitionMissing => nil,
@@ -1086,6 +1100,7 @@ module Steep
1086
1100
  InsufficientKeywordArguments => :error,
1087
1101
  InsufficientPositionalArguments => :error,
1088
1102
  InsufficientTypeArgument => :error,
1103
+ InvalidIgnoreComment => :warning,
1089
1104
  MethodArityMismatch => :error,
1090
1105
  MethodBodyTypeMismatch => :error,
1091
1106
  MethodDefinitionMissing => :hint,
@@ -1144,6 +1159,7 @@ module Steep
1144
1159
  InsufficientKeywordArguments => :information,
1145
1160
  InsufficientPositionalArguments => :information,
1146
1161
  InsufficientTypeArgument => nil,
1162
+ InvalidIgnoreComment => :warning,
1147
1163
  MethodArityMismatch => :information,
1148
1164
  MethodBodyTypeMismatch => :warning,
1149
1165
  MethodDefinitionMissing => nil,
@@ -5,18 +5,29 @@ module Steep
5
5
  attr_accessor :steepfile
6
6
 
7
7
  def load_config(path: steepfile || Pathname("Steepfile"))
8
- raise "Cannot find a configuration at #{path}: `steep init` to scaffold" unless path.file?
9
-
10
- steep_file_path = path.absolute? ? path : Pathname.pwd + path
11
- Project.new(steepfile_path: steep_file_path).tap do |project|
12
- Project::DSL.parse(project, path.read, filename: path.to_s)
13
-
8
+ if path.file?
9
+ steep_file_path = path.absolute? ? path : Pathname.pwd + path
10
+ Project.new(steepfile_path: steep_file_path).tap do |project|
11
+ Project::DSL.parse(project, path.read, filename: path.to_s)
12
+ end
13
+ else
14
+ Steep.ui_logger.error { "Cannot find a configuration at #{path}: `steep init` to scaffold. Using current directory..." }
15
+ Project.new(steepfile_path: nil, base_dir: Pathname.pwd).tap do |project|
16
+ Project::DSL.new(project: project).target :'.' do
17
+ check '.'
18
+ signature '.'
19
+ end
20
+ end
21
+ end.tap do |project|
14
22
  project.targets.each do |target|
15
- if collection_lock = target.options.collection_lock
16
- begin
17
- collection_lock.check_rbs_availability!
18
- rescue RBS::Collection::Config::CollectionNotAvailable
19
- raise "Run `rbs collection install` to install type definitions"
23
+ case result = target.options.load_collection_lock
24
+ when nil, RBS::Collection::Config::Lockfile
25
+ # ok
26
+ else
27
+ if result == target.options.collection_config_path
28
+ Steep.ui_logger.error { "rbs-collection setup is broken: `#{result}` is missing" }
29
+ else
30
+ Steep.ui_logger.error { "Run `rbs collection install` to install type definitions" }
20
31
  end
21
32
  end
22
33
  end
@@ -240,5 +240,17 @@ module Steep
240
240
  end
241
241
  end
242
242
  end
243
+
244
+ def clone_node(node)
245
+ children = node.children.map do |child|
246
+ if child.is_a?(Parser::AST::Node)
247
+ clone_node(child)
248
+ else
249
+ child.dup
250
+ end
251
+ end
252
+
253
+ node.updated(nil, children)
254
+ end
243
255
  end
244
256
  end
@@ -14,9 +14,7 @@ module Steep
14
14
  attr_reader :project
15
15
  attr_reader :collection_config_path
16
16
 
17
- NONE = Object.new.freeze
18
-
19
- def initialize(name, sources: [], libraries: [], signatures: [], ignored_sources: [], repo_paths: [], code_diagnostics_config: {}, project: nil, collection_config_path: NONE)
17
+ def initialize(name, sources: [], libraries: [], signatures: [], ignored_sources: [], repo_paths: [], code_diagnostics_config: {}, project: nil, collection_config_path: nil)
20
18
  @name = name
21
19
  @sources = sources
22
20
  @libraries = libraries
@@ -27,14 +25,7 @@ module Steep
27
25
  @repo_paths = []
28
26
  @code_diagnostics_config = code_diagnostics_config
29
27
  @project = project
30
- @collection_config_path =
31
- case collection_config_path
32
- when NONE
33
- path = project&.absolute_path(RBS::Collection::Config::PATH)
34
- path&.exist? ? path : nil
35
- else
36
- collection_config_path
37
- end
28
+ @collection_config_path = collection_config_path
38
29
  end
39
30
 
40
31
  def initialize_copy(other)
@@ -135,8 +126,8 @@ module Steep
135
126
  end
136
127
 
137
128
  def stdlib_path(core_root:, stdlib_root:)
138
- @core_root = core_root ? Pathname(core_root) : core_root
139
- @stdlib_root = stdlib_root ? Pathname(stdlib_root) : stdlib_root
129
+ @core_root = Pathname(core_root)
130
+ @stdlib_root = Pathname(stdlib_root)
140
131
  end
141
132
 
142
133
  def repo_path(*paths)
@@ -160,7 +151,7 @@ module Steep
160
151
  end
161
152
 
162
153
  def disable_collection
163
- @collection_config_path = nil
154
+ @collection_config_path = false
164
155
  end
165
156
  end
166
157
 
@@ -206,12 +197,18 @@ module Steep
206
197
  source_pattern = Pattern.new(patterns: target.sources, ignores: target.ignored_sources, ext: ".rb")
207
198
  signature_pattern = Pattern.new(patterns: target.signatures, ext: ".rbs")
208
199
 
209
-
210
- if config_path = target.collection_config_path
211
- lockfile_path = RBS::Collection::Config.to_lockfile_path(config_path)
212
- content = YAML.load_file(lockfile_path.to_s)
213
- collection_lock = RBS::Collection::Config::Lockfile.from_lockfile(lockfile_path: lockfile_path, data: content)
214
- end
200
+ config_path =
201
+ case target.collection_config_path
202
+ when Pathname
203
+ target.collection_config_path
204
+ when nil
205
+ default = project.absolute_path(RBS::Collection::Config::PATH)
206
+ if default.file?
207
+ default
208
+ end
209
+ when false
210
+ nil
211
+ end
215
212
 
216
213
  Project::Target.new(
217
214
  name: target.name,
@@ -224,7 +221,7 @@ module Steep
224
221
  stdlib_root: target.stdlib_root,
225
222
  repo_paths: target.repo_paths
226
223
  )
227
- options.collection_lock = collection_lock
224
+ options.collection_config_path = config_path
228
225
  end,
229
226
  code_diagnostics_config: target.code_diagnostics_config
230
227
  ).tap do |target|
@@ -3,7 +3,7 @@ module Steep
3
3
  class Options
4
4
  PathOptions = _ = Struct.new(:core_root, :stdlib_root, :repo_paths, keyword_init: true) do
5
5
  # @implements PathOptions
6
-
6
+
7
7
  def customized_stdlib?
8
8
  stdlib_root != nil
9
9
  end
@@ -15,12 +15,49 @@ module Steep
15
15
 
16
16
  attr_reader :libraries
17
17
  attr_accessor :paths
18
- attr_accessor :collection_lock
18
+ attr_accessor :collection_config_path
19
19
 
20
20
  def initialize
21
21
  @paths = PathOptions.new(repo_paths: [])
22
22
  @libraries = []
23
23
  end
24
+
25
+ def collection_lock_path
26
+ if collection_config_path
27
+ RBS::Collection::Config.to_lockfile_path(collection_config_path)
28
+ end
29
+ end
30
+
31
+ def load_collection_lock(force: false)
32
+ @collection_lock = nil if force
33
+ @collection_lock ||=
34
+ if collection_config_path && collection_lock_path
35
+ case
36
+ when !collection_config_path.file?
37
+ collection_config_path
38
+ when !collection_lock_path.file?
39
+ collection_lock_path
40
+ else
41
+ begin
42
+ content = YAML.load_file(collection_lock_path)
43
+ lock_file = RBS::Collection::Config::Lockfile.from_lockfile(lockfile_path: collection_lock_path, data: content)
44
+ lock_file.check_rbs_availability!
45
+ lock_file
46
+ rescue YAML::SyntaxError, RBS::Collection::Config::CollectionNotAvailable => exn
47
+ exn
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ def collection_lock
54
+ case config = load_collection_lock()
55
+ when RBS::Collection::Config::Lockfile
56
+ config
57
+ else
58
+ nil
59
+ end
60
+ end
24
61
  end
25
62
  end
26
63
  end
data/lib/steep/project.rb CHANGED
@@ -2,18 +2,22 @@ module Steep
2
2
  class Project
3
3
  attr_reader :targets
4
4
  attr_reader :steepfile_path
5
+ attr_reader :base_dir
5
6
 
6
- def initialize(steepfile_path:)
7
+ def initialize(steepfile_path:, base_dir: nil)
7
8
  @targets = []
8
9
  @steepfile_path = steepfile_path
9
-
10
- unless steepfile_path.absolute?
11
- raise "Project#initialize(steepfile_path:): steepfile_path should be absolute path"
10
+ @base_dir = if base_dir
11
+ base_dir
12
+ elsif steepfile_path
13
+ steepfile_path.parent
14
+ else
15
+ raise ArgumentError, "Project#initialize(base_dir:): neither base_dir nor steepfile_path given"
12
16
  end
13
- end
14
17
 
15
- def base_dir
16
- steepfile_path.parent
18
+ if steepfile_path and !steepfile_path.absolute?
19
+ raise ArgumentError, "Project#initialize(steepfile_path:): steepfile_path should be absolute path"
20
+ end
17
21
  end
18
22
 
19
23
  def relative_path(path)
@@ -5,13 +5,13 @@ module Steep
5
5
  attr_reader :buffered_changes
6
6
 
7
7
  def push_buffer
8
- @mutex.synchronize do
8
+ mutex.synchronize do
9
9
  yield buffered_changes
10
10
  end
11
11
  end
12
12
 
13
13
  def pop_buffer
14
- changes = @mutex.synchronize do
14
+ changes = mutex.synchronize do
15
15
  copy = buffered_changes.dup
16
16
  buffered_changes.clear
17
17
  copy
@@ -10,14 +10,15 @@ module Steep
10
10
 
11
11
  LSP = LanguageServer::Protocol
12
12
 
13
- attr_reader :service
13
+ attr_reader :service, :mutex
14
14
 
15
15
  def initialize(project:, reader:, writer:, queue: Queue.new)
16
16
  super(project: project, reader: reader, writer: writer)
17
17
  @queue = queue
18
- @service = Services::TypeCheckService.new(project: project)
19
18
  @mutex = Mutex.new
19
+ @service = Services::TypeCheckService.new(project: project)
20
20
  @buffered_changes = {}
21
+ @last_job_mutex = Mutex.new
21
22
  end
22
23
 
23
24
  def handle_job(job)
@@ -33,25 +34,61 @@ module Steep
33
34
  when ApplyChangeJob
34
35
  # nop
35
36
  when HoverJob
36
- writer.write({ id: job.id, result: process_hover(job) })
37
+ writer.write(
38
+ {
39
+ id: job.id,
40
+ result: process_latest_job(job) { process_hover(job) }
41
+ }
42
+ )
37
43
  when CompletionJob
38
- writer.write({ id: job.id, result: process_completion(job) })
44
+ writer.write(
45
+ {
46
+ id: job.id,
47
+ result: process_latest_job(job) { process_completion(job) }
48
+ }
49
+ )
39
50
  when SignatureHelpJob
40
- writer.write({ id: job.id, result: process_signature_help(job) })
51
+ writer.write(
52
+ {
53
+ id: job.id,
54
+ result: process_latest_job(job) { process_signature_help(job) }
55
+ }
56
+ )
57
+ end
58
+ end
59
+ end
60
+
61
+ def process_latest_job(job)
62
+ @last_job_mutex.synchronize do
63
+ unless job.equal?(@last_job)
64
+ Steep.logger.debug { "Skipping interaction job: latest_job=#{@last_job.class}, skipped_job#{job.class}" }
65
+ return
66
+ end
67
+ @last_job = nil
68
+ end
69
+
70
+ yield
71
+ end
72
+
73
+ def queue_job(job)
74
+ @last_job_mutex.synchronize do
75
+ unless job.is_a?(ApplyChangeJob)
76
+ @last_job = job
41
77
  end
42
78
  end
79
+ queue << job
43
80
  end
44
81
 
45
82
  def handle_request(request)
46
83
  case request[:method]
47
84
  when "initialize"
48
85
  load_files(project: project, commandline_args: [])
49
- queue << ApplyChangeJob.new
86
+ queue_job ApplyChangeJob.new
50
87
  writer.write({ id: request[:id], result: nil })
51
88
 
52
89
  when "textDocument/didChange"
53
90
  collect_changes(request)
54
- queue << ApplyChangeJob.new
91
+ queue_job ApplyChangeJob.new
55
92
 
56
93
  when "textDocument/hover"
57
94
  id = request[:id]
@@ -60,7 +97,7 @@ module Steep
60
97
  line = request[:params][:position][:line]+1
61
98
  column = request[:params][:position][:character]
62
99
 
63
- queue << HoverJob.new(id: id, path: path, line: line, column: column)
100
+ queue_job HoverJob.new(id: id, path: path, line: line, column: column)
64
101
 
65
102
  when "textDocument/completion"
66
103
  id = request[:id]
@@ -71,14 +108,14 @@ module Steep
71
108
  line, column = params[:position].yield_self {|hash| [hash[:line]+1, hash[:character]] }
72
109
  trigger = params.dig(:context, :triggerCharacter)
73
110
 
74
- queue << CompletionJob.new(id: id, path: path, line: line, column: column, trigger: trigger)
111
+ queue_job CompletionJob.new(id: id, path: path, line: line, column: column, trigger: trigger)
75
112
  when "textDocument/signatureHelp"
76
113
  id = request[:id]
77
114
  params = request[:params]
78
115
  path = project.relative_path(PathHelper.to_pathname!(params[:textDocument][:uri]))
79
116
  line, column = params[:position].yield_self {|hash| [hash[:line]+1, hash[:character]] }
80
117
 
81
- queue << SignatureHelpJob.new(id: id, path: path, line: line, column: column)
118
+ queue_job SignatureHelpJob.new(id: id, path: path, line: line, column: column)
82
119
  end
83
120
  end
84
121
 
@@ -394,6 +431,17 @@ module Steep
394
431
  new_text: item.relative_type_name.to_s
395
432
  )
396
433
  )
434
+ when Services::CompletionProvider::TextItem
435
+ LSP::Interface::CompletionItem.new(
436
+ label: item.label,
437
+ label_details: item.help_text && LSP::Interface::CompletionItemLabelDetails.new(description: item.help_text),
438
+ kind: LSP::Constant::CompletionItemKind::SNIPPET,
439
+ insert_text_format: LSP::Constant::InsertTextFormat::SNIPPET,
440
+ text_edit: LSP::Interface::TextEdit.new(
441
+ range: range,
442
+ new_text: item.text
443
+ )
444
+ )
397
445
  end
398
446
  end
399
447
 
@@ -402,7 +450,9 @@ module Steep
402
450
  if target = project.target_for_source_path(job.path)
403
451
  file = service.source_files[job.path] or return
404
452
  subtyping = service.signature_services[target.name].current_subtyping or return
405
- source = Source.parse(file.content, path: file.path, factory: subtyping.factory)
453
+ source =
454
+ Source.parse(file.content, path: file.path, factory: subtyping.factory)
455
+ .without_unrelated_defs(line: job.line, column: job.column)
406
456
 
407
457
  provider = Services::SignatureHelpProvider.new(source: source, subtyping: subtyping)
408
458
 
@@ -64,6 +64,7 @@ module Steep
64
64
 
65
65
  pid = fork do
66
66
  Process.setpgid(0, 0)
67
+ Steep.ui_logger.level = :fatal
67
68
  stdin_out.close
68
69
  stdout_in.close
69
70
  worker.run()
@@ -92,7 +93,8 @@ module Steep
92
93
  end
93
94
 
94
95
  def self.spawn_worker(type, name:, steepfile:, steep_command:, index:, delay_shutdown:, patterns:)
95
- args = ["--name=#{name}", "--steepfile=#{steepfile}"]
96
+ args = ["--name=#{name}"]
97
+ args << "--steepfile=#{steepfile}" if steepfile
96
98
  args << (%w(debug info warn error fatal unknown)[Steep.logger.level].yield_self {|log_level| "--log-level=#{log_level}" })
97
99
 
98
100
  if Steep.log_output.is_a?(String)