cosmicgraph 0.49.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (259) hide show
  1. checksums.yaml +7 -0
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/workflows/rspec.yml +41 -0
  4. data/.gitignore +9 -0
  5. data/.rspec +2 -0
  6. data/.yardopts +2 -0
  7. data/CHANGELOG.md +1150 -0
  8. data/Gemfile +7 -0
  9. data/LICENSE +21 -0
  10. data/README.md +136 -0
  11. data/Rakefile +25 -0
  12. data/SPONSORS.md +15 -0
  13. data/bin/solargraph +5 -0
  14. data/cosmicgraph.gemspec +44 -0
  15. data/lib/.rubocop.yml +22 -0
  16. data/lib/solargraph/api_map/bundler_methods.rb +22 -0
  17. data/lib/solargraph/api_map/cache.rb +70 -0
  18. data/lib/solargraph/api_map/source_to_yard.rb +81 -0
  19. data/lib/solargraph/api_map/store.rb +268 -0
  20. data/lib/solargraph/api_map.rb +704 -0
  21. data/lib/solargraph/bench.rb +27 -0
  22. data/lib/solargraph/cache.rb +51 -0
  23. data/lib/solargraph/complex_type/type_methods.rb +134 -0
  24. data/lib/solargraph/complex_type/unique_type.rb +132 -0
  25. data/lib/solargraph/complex_type.rb +254 -0
  26. data/lib/solargraph/convention/base.rb +33 -0
  27. data/lib/solargraph/convention/gemfile.rb +15 -0
  28. data/lib/solargraph/convention/gemspec.rb +22 -0
  29. data/lib/solargraph/convention/rakefile.rb +17 -0
  30. data/lib/solargraph/convention/rspec.rb +30 -0
  31. data/lib/solargraph/convention.rb +49 -0
  32. data/lib/solargraph/converters/dd.rb +12 -0
  33. data/lib/solargraph/converters/dl.rb +12 -0
  34. data/lib/solargraph/converters/dt.rb +12 -0
  35. data/lib/solargraph/converters/misc.rb +1 -0
  36. data/lib/solargraph/diagnostics/base.rb +29 -0
  37. data/lib/solargraph/diagnostics/require_not_found.rb +53 -0
  38. data/lib/solargraph/diagnostics/rubocop.rb +112 -0
  39. data/lib/solargraph/diagnostics/rubocop_helpers.rb +65 -0
  40. data/lib/solargraph/diagnostics/severities.rb +15 -0
  41. data/lib/solargraph/diagnostics/type_check.rb +54 -0
  42. data/lib/solargraph/diagnostics/update_errors.rb +41 -0
  43. data/lib/solargraph/diagnostics.rb +55 -0
  44. data/lib/solargraph/documentor.rb +76 -0
  45. data/lib/solargraph/environ.rb +45 -0
  46. data/lib/solargraph/language_server/completion_item_kinds.rb +35 -0
  47. data/lib/solargraph/language_server/error_codes.rb +20 -0
  48. data/lib/solargraph/language_server/host/cataloger.rb +56 -0
  49. data/lib/solargraph/language_server/host/diagnoser.rb +89 -0
  50. data/lib/solargraph/language_server/host/dispatch.rb +111 -0
  51. data/lib/solargraph/language_server/host/message_worker.rb +59 -0
  52. data/lib/solargraph/language_server/host/sources.rb +156 -0
  53. data/lib/solargraph/language_server/host.rb +869 -0
  54. data/lib/solargraph/language_server/message/base.rb +89 -0
  55. data/lib/solargraph/language_server/message/cancel_request.rb +13 -0
  56. data/lib/solargraph/language_server/message/client/register_capability.rb +15 -0
  57. data/lib/solargraph/language_server/message/client.rb +11 -0
  58. data/lib/solargraph/language_server/message/completion_item/resolve.rb +58 -0
  59. data/lib/solargraph/language_server/message/completion_item.rb +11 -0
  60. data/lib/solargraph/language_server/message/exit_notification.rb +13 -0
  61. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +100 -0
  62. data/lib/solargraph/language_server/message/extended/document.rb +20 -0
  63. data/lib/solargraph/language_server/message/extended/document_gems.rb +32 -0
  64. data/lib/solargraph/language_server/message/extended/download_core.rb +19 -0
  65. data/lib/solargraph/language_server/message/extended/environment.rb +25 -0
  66. data/lib/solargraph/language_server/message/extended/search.rb +20 -0
  67. data/lib/solargraph/language_server/message/extended.rb +21 -0
  68. data/lib/solargraph/language_server/message/initialize.rb +164 -0
  69. data/lib/solargraph/language_server/message/initialized.rb +27 -0
  70. data/lib/solargraph/language_server/message/method_not_found.rb +16 -0
  71. data/lib/solargraph/language_server/message/method_not_implemented.rb +14 -0
  72. data/lib/solargraph/language_server/message/shutdown.rb +13 -0
  73. data/lib/solargraph/language_server/message/text_document/base.rb +19 -0
  74. data/lib/solargraph/language_server/message/text_document/code_action.rb +17 -0
  75. data/lib/solargraph/language_server/message/text_document/completion.rb +59 -0
  76. data/lib/solargraph/language_server/message/text_document/definition.rb +38 -0
  77. data/lib/solargraph/language_server/message/text_document/did_change.rb +15 -0
  78. data/lib/solargraph/language_server/message/text_document/did_close.rb +15 -0
  79. data/lib/solargraph/language_server/message/text_document/did_open.rb +15 -0
  80. data/lib/solargraph/language_server/message/text_document/did_save.rb +17 -0
  81. data/lib/solargraph/language_server/message/text_document/document_highlight.rb +16 -0
  82. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +23 -0
  83. data/lib/solargraph/language_server/message/text_document/folding_range.rb +26 -0
  84. data/lib/solargraph/language_server/message/text_document/formatting.rb +126 -0
  85. data/lib/solargraph/language_server/message/text_document/hover.rb +56 -0
  86. data/lib/solargraph/language_server/message/text_document/on_type_formatting.rb +34 -0
  87. data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +11 -0
  88. data/lib/solargraph/language_server/message/text_document/references.rb +16 -0
  89. data/lib/solargraph/language_server/message/text_document/rename.rb +19 -0
  90. data/lib/solargraph/language_server/message/text_document/signature_help.rb +24 -0
  91. data/lib/solargraph/language_server/message/text_document.rb +28 -0
  92. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +30 -0
  93. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +40 -0
  94. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +24 -0
  95. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +23 -0
  96. data/lib/solargraph/language_server/message/workspace.rb +14 -0
  97. data/lib/solargraph/language_server/message.rb +93 -0
  98. data/lib/solargraph/language_server/message_types.rb +14 -0
  99. data/lib/solargraph/language_server/request.rb +24 -0
  100. data/lib/solargraph/language_server/symbol_kinds.rb +36 -0
  101. data/lib/solargraph/language_server/transport/adapter.rb +53 -0
  102. data/lib/solargraph/language_server/transport/data_reader.rb +72 -0
  103. data/lib/solargraph/language_server/transport.rb +13 -0
  104. data/lib/solargraph/language_server/uri_helpers.rb +49 -0
  105. data/lib/solargraph/language_server.rb +19 -0
  106. data/lib/solargraph/library.rb +547 -0
  107. data/lib/solargraph/location.rb +37 -0
  108. data/lib/solargraph/logging.rb +27 -0
  109. data/lib/solargraph/page.rb +83 -0
  110. data/lib/solargraph/parser/comment_ripper.rb +52 -0
  111. data/lib/solargraph/parser/legacy/class_methods.rb +135 -0
  112. data/lib/solargraph/parser/legacy/flawed_builder.rb +16 -0
  113. data/lib/solargraph/parser/legacy/node_chainer.rb +148 -0
  114. data/lib/solargraph/parser/legacy/node_methods.rb +325 -0
  115. data/lib/solargraph/parser/legacy/node_processors/alias_node.rb +23 -0
  116. data/lib/solargraph/parser/legacy/node_processors/args_node.rb +35 -0
  117. data/lib/solargraph/parser/legacy/node_processors/begin_node.rb +15 -0
  118. data/lib/solargraph/parser/legacy/node_processors/block_node.rb +42 -0
  119. data/lib/solargraph/parser/legacy/node_processors/casgn_node.rb +35 -0
  120. data/lib/solargraph/parser/legacy/node_processors/cvasgn_node.rb +23 -0
  121. data/lib/solargraph/parser/legacy/node_processors/def_node.rb +63 -0
  122. data/lib/solargraph/parser/legacy/node_processors/defs_node.rb +36 -0
  123. data/lib/solargraph/parser/legacy/node_processors/gvasgn_node.rb +23 -0
  124. data/lib/solargraph/parser/legacy/node_processors/ivasgn_node.rb +38 -0
  125. data/lib/solargraph/parser/legacy/node_processors/lvasgn_node.rb +28 -0
  126. data/lib/solargraph/parser/legacy/node_processors/namespace_node.rb +39 -0
  127. data/lib/solargraph/parser/legacy/node_processors/orasgn_node.rb +16 -0
  128. data/lib/solargraph/parser/legacy/node_processors/resbody_node.rb +36 -0
  129. data/lib/solargraph/parser/legacy/node_processors/sclass_node.rb +42 -0
  130. data/lib/solargraph/parser/legacy/node_processors/send_node.rb +257 -0
  131. data/lib/solargraph/parser/legacy/node_processors/sym_node.rb +18 -0
  132. data/lib/solargraph/parser/legacy/node_processors.rb +54 -0
  133. data/lib/solargraph/parser/legacy.rb +12 -0
  134. data/lib/solargraph/parser/node_methods.rb +43 -0
  135. data/lib/solargraph/parser/node_processor/base.rb +77 -0
  136. data/lib/solargraph/parser/node_processor.rb +43 -0
  137. data/lib/solargraph/parser/region.rb +66 -0
  138. data/lib/solargraph/parser/rubyvm/class_methods.rb +149 -0
  139. data/lib/solargraph/parser/rubyvm/node_chainer.rb +160 -0
  140. data/lib/solargraph/parser/rubyvm/node_methods.rb +315 -0
  141. data/lib/solargraph/parser/rubyvm/node_processors/alias_node.rb +23 -0
  142. data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +85 -0
  143. data/lib/solargraph/parser/rubyvm/node_processors/begin_node.rb +15 -0
  144. data/lib/solargraph/parser/rubyvm/node_processors/block_node.rb +42 -0
  145. data/lib/solargraph/parser/rubyvm/node_processors/casgn_node.rb +33 -0
  146. data/lib/solargraph/parser/rubyvm/node_processors/cvasgn_node.rb +23 -0
  147. data/lib/solargraph/parser/rubyvm/node_processors/def_node.rb +75 -0
  148. data/lib/solargraph/parser/rubyvm/node_processors/defs_node.rb +68 -0
  149. data/lib/solargraph/parser/rubyvm/node_processors/gvasgn_node.rb +23 -0
  150. data/lib/solargraph/parser/rubyvm/node_processors/ivasgn_node.rb +38 -0
  151. data/lib/solargraph/parser/rubyvm/node_processors/kw_arg_node.rb +39 -0
  152. data/lib/solargraph/parser/rubyvm/node_processors/lit_node.rb +20 -0
  153. data/lib/solargraph/parser/rubyvm/node_processors/lvasgn_node.rb +27 -0
  154. data/lib/solargraph/parser/rubyvm/node_processors/namespace_node.rb +39 -0
  155. data/lib/solargraph/parser/rubyvm/node_processors/opt_arg_node.rb +26 -0
  156. data/lib/solargraph/parser/rubyvm/node_processors/orasgn_node.rb +15 -0
  157. data/lib/solargraph/parser/rubyvm/node_processors/resbody_node.rb +45 -0
  158. data/lib/solargraph/parser/rubyvm/node_processors/sclass_node.rb +32 -0
  159. data/lib/solargraph/parser/rubyvm/node_processors/scope_node.rb +15 -0
  160. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +279 -0
  161. data/lib/solargraph/parser/rubyvm/node_processors/sym_node.rb +18 -0
  162. data/lib/solargraph/parser/rubyvm/node_processors.rb +63 -0
  163. data/lib/solargraph/parser/rubyvm/node_wrapper.rb +47 -0
  164. data/lib/solargraph/parser/rubyvm.rb +40 -0
  165. data/lib/solargraph/parser/snippet.rb +13 -0
  166. data/lib/solargraph/parser.rb +26 -0
  167. data/lib/solargraph/pin/base.rb +299 -0
  168. data/lib/solargraph/pin/base_variable.rb +84 -0
  169. data/lib/solargraph/pin/block.rb +73 -0
  170. data/lib/solargraph/pin/class_variable.rb +8 -0
  171. data/lib/solargraph/pin/closure.rb +37 -0
  172. data/lib/solargraph/pin/common.rb +70 -0
  173. data/lib/solargraph/pin/constant.rb +43 -0
  174. data/lib/solargraph/pin/conversions.rb +92 -0
  175. data/lib/solargraph/pin/documenting.rb +105 -0
  176. data/lib/solargraph/pin/duck_method.rb +16 -0
  177. data/lib/solargraph/pin/global_variable.rb +8 -0
  178. data/lib/solargraph/pin/instance_variable.rb +30 -0
  179. data/lib/solargraph/pin/keyword.rb +15 -0
  180. data/lib/solargraph/pin/keyword_param.rb +8 -0
  181. data/lib/solargraph/pin/local_variable.rb +55 -0
  182. data/lib/solargraph/pin/method.rb +335 -0
  183. data/lib/solargraph/pin/method_alias.rb +31 -0
  184. data/lib/solargraph/pin/namespace.rb +94 -0
  185. data/lib/solargraph/pin/parameter.rb +206 -0
  186. data/lib/solargraph/pin/proxy_type.rb +29 -0
  187. data/lib/solargraph/pin/reference/extend.rb +10 -0
  188. data/lib/solargraph/pin/reference/include.rb +10 -0
  189. data/lib/solargraph/pin/reference/override.rb +29 -0
  190. data/lib/solargraph/pin/reference/prepend.rb +10 -0
  191. data/lib/solargraph/pin/reference/require.rb +14 -0
  192. data/lib/solargraph/pin/reference/superclass.rb +10 -0
  193. data/lib/solargraph/pin/reference.rb +14 -0
  194. data/lib/solargraph/pin/search.rb +56 -0
  195. data/lib/solargraph/pin/signature.rb +23 -0
  196. data/lib/solargraph/pin/singleton.rb +11 -0
  197. data/lib/solargraph/pin/symbol.rb +47 -0
  198. data/lib/solargraph/pin.rb +38 -0
  199. data/lib/solargraph/position.rb +100 -0
  200. data/lib/solargraph/range.rb +95 -0
  201. data/lib/solargraph/rbs_map/conversions.rb +394 -0
  202. data/lib/solargraph/rbs_map/core_fills.rb +61 -0
  203. data/lib/solargraph/rbs_map/core_map.rb +38 -0
  204. data/lib/solargraph/rbs_map/core_signs.rb +33 -0
  205. data/lib/solargraph/rbs_map/stdlib_map.rb +36 -0
  206. data/lib/solargraph/rbs_map.rb +73 -0
  207. data/lib/solargraph/server_methods.rb +16 -0
  208. data/lib/solargraph/shell.rb +234 -0
  209. data/lib/solargraph/source/chain/block_variable.rb +13 -0
  210. data/lib/solargraph/source/chain/call.rb +215 -0
  211. data/lib/solargraph/source/chain/class_variable.rb +13 -0
  212. data/lib/solargraph/source/chain/constant.rb +75 -0
  213. data/lib/solargraph/source/chain/global_variable.rb +13 -0
  214. data/lib/solargraph/source/chain/hash.rb +28 -0
  215. data/lib/solargraph/source/chain/head.rb +19 -0
  216. data/lib/solargraph/source/chain/instance_variable.rb +13 -0
  217. data/lib/solargraph/source/chain/link.rb +71 -0
  218. data/lib/solargraph/source/chain/literal.rb +23 -0
  219. data/lib/solargraph/source/chain/or.rb +23 -0
  220. data/lib/solargraph/source/chain/q_call.rb +11 -0
  221. data/lib/solargraph/source/chain/variable.rb +13 -0
  222. data/lib/solargraph/source/chain/z_super.rb +30 -0
  223. data/lib/solargraph/source/chain.rb +179 -0
  224. data/lib/solargraph/source/change.rb +79 -0
  225. data/lib/solargraph/source/cursor.rb +164 -0
  226. data/lib/solargraph/source/encoding_fixes.rb +23 -0
  227. data/lib/solargraph/source/source_chainer.rb +191 -0
  228. data/lib/solargraph/source/updater.rb +54 -0
  229. data/lib/solargraph/source.rb +522 -0
  230. data/lib/solargraph/source_map/clip.rb +229 -0
  231. data/lib/solargraph/source_map/completion.rb +23 -0
  232. data/lib/solargraph/source_map/mapper.rb +241 -0
  233. data/lib/solargraph/source_map.rb +180 -0
  234. data/lib/solargraph/type_checker/checks.rb +112 -0
  235. data/lib/solargraph/type_checker/param_def.rb +35 -0
  236. data/lib/solargraph/type_checker/problem.rb +32 -0
  237. data/lib/solargraph/type_checker/rules.rb +57 -0
  238. data/lib/solargraph/type_checker.rb +549 -0
  239. data/lib/solargraph/version.rb +5 -0
  240. data/lib/solargraph/views/_method.erb +62 -0
  241. data/lib/solargraph/views/_name_type_tag.erb +10 -0
  242. data/lib/solargraph/views/_namespace.erb +24 -0
  243. data/lib/solargraph/views/document.erb +23 -0
  244. data/lib/solargraph/views/environment.erb +58 -0
  245. data/lib/solargraph/views/layout.erb +44 -0
  246. data/lib/solargraph/views/search.erb +11 -0
  247. data/lib/solargraph/workspace/config.rb +231 -0
  248. data/lib/solargraph/workspace.rb +212 -0
  249. data/lib/solargraph/yard_map/cache.rb +19 -0
  250. data/lib/solargraph/yard_map/helpers.rb +16 -0
  251. data/lib/solargraph/yard_map/mapper/to_constant.rb +25 -0
  252. data/lib/solargraph/yard_map/mapper/to_method.rb +81 -0
  253. data/lib/solargraph/yard_map/mapper/to_namespace.rb +27 -0
  254. data/lib/solargraph/yard_map/mapper.rb +77 -0
  255. data/lib/solargraph/yard_map/to_method.rb +79 -0
  256. data/lib/solargraph/yard_map.rb +301 -0
  257. data/lib/solargraph.rb +69 -0
  258. data/lib/yard-solargraph.rb +33 -0
  259. metadata +587 -0
@@ -0,0 +1,549 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ # A static analysis tool for validating data types.
5
+ #
6
+ class TypeChecker
7
+ autoload :Problem, 'solargraph/type_checker/problem'
8
+ autoload :ParamDef, 'solargraph/type_checker/param_def'
9
+ autoload :Rules, 'solargraph/type_checker/rules'
10
+ autoload :Checks, 'solargraph/type_checker/checks'
11
+
12
+ include Checks
13
+ include Parser::NodeMethods
14
+
15
+ # @return [String]
16
+ attr_reader :filename
17
+
18
+ # @return [Rules]
19
+ attr_reader :rules
20
+
21
+ # @return [ApiMap]
22
+ attr_reader :api_map
23
+
24
+ # @param filename [String]
25
+ # @param api_map [ApiMap]
26
+ # @param level [Symbol]
27
+ def initialize filename, api_map: nil, level: :normal
28
+ @filename = filename
29
+ # @todo Smarter directory resolution
30
+ @api_map = api_map || Solargraph::ApiMap.load(File.dirname(filename))
31
+ @rules = Rules.new(level)
32
+ @marked_ranges = []
33
+ end
34
+
35
+ # @return [SourceMap]
36
+ def source_map
37
+ @source_map ||= api_map.source_map(filename)
38
+ end
39
+
40
+ # @return [Array<Problem>]
41
+ def problems
42
+ @problems ||= begin
43
+ without_ignored(
44
+ method_tag_problems
45
+ .concat variable_type_tag_problems
46
+ .concat const_problems
47
+ .concat call_problems
48
+ )
49
+ end
50
+ end
51
+
52
+ class << self
53
+ # @param filename [String]
54
+ # @return [self]
55
+ def load filename, level = :normal
56
+ source = Solargraph::Source.load(filename)
57
+ api_map = Solargraph::ApiMap.new
58
+ api_map.map(source)
59
+ new(filename, api_map: api_map, level: level)
60
+ end
61
+
62
+ # @param code [String]
63
+ # @param filename [String, nil]
64
+ # @return [self]
65
+ def load_string code, filename = nil, level = :normal
66
+ source = Solargraph::Source.load_string(code, filename)
67
+ api_map = Solargraph::ApiMap.new
68
+ api_map.map(source)
69
+ new(filename, api_map: api_map, level: level)
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ # @return [Array<Problem>]
76
+ def method_tag_problems
77
+ result = []
78
+ # @param pin [Pin::Method]
79
+ source_map.pins_by_class(Pin::Method).each do |pin|
80
+ result.concat method_return_type_problems_for(pin)
81
+ result.concat method_param_type_problems_for(pin)
82
+ end
83
+ result
84
+ end
85
+
86
+ # @param pin [Pin::Method]
87
+ # @return [Array<Problem>]
88
+ def method_return_type_problems_for pin
89
+ return [] if pin.is_a?(Pin::MethodAlias)
90
+ result = []
91
+ declared = pin.typify(api_map).self_to(pin.full_context.namespace)
92
+ if declared.undefined?
93
+ if pin.return_type.undefined? && rules.require_type_tags?
94
+ result.push Problem.new(pin.location, "Missing @return tag for #{pin.path}", pin: pin)
95
+ elsif pin.return_type.defined? && !resolved_constant?(pin)
96
+ result.push Problem.new(pin.location, "Unresolved return type #{pin.return_type} for #{pin.path}", pin: pin)
97
+ elsif rules.must_tag_or_infer? && pin.probe(api_map).undefined?
98
+ result.push Problem.new(pin.location, "Untyped method #{pin.path} could not be inferred")
99
+ end
100
+ elsif rules.validate_tags?
101
+ unless pin.node.nil? || declared.void? || virtual_pin?(pin) || abstract?(pin)
102
+ inferred = pin.probe(api_map).self_to(pin.full_context.namespace)
103
+ if inferred.undefined?
104
+ unless rules.ignore_all_undefined? || external?(pin)
105
+ result.push Problem.new(pin.location, "#{pin.path} return type could not be inferred", pin: pin)
106
+ end
107
+ else
108
+ unless (rules.rank > 1 ? all_types_match?(api_map, inferred, declared) : any_types_match?(api_map, declared, inferred))
109
+ result.push Problem.new(pin.location, "Declared return type #{declared} does not match inferred type #{inferred} for #{pin.path}", pin: pin)
110
+ end
111
+ end
112
+ end
113
+ end
114
+ result
115
+ end
116
+
117
+ # @todo This is not optimal. A better solution would probably be to mix
118
+ # namespace alias into types at the ApiMap level.
119
+ #
120
+ # @param pin [Pin::Base]
121
+ # @return [Boolean]
122
+ def resolved_constant? pin
123
+ return true if pin.typify(api_map).defined?
124
+ api_map.get_constants('', *pin.closure.gates)
125
+ .select { |p| p.name == pin.return_type.namespace }
126
+ .any? { |p| p.infer(api_map).defined? }
127
+ end
128
+
129
+ def virtual_pin? pin
130
+ pin.location && source_map.source.comment_at?(pin.location.range.ending)
131
+ end
132
+
133
+ # @param pin [Pin::Method]
134
+ # @return [Array<Problem>]
135
+ def method_param_type_problems_for pin
136
+ stack = api_map.get_method_stack(pin.namespace, pin.name, scope: pin.scope)
137
+ params = first_param_hash(stack)
138
+ result = []
139
+ if rules.require_type_tags?
140
+ pin.signatures.each do |sig|
141
+ sig.parameters.each do |par|
142
+ break if par.decl == :restarg || par.decl == :kwrestarg || par.decl == :blockarg
143
+ unless params[par.name]
144
+ result.push Problem.new(pin.location, "Missing @param tag for #{par.name} on #{pin.path}", pin: pin)
145
+ end
146
+ end
147
+ end
148
+ end
149
+ params.each_pair do |name, data|
150
+ type = data[:qualified]
151
+ if type.undefined?
152
+ result.push Problem.new(pin.location, "Unresolved type #{data[:tagged]} for #{name} param on #{pin.path}", pin: pin)
153
+ end
154
+ end
155
+ result
156
+ end
157
+
158
+ def ignored_pins
159
+ @ignored_pins ||= []
160
+ end
161
+
162
+ # @return [Array<Problem>]
163
+ def variable_type_tag_problems
164
+ result = []
165
+ all_variables.each do |pin|
166
+ if pin.return_type.defined?
167
+ declared = pin.typify(api_map)
168
+ next if declared.duck_type?
169
+ if declared.defined?
170
+ if rules.validate_tags?
171
+ inferred = pin.probe(api_map)
172
+ if inferred.undefined?
173
+ next if rules.ignore_all_undefined?
174
+ if declared_externally?(pin)
175
+ ignored_pins.push pin
176
+ else
177
+ result.push Problem.new(pin.location, "Variable type could not be inferred for #{pin.name}", pin: pin)
178
+ end
179
+ else
180
+ unless any_types_match?(api_map, declared, inferred)
181
+ result.push Problem.new(pin.location, "Declared type #{declared} does not match inferred type #{inferred} for variable #{pin.name}", pin: pin)
182
+ end
183
+ end
184
+ elsif declared_externally?(pin)
185
+ ignored_pins.push pin
186
+ end
187
+ elsif !pin.is_a?(Pin::Parameter) && !resolved_constant?(pin)
188
+ result.push Problem.new(pin.location, "Unresolved type #{pin.return_type} for variable #{pin.name}", pin: pin)
189
+ end
190
+ else
191
+ inferred = pin.probe(api_map)
192
+ if inferred.undefined? && declared_externally?(pin)
193
+ ignored_pins.push pin
194
+ end
195
+ end
196
+ end
197
+ result
198
+ end
199
+
200
+ # @return [Array<Pin::BaseVariable>]
201
+ def all_variables
202
+ source_map.pins_by_class(Pin::BaseVariable) + source_map.locals.select { |pin| pin.is_a?(Pin::LocalVariable) }
203
+ end
204
+
205
+ def const_problems
206
+ return [] unless rules.validate_consts?
207
+ result = []
208
+ Solargraph::Parser::NodeMethods.const_nodes_from(source_map.source.node).each do |const|
209
+ rng = Solargraph::Range.from_node(const)
210
+ chain = Solargraph::Parser.chain(const, filename)
211
+ block_pin = source_map.locate_block_pin(rng.start.line, rng.start.column)
212
+ location = Location.new(filename, rng)
213
+ locals = source_map.locals_at(location)
214
+ pins = chain.define(api_map, block_pin, locals)
215
+ if pins.empty?
216
+ result.push Problem.new(location, "Unresolved constant #{Solargraph::Parser::NodeMethods.unpack_name(const)}")
217
+ @marked_ranges.push location.range
218
+ end
219
+ end
220
+ result
221
+ end
222
+
223
+ def call_problems
224
+ result = []
225
+ Solargraph::Parser::NodeMethods.call_nodes_from(source_map.source.node).each do |call|
226
+ rng = Solargraph::Range.from_node(call)
227
+ next if @marked_ranges.any? { |d| d.contain?(rng.start) }
228
+ chain = Solargraph::Parser.chain(call, filename)
229
+ block_pin = source_map.locate_block_pin(rng.start.line, rng.start.column)
230
+ location = Location.new(filename, rng)
231
+ locals = source_map.locals_at(location)
232
+ type = chain.infer(api_map, block_pin, locals)
233
+ if type.undefined? && !rules.ignore_all_undefined?
234
+ base = chain
235
+ missing = chain
236
+ found = nil
237
+ closest = ComplexType::UNDEFINED
238
+ until base.links.first.undefined?
239
+ found = base.define(api_map, block_pin, locals).first
240
+ break if found
241
+ missing = base
242
+ base = base.base
243
+ end
244
+ closest = found.typify(api_map) if found
245
+ if !found || found.is_a?(Pin::BaseVariable) || (closest.defined? && internal_or_core?(found))
246
+ unless closest.parameterized? || ignored_pins.include?(found)
247
+ result.push Problem.new(location, "Unresolved call to #{missing.links.last.word}")
248
+ @marked_ranges.push rng
249
+ end
250
+ end
251
+ end
252
+ result.concat argument_problems_for(chain, api_map, block_pin, locals, location)
253
+ end
254
+ result
255
+ end
256
+
257
+ def argument_problems_for chain, api_map, block_pin, locals, location
258
+ result = []
259
+ base = chain
260
+ until base.links.length == 1 && base.undefined?
261
+ pins = base.define(api_map, block_pin, locals)
262
+ if pins.first.is_a?(Pin::Method)
263
+ # @type [Pin::Method]
264
+ pin = pins.first
265
+ ap = if base.links.last.is_a?(Solargraph::Source::Chain::ZSuper)
266
+ arity_problems_for(pin, fake_args_for(block_pin), location)
267
+ else
268
+ arity_problems_for(pin, base.links.last.arguments, location)
269
+ end
270
+ unless ap.empty?
271
+ result.concat ap
272
+ break
273
+ end
274
+ break unless rules.validate_calls?
275
+ params = first_param_hash(pins)
276
+
277
+ all_errors = []
278
+ pin.signatures.sort { |sig| sig.parameters.length }.each do |sig|
279
+ errors = []
280
+ sig.parameters.each_with_index do |par, idx|
281
+ argchain = base.links.last.arguments[idx]
282
+ if argchain.nil? && par.decl == :arg
283
+ errors.push Problem.new(location, "Not enough arguments to #{pin.path}")
284
+ next
285
+ end
286
+ if argchain
287
+ if par.decl != :arg
288
+ errors.concat kwarg_problems_for argchain, api_map, block_pin, locals, location, pin, params, idx
289
+ next
290
+ else
291
+ ptype = params.key?(par.name) ? params[par.name][:qualified] : ComplexType::UNDEFINED
292
+ if ptype.nil?
293
+ # @todo Some level (strong, I guess) should require the param here
294
+ else
295
+ argtype = argchain.infer(api_map, block_pin, locals)
296
+ if argtype.defined? && ptype.defined? && !any_types_match?(api_map, ptype, argtype)
297
+ errors.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}")
298
+ next
299
+ end
300
+ end
301
+ end
302
+ elsif par.decl == :kwarg
303
+ errors.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}")
304
+ next
305
+ end
306
+ end
307
+ if errors.empty?
308
+ all_errors.clear
309
+ break
310
+ end
311
+ all_errors.concat errors
312
+ end
313
+ result.concat all_errors
314
+ end
315
+ base = base.base
316
+ end
317
+ result
318
+ end
319
+
320
+ def kwarg_problems_for argchain, api_map, block_pin, locals, location, pin, params, first
321
+ result = []
322
+ kwargs = convert_hash(argchain.node)
323
+ pin.signatures.first.parameters[first..-1].each_with_index do |par, cur|
324
+ idx = first + cur
325
+ argchain = kwargs[par.name.to_sym]
326
+ if par.decl == :kwrestarg || (par.decl == :optarg && idx == pin.parameters.length - 1 && par.asgn_code == '{}')
327
+ result.concat kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kwargs)
328
+ else
329
+ if argchain
330
+ data = params[par.name]
331
+ if data.nil?
332
+ # @todo Some level (strong, I guess) should require the param here
333
+ else
334
+ ptype = data[:qualified]
335
+ next if ptype.undefined?
336
+ argtype = argchain.infer(api_map, block_pin, locals)
337
+ if argtype.defined? && ptype && !any_types_match?(api_map, ptype, argtype)
338
+ result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}")
339
+ end
340
+ end
341
+ elsif par.decl == :kwarg
342
+ result.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}")
343
+ end
344
+ end
345
+ end
346
+ result
347
+ end
348
+
349
+ def kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kwargs)
350
+ result = []
351
+ kwargs.each_pair do |pname, argchain|
352
+ next unless params.key?(pname.to_s)
353
+ ptype = params[pname.to_s][:qualified]
354
+ argtype = argchain.infer(api_map, block_pin, locals)
355
+ if argtype.defined? && ptype && !any_types_match?(api_map, ptype, argtype)
356
+ result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{pname} expected #{ptype}, received #{argtype}")
357
+ end
358
+ end
359
+ result
360
+ end
361
+
362
+ # @param [Pin::Method]
363
+ # @return [Hash]
364
+ def param_hash(pin)
365
+ tags = pin.docstring.tags(:param)
366
+ return {} if tags.empty?
367
+ result = {}
368
+ tags.each do |tag|
369
+ next if tag.types.nil? || tag.types.empty?
370
+ result[tag.name.to_s] = {
371
+ tagged: tag.types.join(', '),
372
+ qualified: Solargraph::ComplexType.try_parse(*tag.types).qualify(api_map, pin.full_context.namespace)
373
+ }
374
+ end
375
+ result
376
+ end
377
+
378
+ # @param [Array<Pin::Method>]
379
+ # @return [Hash]
380
+ def first_param_hash(pins)
381
+ pins.each do |pin|
382
+ result = param_hash(pin)
383
+ return result unless result.empty?
384
+ end
385
+ {}
386
+ end
387
+
388
+ # @param pin [Pin::Base]
389
+ def internal? pin
390
+ return false if pin.nil?
391
+ pin.location && api_map.bundled?(pin.location.filename)
392
+ end
393
+
394
+ # True if the pin is either internal (part of the workspace) or from the core/stdlib
395
+ def internal_or_core? pin
396
+ # @todo RBS pins are not necessarily core/stdlib pins
397
+ internal?(pin) || pin.source == :rbs
398
+ end
399
+
400
+ # @param pin [Pin::Base]
401
+ def external? pin
402
+ !internal? pin
403
+ end
404
+
405
+ def declared_externally? pin
406
+ return true if pin.assignment.nil?
407
+ chain = Solargraph::Parser.chain(pin.assignment, filename)
408
+ rng = Solargraph::Range.from_node(pin.assignment)
409
+ block_pin = source_map.locate_block_pin(rng.start.line, rng.start.column)
410
+ location = Location.new(filename, Range.from_node(pin.assignment))
411
+ locals = source_map.locals_at(location)
412
+ type = chain.infer(api_map, block_pin, locals)
413
+ if type.undefined? && !rules.ignore_all_undefined?
414
+ base = chain
415
+ missing = chain
416
+ found = nil
417
+ closest = ComplexType::UNDEFINED
418
+ until base.links.first.undefined?
419
+ found = base.define(api_map, block_pin, locals).first
420
+ break if found
421
+ missing = base
422
+ base = base.base
423
+ end
424
+ closest = found.typify(api_map) if found
425
+ if !found || closest.defined? || internal?(found)
426
+ return false
427
+ end
428
+ end
429
+ true
430
+ end
431
+
432
+ def arity_problems_for pin, arguments, location
433
+ results = pin.signatures.map do |sig|
434
+ r = parameterized_arity_problems_for(pin, sig.parameters, arguments, location)
435
+ return [] if r.empty?
436
+ r
437
+ end
438
+ results.first
439
+ end
440
+
441
+ def parameterized_arity_problems_for(pin, parameters, arguments, location)
442
+ return [] unless pin.explicit?
443
+ return [] if parameters.empty? && arguments.empty?
444
+ return [] if pin.anon_splat?
445
+ if parameters.empty?
446
+ # Functions tagged param_tuple accepts two arguments (e.g., Hash#[]=)
447
+ return [] if pin.docstring.tag(:param_tuple) && arguments.length == 2
448
+ return [] if arguments.length == 1 && arguments.last.links.last.is_a?(Source::Chain::BlockVariable)
449
+ return [Problem.new(location, "Too many arguments to #{pin.path}")]
450
+ end
451
+ unchecked = arguments.clone
452
+ add_params = 0
453
+ if unchecked.empty? && parameters.any? { |param| param.decl == :kwarg }
454
+ return [Problem.new(location, "Missing keyword arguments to #{pin.path}")]
455
+ end
456
+ settled_kwargs = 0
457
+ unless unchecked.empty?
458
+ if any_splatted_call?(unchecked.map(&:node))
459
+ settled_kwargs = parameters.count(&:keyword?)
460
+ else
461
+ kwargs = convert_hash(unchecked.last.node)
462
+ if parameters.any? { |param| [:kwarg, :kwoptarg].include?(param.decl) || param.kwrestarg? }
463
+ if kwargs.empty?
464
+ add_params += 1
465
+ else
466
+ unchecked.pop
467
+ parameters.each do |param|
468
+ next unless param.keyword?
469
+ if kwargs.key?(param.name.to_sym)
470
+ kwargs.delete param.name.to_sym
471
+ settled_kwargs += 1
472
+ elsif param.decl == :kwarg
473
+ return [] if arguments.last.links.last.is_a?(Solargraph::Source::Chain::Hash) && arguments.last.links.last.splatted?
474
+ return [Problem.new(location, "Missing keyword argument #{param.name} to #{pin.path}")]
475
+ end
476
+ end
477
+ kwargs.clear if parameters.any?(&:kwrestarg?)
478
+ unless kwargs.empty?
479
+ return [Problem.new(location, "Unrecognized keyword argument #{kwargs.keys.first} to #{pin.path}")]
480
+ end
481
+ end
482
+ end
483
+ end
484
+ end
485
+ req = required_param_count(parameters)
486
+ if req + add_params < unchecked.length
487
+ return [] if parameters.any?(&:rest?)
488
+ opt = optional_param_count(parameters)
489
+ return [] if unchecked.length <= req + opt
490
+ if unchecked.length == req + opt + 1 && unchecked.last.links.last.is_a?(Source::Chain::BlockVariable)
491
+ return []
492
+ end
493
+ if req + add_params + 1 == unchecked.length && any_splatted_call?(unchecked.map(&:node)) && (parameters.map(&:decl) & [:kwarg, :kwoptarg, :kwrestarg]).any?
494
+ return []
495
+ end
496
+ return [] if arguments.length - req == parameters.select { |p| [:optarg, :kwoptarg].include?(p.decl) }.length
497
+ return [Problem.new(location, "Too many arguments to #{pin.path}")]
498
+ elsif unchecked.length < req - settled_kwargs && (arguments.empty? || (!arguments.last.splat? && !arguments.last.links.last.is_a?(Solargraph::Source::Chain::Hash)))
499
+ # HACK: Kernel#raise signature is incorrect in Ruby 2.7 core docs.
500
+ # See https://github.com/castwide/solargraph/issues/418
501
+ unless arguments.empty? && pin.path == 'Kernel#raise'
502
+ return [Problem.new(location, "Not enough arguments to #{pin.path}")]
503
+ end
504
+ end
505
+ []
506
+ end
507
+
508
+ def required_param_count(parameters)
509
+ parameters.sum { |param| %i[arg kwarg].include?(param.decl) ? 1 : 0 }
510
+ end
511
+
512
+ # @param pin [Pin::Method]
513
+ def optional_param_count(parameters)
514
+ parameters.select { |p| p.decl == :optarg }.length
515
+ end
516
+
517
+ def abstract? pin
518
+ pin.docstring.has_tag?(:abstract) ||
519
+ (pin.closure && pin.closure.docstring.has_tag?(:abstract))
520
+ end
521
+
522
+ def fake_args_for(pin)
523
+ args = []
524
+ with_opts = false
525
+ with_block = false
526
+ pin.parameters.each do |pin|
527
+ if [:kwarg, :kwoptarg, :kwrestarg].include?(pin.decl)
528
+ with_opts = true
529
+ elsif pin.decl == :block
530
+ with_block = true
531
+ elsif pin.decl == :restarg
532
+ args.push Solargraph::Source::Chain.new([Solargraph::Source::Chain::Variable.new(pin.name)], nil, true)
533
+ else
534
+ args.push Solargraph::Source::Chain.new([Solargraph::Source::Chain::Variable.new(pin.name)])
535
+ end
536
+ end
537
+ args.push Solargraph::Parser.chain_string('{}') if with_opts
538
+ args.push Solargraph::Parser.chain_string('&') if with_block
539
+ args
540
+ end
541
+
542
+ def without_ignored problems
543
+ problems.reject do |problem|
544
+ node = source_map.source.node_at(problem.location.range.start.line, problem.location.range.start.column)
545
+ source_map.source.comments_for(node)&.include?('@sg-ignore')
546
+ end
547
+ end
548
+ end
549
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ VERSION = '0.49.0'
5
+ end
@@ -0,0 +1,62 @@
1
+ <h2>
2
+ Namespace:
3
+ </h2>
4
+ <p>
5
+ <a href="solargraph:/document?query=<%= CGI.escape object.namespace.path %>"><%= object.namespace %></a>
6
+ </p>
7
+ <h2>
8
+ Overview:
9
+ </h2>
10
+ <%= htmlify object.docstring %>
11
+ <p class="document-section">
12
+ <big><strong>Visibility:</strong></big> <%= object.visibility %>
13
+ </p>
14
+ <% unless object.tags(:param).empty? %>
15
+ <h2>
16
+ Parameters:
17
+ </h2>
18
+ <ul>
19
+ <% object.tags(:param).each do |tag| %>
20
+ <li>
21
+ <%= erb :_name_type_tag, layout: false, locals: {tag: tag} %>
22
+ </li>
23
+ <% end %>
24
+ </ul>
25
+ <% end %>
26
+ <% unless object.tags(:raise).empty? %>
27
+ <h2>
28
+ Raises:
29
+ </h2>
30
+ <ul>
31
+ <% object.tags(:raise).each do |tag| %>
32
+ <li>
33
+ <%= erb :_name_type_tag, layout: false, locals: {tag: tag} %>
34
+ </li>
35
+ <% end %>
36
+ </ul>
37
+ <% end %>
38
+ <h2>
39
+ Returns:
40
+ </h2>
41
+ <% if object.tag(:return).nil? %>
42
+ <p>
43
+ Undefined/unknown
44
+ </p>
45
+ <% else %>
46
+ <ul>
47
+ <% object.tags(:return).each do |tag| %>
48
+ <li>
49
+ <%= erb :_name_type_tag, layout: false, locals: {tag: tag} %>
50
+ </li>
51
+ <% end %>
52
+ </ul>
53
+ <% end %>
54
+ <% examples = object.tags(:example) %>
55
+ <% unless examples.nil? %>
56
+ <% examples.each do |example| %>
57
+ <h2>
58
+ Example: <%= example.name %>
59
+ </h2>
60
+ <%= ruby_to_html example.text.strip %>
61
+ <% end %>
62
+ <% end %>
@@ -0,0 +1,10 @@
1
+ <%= tag.name %>
2
+ <% unless tag.types.nil? or tag.types.empty? %>
3
+ [<%= escape(tag.types.join(', ')) %>]
4
+ <% end %>
5
+ <% unless tag.text.nil? || tag.text.empty? %>
6
+ <% unless (tag.name.nil? or tag.name.empty?) and (tag.types.nil? or tag.types.empty?) %>
7
+ -
8
+ <% end %>
9
+ <%= tag.text %>
10
+ <% end %>
@@ -0,0 +1,24 @@
1
+ <h2>
2
+ Overview:
3
+ </h2>
4
+ <%= htmlify object.docstring %>
5
+ <h2>
6
+ Class Methods
7
+ </h2>
8
+ <ul class="doc-list">
9
+ <% object.meths(scope: :class).sort{|a, b| a.name <=> b.name}.each do |meth| %>
10
+ <li>
11
+ <a href="solargraph:/document?query=<%= CGI.escape(meth.path) %>"><%= meth.name %></a>
12
+ </li>
13
+ <% end %>
14
+ </ul>
15
+ <h2>
16
+ Instance Methods
17
+ </h2>
18
+ <ul class="doc-list">
19
+ <% object.meths(scope: :instance).sort{|a, b| a.name <=> b.name}.each do |meth| %>
20
+ <li>
21
+ <a href="solargraph:/document?query=<%= CGI.escape(meth.path) %>"><%= meth.name %></a>
22
+ </li>
23
+ <% end %>
24
+ </ul>
@@ -0,0 +1,23 @@
1
+ <% objects.reverse.each do |object| %>
2
+ <h1>
3
+ <%= object.name %>
4
+ <% if object.is_a?(YARD::CodeObjects::MethodObject) and !object.parameters.empty? %>
5
+ <small>(<%= object.parameters.map {|p| "#{p[0]}#{p[1] and p[0].end_with?(':') ? ' ' : (p[1] ? ' = ' : '')}#{p[1]}"}.join(', ') %>)</small>
6
+ <% end %>
7
+ </h1>
8
+ <% unless object.files.empty? %>
9
+ <h2>
10
+ Defined in:
11
+ </h2>
12
+ <ul>
13
+ <% object.files.each do |f| %>
14
+ <li><%= f %></li>
15
+ <% end %>
16
+ </ul>
17
+ <% end %>
18
+ <% if object.is_a?(YARD::CodeObjects::NamespaceObject) %>
19
+ <%= erb :_namespace, layout: false, locals: {object: object} %>
20
+ <% elsif object.is_a?(YARD::CodeObjects::MethodObject) %>
21
+ <%= erb :_method, layout: false, locals: {object: object} %>
22
+ <% end %>
23
+ <% end %>