jimeh-solargraph 0.40.4.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 (252) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +54 -0
  3. data/.gitignore +9 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +24 -0
  6. data/.yardopts +2 -0
  7. data/CHANGELOG.md +1003 -0
  8. data/Gemfile +7 -0
  9. data/LICENSE +21 -0
  10. data/README.md +123 -0
  11. data/Rakefile +25 -0
  12. data/SPONSORS.md +15 -0
  13. data/bin/solargraph +5 -0
  14. data/jimeh-solargraph.gemspec +44 -0
  15. data/lib/.rubocop.yml +21 -0
  16. data/lib/solargraph.rb +67 -0
  17. data/lib/solargraph/api_map.rb +752 -0
  18. data/lib/solargraph/api_map/bundler_methods.rb +27 -0
  19. data/lib/solargraph/api_map/cache.rb +70 -0
  20. data/lib/solargraph/api_map/source_to_yard.rb +81 -0
  21. data/lib/solargraph/api_map/store.rb +256 -0
  22. data/lib/solargraph/bench.rb +30 -0
  23. data/lib/solargraph/compat.rb +23 -0
  24. data/lib/solargraph/complex_type.rb +213 -0
  25. data/lib/solargraph/complex_type/type_methods.rb +127 -0
  26. data/lib/solargraph/complex_type/unique_type.rb +75 -0
  27. data/lib/solargraph/convention.rb +47 -0
  28. data/lib/solargraph/convention/base.rb +33 -0
  29. data/lib/solargraph/convention/gemfile.rb +15 -0
  30. data/lib/solargraph/convention/gemspec.rb +22 -0
  31. data/lib/solargraph/convention/rspec.rb +21 -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.rb +55 -0
  37. data/lib/solargraph/diagnostics/base.rb +29 -0
  38. data/lib/solargraph/diagnostics/require_not_found.rb +37 -0
  39. data/lib/solargraph/diagnostics/rubocop.rb +90 -0
  40. data/lib/solargraph/diagnostics/rubocop_helpers.rb +45 -0
  41. data/lib/solargraph/diagnostics/severities.rb +15 -0
  42. data/lib/solargraph/diagnostics/type_check.rb +54 -0
  43. data/lib/solargraph/diagnostics/update_errors.rb +41 -0
  44. data/lib/solargraph/documentor.rb +78 -0
  45. data/lib/solargraph/environ.rb +45 -0
  46. data/lib/solargraph/language_server.rb +19 -0
  47. data/lib/solargraph/language_server/completion_item_kinds.rb +35 -0
  48. data/lib/solargraph/language_server/error_codes.rb +20 -0
  49. data/lib/solargraph/language_server/host.rb +746 -0
  50. data/lib/solargraph/language_server/host/cataloger.rb +56 -0
  51. data/lib/solargraph/language_server/host/diagnoser.rb +81 -0
  52. data/lib/solargraph/language_server/host/dispatch.rb +112 -0
  53. data/lib/solargraph/language_server/host/sources.rb +156 -0
  54. data/lib/solargraph/language_server/message.rb +92 -0
  55. data/lib/solargraph/language_server/message/base.rb +85 -0
  56. data/lib/solargraph/language_server/message/cancel_request.rb +13 -0
  57. data/lib/solargraph/language_server/message/client.rb +11 -0
  58. data/lib/solargraph/language_server/message/client/register_capability.rb +15 -0
  59. data/lib/solargraph/language_server/message/completion_item.rb +11 -0
  60. data/lib/solargraph/language_server/message/completion_item/resolve.rb +57 -0
  61. data/lib/solargraph/language_server/message/exit_notification.rb +13 -0
  62. data/lib/solargraph/language_server/message/extended.rb +21 -0
  63. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +100 -0
  64. data/lib/solargraph/language_server/message/extended/document.rb +20 -0
  65. data/lib/solargraph/language_server/message/extended/document_gems.rb +32 -0
  66. data/lib/solargraph/language_server/message/extended/download_core.rb +23 -0
  67. data/lib/solargraph/language_server/message/extended/environment.rb +25 -0
  68. data/lib/solargraph/language_server/message/extended/search.rb +20 -0
  69. data/lib/solargraph/language_server/message/initialize.rb +153 -0
  70. data/lib/solargraph/language_server/message/initialized.rb +26 -0
  71. data/lib/solargraph/language_server/message/method_not_found.rb +16 -0
  72. data/lib/solargraph/language_server/message/method_not_implemented.rb +14 -0
  73. data/lib/solargraph/language_server/message/shutdown.rb +13 -0
  74. data/lib/solargraph/language_server/message/text_document.rb +28 -0
  75. data/lib/solargraph/language_server/message/text_document/base.rb +19 -0
  76. data/lib/solargraph/language_server/message/text_document/code_action.rb +17 -0
  77. data/lib/solargraph/language_server/message/text_document/completion.rb +57 -0
  78. data/lib/solargraph/language_server/message/text_document/definition.rb +38 -0
  79. data/lib/solargraph/language_server/message/text_document/did_change.rb +15 -0
  80. data/lib/solargraph/language_server/message/text_document/did_close.rb +15 -0
  81. data/lib/solargraph/language_server/message/text_document/did_open.rb +15 -0
  82. data/lib/solargraph/language_server/message/text_document/did_save.rb +17 -0
  83. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +23 -0
  84. data/lib/solargraph/language_server/message/text_document/folding_range.rb +26 -0
  85. data/lib/solargraph/language_server/message/text_document/formatting.rb +105 -0
  86. data/lib/solargraph/language_server/message/text_document/hover.rb +44 -0
  87. data/lib/solargraph/language_server/message/text_document/on_type_formatting.rb +34 -0
  88. data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +11 -0
  89. data/lib/solargraph/language_server/message/text_document/references.rb +16 -0
  90. data/lib/solargraph/language_server/message/text_document/rename.rb +19 -0
  91. data/lib/solargraph/language_server/message/text_document/signature_help.rb +29 -0
  92. data/lib/solargraph/language_server/message/workspace.rb +14 -0
  93. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +29 -0
  94. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +33 -0
  95. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +24 -0
  96. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +23 -0
  97. data/lib/solargraph/language_server/message_types.rb +14 -0
  98. data/lib/solargraph/language_server/request.rb +24 -0
  99. data/lib/solargraph/language_server/symbol_kinds.rb +36 -0
  100. data/lib/solargraph/language_server/transport.rb +13 -0
  101. data/lib/solargraph/language_server/transport/adapter.rb +56 -0
  102. data/lib/solargraph/language_server/transport/data_reader.rb +72 -0
  103. data/lib/solargraph/language_server/uri_helpers.rb +49 -0
  104. data/lib/solargraph/library.rb +426 -0
  105. data/lib/solargraph/location.rb +37 -0
  106. data/lib/solargraph/logging.rb +27 -0
  107. data/lib/solargraph/page.rb +83 -0
  108. data/lib/solargraph/parser.rb +26 -0
  109. data/lib/solargraph/parser/comment_ripper.rb +52 -0
  110. data/lib/solargraph/parser/legacy.rb +12 -0
  111. data/lib/solargraph/parser/legacy/class_methods.rb +109 -0
  112. data/lib/solargraph/parser/legacy/flawed_builder.rb +16 -0
  113. data/lib/solargraph/parser/legacy/node_chainer.rb +118 -0
  114. data/lib/solargraph/parser/legacy/node_methods.rb +311 -0
  115. data/lib/solargraph/parser/legacy/node_processors.rb +54 -0
  116. data/lib/solargraph/parser/legacy/node_processors/alias_node.rb +23 -0
  117. data/lib/solargraph/parser/legacy/node_processors/args_node.rb +35 -0
  118. data/lib/solargraph/parser/legacy/node_processors/begin_node.rb +15 -0
  119. data/lib/solargraph/parser/legacy/node_processors/block_node.rb +22 -0
  120. data/lib/solargraph/parser/legacy/node_processors/casgn_node.rb +25 -0
  121. data/lib/solargraph/parser/legacy/node_processors/cvasgn_node.rb +23 -0
  122. data/lib/solargraph/parser/legacy/node_processors/def_node.rb +63 -0
  123. data/lib/solargraph/parser/legacy/node_processors/defs_node.rb +36 -0
  124. data/lib/solargraph/parser/legacy/node_processors/gvasgn_node.rb +23 -0
  125. data/lib/solargraph/parser/legacy/node_processors/ivasgn_node.rb +38 -0
  126. data/lib/solargraph/parser/legacy/node_processors/lvasgn_node.rb +28 -0
  127. data/lib/solargraph/parser/legacy/node_processors/namespace_node.rb +39 -0
  128. data/lib/solargraph/parser/legacy/node_processors/orasgn_node.rb +16 -0
  129. data/lib/solargraph/parser/legacy/node_processors/resbody_node.rb +36 -0
  130. data/lib/solargraph/parser/legacy/node_processors/sclass_node.rb +21 -0
  131. data/lib/solargraph/parser/legacy/node_processors/send_node.rb +257 -0
  132. data/lib/solargraph/parser/legacy/node_processors/sym_node.rb +18 -0
  133. data/lib/solargraph/parser/node_methods.rb +43 -0
  134. data/lib/solargraph/parser/node_processor.rb +43 -0
  135. data/lib/solargraph/parser/node_processor/base.rb +80 -0
  136. data/lib/solargraph/parser/region.rb +66 -0
  137. data/lib/solargraph/parser/rubyvm.rb +40 -0
  138. data/lib/solargraph/parser/rubyvm/class_methods.rb +150 -0
  139. data/lib/solargraph/parser/rubyvm/node_chainer.rb +135 -0
  140. data/lib/solargraph/parser/rubyvm/node_methods.rb +301 -0
  141. data/lib/solargraph/parser/rubyvm/node_processors.rb +62 -0
  142. data/lib/solargraph/parser/rubyvm/node_processors/alias_node.rb +23 -0
  143. data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +86 -0
  144. data/lib/solargraph/parser/rubyvm/node_processors/begin_node.rb +15 -0
  145. data/lib/solargraph/parser/rubyvm/node_processors/block_node.rb +22 -0
  146. data/lib/solargraph/parser/rubyvm/node_processors/casgn_node.rb +22 -0
  147. data/lib/solargraph/parser/rubyvm/node_processors/cvasgn_node.rb +23 -0
  148. data/lib/solargraph/parser/rubyvm/node_processors/def_node.rb +64 -0
  149. data/lib/solargraph/parser/rubyvm/node_processors/defs_node.rb +57 -0
  150. data/lib/solargraph/parser/rubyvm/node_processors/gvasgn_node.rb +23 -0
  151. data/lib/solargraph/parser/rubyvm/node_processors/ivasgn_node.rb +38 -0
  152. data/lib/solargraph/parser/rubyvm/node_processors/kw_arg_node.rb +39 -0
  153. data/lib/solargraph/parser/rubyvm/node_processors/lit_node.rb +20 -0
  154. data/lib/solargraph/parser/rubyvm/node_processors/lvasgn_node.rb +27 -0
  155. data/lib/solargraph/parser/rubyvm/node_processors/namespace_node.rb +39 -0
  156. data/lib/solargraph/parser/rubyvm/node_processors/opt_arg_node.rb +26 -0
  157. data/lib/solargraph/parser/rubyvm/node_processors/orasgn_node.rb +15 -0
  158. data/lib/solargraph/parser/rubyvm/node_processors/resbody_node.rb +45 -0
  159. data/lib/solargraph/parser/rubyvm/node_processors/sclass_node.rb +21 -0
  160. data/lib/solargraph/parser/rubyvm/node_processors/scope_node.rb +15 -0
  161. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +277 -0
  162. data/lib/solargraph/parser/rubyvm/node_processors/sym_node.rb +18 -0
  163. data/lib/solargraph/parser/snippet.rb +13 -0
  164. data/lib/solargraph/pin.rb +36 -0
  165. data/lib/solargraph/pin/base.rb +296 -0
  166. data/lib/solargraph/pin/base_variable.rb +84 -0
  167. data/lib/solargraph/pin/block.rb +62 -0
  168. data/lib/solargraph/pin/class_variable.rb +8 -0
  169. data/lib/solargraph/pin/closure.rb +37 -0
  170. data/lib/solargraph/pin/common.rb +70 -0
  171. data/lib/solargraph/pin/constant.rb +43 -0
  172. data/lib/solargraph/pin/conversions.rb +96 -0
  173. data/lib/solargraph/pin/documenting.rb +105 -0
  174. data/lib/solargraph/pin/duck_method.rb +16 -0
  175. data/lib/solargraph/pin/global_variable.rb +8 -0
  176. data/lib/solargraph/pin/instance_variable.rb +30 -0
  177. data/lib/solargraph/pin/keyword.rb +15 -0
  178. data/lib/solargraph/pin/keyword_param.rb +8 -0
  179. data/lib/solargraph/pin/local_variable.rb +21 -0
  180. data/lib/solargraph/pin/localized.rb +43 -0
  181. data/lib/solargraph/pin/method.rb +245 -0
  182. data/lib/solargraph/pin/method_alias.rb +31 -0
  183. data/lib/solargraph/pin/namespace.rb +85 -0
  184. data/lib/solargraph/pin/parameter.rb +206 -0
  185. data/lib/solargraph/pin/proxy_type.rb +29 -0
  186. data/lib/solargraph/pin/reference.rb +14 -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/singleton.rb +11 -0
  194. data/lib/solargraph/pin/symbol.rb +47 -0
  195. data/lib/solargraph/position.rb +100 -0
  196. data/lib/solargraph/range.rb +95 -0
  197. data/lib/solargraph/server_methods.rb +16 -0
  198. data/lib/solargraph/shell.rb +222 -0
  199. data/lib/solargraph/source.rb +537 -0
  200. data/lib/solargraph/source/chain.rb +154 -0
  201. data/lib/solargraph/source/chain/block_variable.rb +13 -0
  202. data/lib/solargraph/source/chain/call.rb +203 -0
  203. data/lib/solargraph/source/chain/class_variable.rb +13 -0
  204. data/lib/solargraph/source/chain/constant.rb +75 -0
  205. data/lib/solargraph/source/chain/global_variable.rb +13 -0
  206. data/lib/solargraph/source/chain/head.rb +35 -0
  207. data/lib/solargraph/source/chain/instance_variable.rb +13 -0
  208. data/lib/solargraph/source/chain/link.rb +67 -0
  209. data/lib/solargraph/source/chain/literal.rb +23 -0
  210. data/lib/solargraph/source/chain/or.rb +23 -0
  211. data/lib/solargraph/source/chain/variable.rb +13 -0
  212. data/lib/solargraph/source/chain/z_super.rb +30 -0
  213. data/lib/solargraph/source/change.rb +79 -0
  214. data/lib/solargraph/source/cursor.rb +164 -0
  215. data/lib/solargraph/source/encoding_fixes.rb +23 -0
  216. data/lib/solargraph/source/source_chainer.rb +190 -0
  217. data/lib/solargraph/source/updater.rb +54 -0
  218. data/lib/solargraph/source_map.rb +188 -0
  219. data/lib/solargraph/source_map/clip.rb +224 -0
  220. data/lib/solargraph/source_map/completion.rb +23 -0
  221. data/lib/solargraph/source_map/mapper.rb +215 -0
  222. data/lib/solargraph/type_checker.rb +503 -0
  223. data/lib/solargraph/type_checker/checks.rb +99 -0
  224. data/lib/solargraph/type_checker/param_def.rb +35 -0
  225. data/lib/solargraph/type_checker/problem.rb +32 -0
  226. data/lib/solargraph/type_checker/rules.rb +57 -0
  227. data/lib/solargraph/version.rb +5 -0
  228. data/lib/solargraph/views/_method.erb +62 -0
  229. data/lib/solargraph/views/_name_type_tag.erb +10 -0
  230. data/lib/solargraph/views/_namespace.erb +24 -0
  231. data/lib/solargraph/views/document.erb +23 -0
  232. data/lib/solargraph/views/environment.erb +58 -0
  233. data/lib/solargraph/views/layout.erb +44 -0
  234. data/lib/solargraph/views/search.erb +11 -0
  235. data/lib/solargraph/workspace.rb +209 -0
  236. data/lib/solargraph/workspace/config.rb +230 -0
  237. data/lib/solargraph/yard_map.rb +435 -0
  238. data/lib/solargraph/yard_map/cache.rb +19 -0
  239. data/lib/solargraph/yard_map/core_docs.rb +170 -0
  240. data/lib/solargraph/yard_map/core_fills.rb +185 -0
  241. data/lib/solargraph/yard_map/core_gen.rb +76 -0
  242. data/lib/solargraph/yard_map/helpers.rb +16 -0
  243. data/lib/solargraph/yard_map/mapper.rb +77 -0
  244. data/lib/solargraph/yard_map/mapper/to_constant.rb +25 -0
  245. data/lib/solargraph/yard_map/mapper/to_method.rb +78 -0
  246. data/lib/solargraph/yard_map/mapper/to_namespace.rb +27 -0
  247. data/lib/solargraph/yard_map/rdoc_to_yard.rb +140 -0
  248. data/lib/solargraph/yard_map/stdlib_fills.rb +43 -0
  249. data/lib/solargraph/yard_map/to_method.rb +79 -0
  250. data/lib/yard-solargraph.rb +30 -0
  251. data/yardoc/2.2.2.tar.gz +0 -0
  252. metadata +564 -0
@@ -0,0 +1,503 @@
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
+ method_tag_problems
44
+ .concat variable_type_tag_problems
45
+ .concat const_problems
46
+ .concat call_problems
47
+ end
48
+ end
49
+
50
+ class << self
51
+ # @param filename [String]
52
+ # @return [self]
53
+ def load filename, level = :normal
54
+ source = Solargraph::Source.load(filename)
55
+ api_map = Solargraph::ApiMap.new
56
+ api_map.map(source)
57
+ new(filename, api_map: api_map, level: level)
58
+ end
59
+
60
+ # @param code [String]
61
+ # @param filename [String, nil]
62
+ # @return [self]
63
+ def load_string code, filename = nil, level = :normal
64
+ source = Solargraph::Source.load_string(code, filename)
65
+ api_map = Solargraph::ApiMap.new
66
+ api_map.map(source)
67
+ new(filename, api_map: api_map, level: level)
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ # @return [Array<Problem>]
74
+ def method_tag_problems
75
+ result = []
76
+ # @param pin [Pin::Method]
77
+ source_map.pins_by_class(Pin::Method).each do |pin|
78
+ result.concat method_return_type_problems_for(pin)
79
+ result.concat method_param_type_problems_for(pin)
80
+ end
81
+ result
82
+ end
83
+
84
+ # @param pin [Pin::Method]
85
+ # @return [Array<Problem>]
86
+ def method_return_type_problems_for pin
87
+ result = []
88
+ declared = pin.typify(api_map).self_to(pin.full_context.namespace)
89
+ if declared.undefined?
90
+ if pin.return_type.undefined? && rules.require_type_tags?
91
+ result.push Problem.new(pin.location, "Missing @return tag for #{pin.path}", pin: pin)
92
+ elsif pin.return_type.defined?
93
+ result.push Problem.new(pin.location, "Unresolved return type #{pin.return_type} for #{pin.path}", pin: pin)
94
+ elsif rules.must_tag_or_infer? && pin.probe(api_map).undefined?
95
+ result.push Problem.new(pin.location, "Untyped method #{pin.path} could not be inferred")
96
+ end
97
+ elsif rules.validate_tags?
98
+ unless pin.node.nil? || declared.void? || virtual_pin?(pin) || abstract?(pin)
99
+ inferred = pin.probe(api_map).self_to(pin.full_context.namespace)
100
+ if inferred.undefined?
101
+ unless rules.ignore_all_undefined? || external?(pin)
102
+ result.push Problem.new(pin.location, "#{pin.path} return type could not be inferred", pin: pin)
103
+ end
104
+ else
105
+ unless (rules.rank > 1 ? types_match?(api_map, declared, inferred) : any_types_match?(api_map, declared, inferred))
106
+ result.push Problem.new(pin.location, "Declared return type #{declared} does not match inferred type #{inferred} for #{pin.path}", pin: pin)
107
+ end
108
+ end
109
+ end
110
+ end
111
+ result
112
+ end
113
+
114
+ def virtual_pin? pin
115
+ pin.location && source_map.source.comment_at?(pin.location.range.ending)
116
+ end
117
+
118
+ # @param pin [Pin::Method]
119
+ # @return [Array<Problem>]
120
+ def method_param_type_problems_for pin
121
+ stack = api_map.get_method_stack(pin.namespace, pin.name, scope: pin.scope)
122
+ params = first_param_hash(stack)
123
+ result = []
124
+ if rules.require_type_tags?
125
+ pin.parameters.each do |par|
126
+ break if par.decl == :restarg || par.decl == :kwrestarg || par.decl == :blockarg
127
+ unless params[par.name]
128
+ result.push Problem.new(pin.location, "Missing @param tag for #{par.name} on #{pin.path}", pin: pin)
129
+ end
130
+ end
131
+ end
132
+ params.each_pair do |name, data|
133
+ type = data[:qualified]
134
+ if type.undefined?
135
+ result.push Problem.new(pin.location, "Unresolved type #{data[:tagged]} for #{name} param on #{pin.path}", pin: pin)
136
+ end
137
+ end
138
+ result
139
+ end
140
+
141
+ def ignored_pins
142
+ @ignored_pins ||= []
143
+ end
144
+
145
+ # @return [Array<Problem>]
146
+ def variable_type_tag_problems
147
+ result = []
148
+ all_variables.each do |pin|
149
+ if pin.return_type.defined?
150
+ declared = pin.typify(api_map)
151
+ if declared.defined?
152
+ if rules.validate_tags?
153
+ inferred = pin.probe(api_map)
154
+ if inferred.undefined?
155
+ next if rules.ignore_all_undefined?
156
+ if declared_externally?(pin)
157
+ ignored_pins.push pin
158
+ else
159
+ result.push Problem.new(pin.location, "Variable type could not be inferred for #{pin.name}", pin: pin)
160
+ end
161
+ else
162
+ unless (rules.rank > 1 ? types_match?(api_map, declared, inferred) : any_types_match?(api_map, declared, inferred))
163
+ result.push Problem.new(pin.location, "Declared type #{declared} does not match inferred type #{inferred} for variable #{pin.name}", pin: pin)
164
+ end
165
+ end
166
+ elsif declared_externally?(pin)
167
+ ignored_pins.push pin
168
+ end
169
+ elsif !pin.is_a?(Pin::Parameter)
170
+ result.push Problem.new(pin.location, "Unresolved type #{pin.return_type} for variable #{pin.name}", pin: pin)
171
+ end
172
+ else
173
+ inferred = pin.probe(api_map)
174
+ if inferred.undefined? && declared_externally?(pin)
175
+ ignored_pins.push pin
176
+ end
177
+ end
178
+ end
179
+ result
180
+ end
181
+
182
+ # @return [Array<Pin::BaseVariable>]
183
+ def all_variables
184
+ source_map.pins_by_class(Pin::BaseVariable) + source_map.locals.select { |pin| pin.is_a?(Pin::LocalVariable) }
185
+ end
186
+
187
+ def const_problems
188
+ return [] unless rules.validate_consts?
189
+ result = []
190
+ Solargraph::Parser::NodeMethods.const_nodes_from(source_map.source.node).each do |const|
191
+ rng = Solargraph::Range.from_node(const)
192
+ chain = Solargraph::Parser.chain(const, filename)
193
+ block_pin = source_map.locate_block_pin(rng.start.line, rng.start.column)
194
+ location = Location.new(filename, rng)
195
+ locals = source_map.locals_at(location)
196
+ pins = chain.define(api_map, block_pin, locals)
197
+ if pins.empty?
198
+ result.push Problem.new(location, "Unresolved constant #{Solargraph::Parser::NodeMethods.unpack_name(const)}")
199
+ @marked_ranges.push location.range
200
+ end
201
+ end
202
+ result
203
+ end
204
+
205
+ def call_problems
206
+ result = []
207
+ Solargraph::Parser::NodeMethods.call_nodes_from(source_map.source.node).each do |call|
208
+ rng = Solargraph::Range.from_node(call)
209
+ next if @marked_ranges.any? { |d| d.contain?(rng.start) }
210
+ chain = Solargraph::Parser.chain(call, 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
+ type = chain.infer(api_map, block_pin, locals)
215
+ if type.undefined? && !rules.ignore_all_undefined?
216
+ base = chain
217
+ missing = chain
218
+ found = nil
219
+ closest = ComplexType::UNDEFINED
220
+ until base.links.first.undefined?
221
+ found = base.define(api_map, block_pin, locals).first
222
+ break if found
223
+ missing = base
224
+ base = base.base
225
+ end
226
+ closest = found.typify(api_map) if found
227
+ if !found || (closest.defined? && internal_or_core?(found))
228
+ unless ignored_pins.include?(found)
229
+ result.push Problem.new(location, "Unresolved call to #{missing.links.last.word}")
230
+ @marked_ranges.push rng
231
+ end
232
+ end
233
+ end
234
+ result.concat argument_problems_for(chain, api_map, block_pin, locals, location)
235
+ end
236
+ result
237
+ end
238
+
239
+ def argument_problems_for chain, api_map, block_pin, locals, location
240
+ result = []
241
+ base = chain
242
+ until base.links.length == 1 && base.undefined?
243
+ pins = base.define(api_map, block_pin, locals)
244
+ if pins.first.is_a?(Pin::Method)
245
+ # @type [Pin::Method]
246
+ pin = pins.first
247
+ ap = if base.links.last.is_a?(Solargraph::Source::Chain::ZSuper)
248
+ arity_problems_for(pin, fake_args_for(block_pin), location)
249
+ else
250
+ arity_problems_for(pin, base.links.last.arguments, location)
251
+ end
252
+ unless ap.empty?
253
+ result.concat ap
254
+ break
255
+ end
256
+ break unless rules.validate_calls?
257
+ params = first_param_hash(pins)
258
+ pin.parameters.each_with_index do |par, idx|
259
+ argchain = base.links.last.arguments[idx]
260
+ if argchain.nil? && par.decl == :arg
261
+ result.push Problem.new(location, "Not enough arguments to #{pin.path}")
262
+ break
263
+ end
264
+ if argchain
265
+ if par.decl != :arg
266
+ result.concat kwarg_problems_for argchain, api_map, block_pin, locals, location, pin, params, idx
267
+ break
268
+ else
269
+ ptype = params.key?(par.name) ? params[par.name][:qualified] : ComplexType::UNDEFINED
270
+ if ptype.nil?
271
+ # @todo Some level (strong, I guess) should require the param here
272
+ else
273
+ argtype = argchain.infer(api_map, block_pin, locals)
274
+ if argtype.defined? && ptype.defined? && !any_types_match?(api_map, ptype, argtype)
275
+ result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}")
276
+ end
277
+ end
278
+ end
279
+ elsif par.rest?
280
+ next
281
+ elsif par.decl == :kwarg
282
+ result.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}")
283
+ break
284
+ end
285
+ end
286
+ end
287
+ base = base.base
288
+ end
289
+ result
290
+ end
291
+
292
+ def kwarg_problems_for argchain, api_map, block_pin, locals, location, pin, params, first
293
+ result = []
294
+ kwargs = convert_hash(argchain.node)
295
+ pin.parameters[first..-1].each_with_index do |par, cur|
296
+ idx = first + cur
297
+ argchain = kwargs[par.name.to_sym]
298
+ if par.decl == :kwrestarg || (par.decl == :optarg && idx == pin.parameters.length - 1 && par.asgn_code == '{}')
299
+ result.concat kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kwargs)
300
+ else
301
+ if argchain
302
+ data = params[par.name]
303
+ if data.nil?
304
+ # @todo Some level (strong, I guess) should require the param here
305
+ else
306
+ ptype = data[:qualified]
307
+ next if ptype.undefined?
308
+ argtype = argchain.infer(api_map, block_pin, locals)
309
+ if argtype.defined? && ptype && !any_types_match?(api_map, ptype, argtype)
310
+ result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}")
311
+ end
312
+ end
313
+ elsif par.decl == :kwarg
314
+ result.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}")
315
+ end
316
+ end
317
+ end
318
+ result
319
+ end
320
+
321
+ def kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kwargs)
322
+ result = []
323
+ kwargs.each_pair do |pname, argchain|
324
+ next unless params.key?(pname.to_s)
325
+ ptype = params[pname.to_s][:qualified]
326
+ argtype = argchain.infer(api_map, block_pin, locals)
327
+ if argtype.defined? && ptype && !any_types_match?(api_map, ptype, argtype)
328
+ result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{pname} expected #{ptype}, received #{argtype}")
329
+ end
330
+ end
331
+ result
332
+ end
333
+
334
+ # @param [Pin::Method]
335
+ # @return [Hash]
336
+ def param_hash(pin)
337
+ tags = pin.docstring.tags(:param)
338
+ return {} if tags.empty?
339
+ result = {}
340
+ tags.each do |tag|
341
+ next if tag.types.nil? || tag.types.empty?
342
+ result[tag.name.to_s] = {
343
+ tagged: tag.types.join(', '),
344
+ qualified: Solargraph::ComplexType.try_parse(*tag.types).qualify(api_map, pin.full_context.namespace)
345
+ }
346
+ end
347
+ result
348
+ end
349
+
350
+ # @param [Array<Pin::Method>]
351
+ # @return [Hash]
352
+ def first_param_hash(pins)
353
+ pins.each do |pin|
354
+ result = param_hash(pin)
355
+ return result unless result.empty?
356
+ end
357
+ {}
358
+ end
359
+
360
+ # @param pin [Pin::Base]
361
+ def internal? pin
362
+ return false if pin.nil?
363
+ pin.location && api_map.bundled?(pin.location.filename)
364
+ end
365
+
366
+ def internal_or_core? pin
367
+ internal?(pin) || api_map.yard_map.core_pins.include?(pin) || api_map.yard_map.stdlib_pins.include?(pin)
368
+ end
369
+
370
+ # @param pin [Pin::Base]
371
+ def external? pin
372
+ !internal? pin
373
+ end
374
+
375
+ def declared_externally? pin
376
+ return true if pin.assignment.nil?
377
+ chain = Solargraph::Parser.chain(pin.assignment, filename)
378
+ rng = Solargraph::Range.from_node(pin.assignment)
379
+ block_pin = source_map.locate_block_pin(rng.start.line, rng.start.column)
380
+ location = Location.new(filename, Range.from_node(pin.assignment))
381
+ locals = source_map.locals_at(location)
382
+ type = chain.infer(api_map, block_pin, locals)
383
+ if type.undefined? && !rules.ignore_all_undefined?
384
+ base = chain
385
+ missing = chain
386
+ found = nil
387
+ closest = ComplexType::UNDEFINED
388
+ until base.links.first.undefined?
389
+ found = base.define(api_map, block_pin, locals).first
390
+ break if found
391
+ missing = base
392
+ base = base.base
393
+ end
394
+ closest = found.typify(api_map) if found
395
+ if !found || closest.defined? || internal?(found)
396
+ return false
397
+ end
398
+ end
399
+ true
400
+ end
401
+
402
+ # @param pin [Pin::Method]
403
+ def arity_problems_for(pin, arguments, location)
404
+ return [] unless pin.explicit?
405
+ return [] if pin.parameters.empty? && arguments.empty?
406
+ if pin.parameters.empty?
407
+ # Functions tagged param_tuple accepts two arguments (e.g., Hash#[]=)
408
+ return [] if pin.docstring.tag(:param_tuple) && arguments.length == 2
409
+ return [] if arguments.length == 1 && arguments.last.links.last.is_a?(Source::Chain::BlockVariable)
410
+ return [Problem.new(location, "Too many arguments to #{pin.path}")]
411
+ end
412
+ unchecked = arguments.clone
413
+ add_params = 0
414
+ if unchecked.empty? && pin.parameters.any? { |param| param.decl == :kwarg }
415
+ return [Problem.new(location, "Missing keyword arguments to #{pin.path}")]
416
+ end
417
+ settled_kwargs = 0
418
+ unless unchecked.empty?
419
+ if Parser.is_ast_node?(unchecked.last.node) && splatted_call?(unchecked.last.node)
420
+ settled_kwargs = pin.parameters.count(&:keyword?)
421
+ else
422
+ kwargs = convert_hash(unchecked.last.node)
423
+ if pin.parameters.any? { |param| [:kwarg, :kwoptarg].include?(param.decl) || param.kwrestarg? }
424
+ if kwargs.empty?
425
+ add_params += 1
426
+ else
427
+ unchecked.pop
428
+ pin.parameters.each do |param|
429
+ next unless param.keyword?
430
+ if kwargs.key?(param.name.to_sym)
431
+ kwargs.delete param.name.to_sym
432
+ settled_kwargs += 1
433
+ elsif param.decl == :kwarg
434
+ return [Problem.new(location, "Missing keyword argument #{param.name} to #{pin.path}")]
435
+ end
436
+ end
437
+ kwargs.clear if pin.parameters.any?(&:kwrestarg?)
438
+ unless kwargs.empty?
439
+ return [Problem.new(location, "Unrecognized keyword argument #{kwargs.keys.first} to #{pin.path}")]
440
+ end
441
+ end
442
+ end
443
+ end
444
+ end
445
+ req = required_param_count(pin)
446
+ if req + add_params < unchecked.length
447
+ return [] if pin.parameters.any?(&:rest?)
448
+ opt = optional_param_count(pin)
449
+ return [] if unchecked.length <= req + opt
450
+ if unchecked.length == req + opt + 1 && unchecked.last.links.last.is_a?(Source::Chain::BlockVariable)
451
+ return []
452
+ end
453
+ if req + add_params + 1 == unchecked.length && splatted_call?(unchecked.last.node) && (pin.parameters.map(&:decl) & [:kwarg, :kwoptarg, :kwrestarg]).any?
454
+ return []
455
+ end
456
+ return [Problem.new(location, "Too many arguments to #{pin.path}")]
457
+ elsif unchecked.length < req - settled_kwargs && (arguments.empty? || !arguments.last.splat?)
458
+ return [Problem.new(location, "Not enough arguments to #{pin.path}")]
459
+ end
460
+ []
461
+ end
462
+
463
+ # @param pin [Pin::Method]
464
+ def required_param_count(pin)
465
+ pin.parameters.sum { |param| %i[arg kwarg].include?(param.decl) ? 1 : 0 }
466
+ end
467
+
468
+ # @param pin [Pin::Method]
469
+ def optional_param_count(pin)
470
+ count = 0
471
+ pin.parameters.each do |param|
472
+ next unless param.decl == :optarg
473
+ count += 1
474
+ end
475
+ count
476
+ end
477
+
478
+ def abstract? pin
479
+ pin.docstring.has_tag?(:abstract) ||
480
+ (pin.closure && pin.closure.docstring.has_tag?(:abstract))
481
+ end
482
+
483
+ def fake_args_for(pin)
484
+ args = []
485
+ with_opts = false
486
+ with_block = false
487
+ pin.parameters.each do |pin|
488
+ if [:kwarg, :kwoptarg, :kwrestarg].include?(pin.decl)
489
+ with_opts = true
490
+ elsif pin.decl == :block
491
+ with_block = true
492
+ elsif pin.decl == :restarg
493
+ args.push Solargraph::Source::Chain.new([Solargraph::Source::Chain::Variable.new(pin.name)], nil, true)
494
+ else
495
+ args.push Solargraph::Source::Chain.new([Solargraph::Source::Chain::Variable.new(pin.name)])
496
+ end
497
+ end
498
+ args.push Solargraph::Parser.chain_string('{}') if with_opts
499
+ args.push Solargraph::Parser.chain_string('&') if with_block
500
+ args
501
+ end
502
+ end
503
+ end