solargraph 0.46.0 → 0.54.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +1 -0
- data/.github/workflows/plugins.yml +40 -0
- data/.github/workflows/rspec.yml +37 -41
- data/.github/workflows/typecheck.yml +34 -0
- data/.gitignore +9 -9
- data/.rspec +2 -2
- data/.yardopts +2 -2
- data/CHANGELOG.md +1338 -1115
- data/Gemfile +0 -0
- data/LICENSE +1 -1
- data/README.md +131 -128
- data/Rakefile +0 -0
- data/SPONSORS.md +10 -18
- data/bin/solargraph +0 -0
- data/lib/solargraph/api_map/cache.rb +109 -70
- data/lib/solargraph/api_map/index.rb +167 -0
- data/lib/solargraph/api_map/source_to_yard.rb +88 -81
- data/lib/solargraph/api_map/store.rb +260 -256
- data/lib/solargraph/api_map.rb +870 -686
- data/lib/solargraph/bench.rb +44 -27
- data/lib/solargraph/cache.rb +77 -0
- data/lib/solargraph/complex_type/type_methods.rb +217 -130
- data/lib/solargraph/complex_type/unique_type.rb +386 -75
- data/lib/solargraph/complex_type.rb +394 -221
- data/lib/solargraph/convention/base.rb +33 -33
- data/lib/solargraph/convention/gemfile.rb +15 -15
- data/lib/solargraph/convention/gemspec.rb +22 -22
- data/lib/solargraph/convention/rakefile.rb +17 -0
- data/lib/solargraph/convention.rb +47 -47
- data/lib/solargraph/converters/dd.rb +17 -12
- data/lib/solargraph/converters/dl.rb +15 -12
- data/lib/solargraph/converters/dt.rb +15 -12
- data/lib/solargraph/converters/misc.rb +1 -1
- data/lib/solargraph/diagnostics/base.rb +29 -29
- data/lib/solargraph/diagnostics/require_not_found.rb +53 -53
- data/lib/solargraph/diagnostics/rubocop.rb +113 -98
- data/lib/solargraph/diagnostics/rubocop_helpers.rb +66 -63
- data/lib/solargraph/diagnostics/severities.rb +15 -15
- data/lib/solargraph/diagnostics/type_check.rb +55 -54
- data/lib/solargraph/diagnostics/update_errors.rb +41 -41
- data/lib/solargraph/diagnostics.rb +55 -55
- data/lib/solargraph/doc_map.rb +188 -0
- data/lib/solargraph/environ.rb +45 -45
- data/lib/solargraph/equality.rb +33 -0
- data/lib/solargraph/gem_pins.rb +72 -0
- data/lib/solargraph/language_server/completion_item_kinds.rb +35 -35
- data/lib/solargraph/language_server/error_codes.rb +20 -20
- data/lib/solargraph/language_server/host/diagnoser.rb +89 -89
- data/lib/solargraph/language_server/host/dispatch.rb +128 -111
- data/lib/solargraph/language_server/host/message_worker.rb +106 -59
- data/lib/solargraph/language_server/host/sources.rb +99 -156
- data/lib/solargraph/language_server/host.rb +861 -865
- data/lib/solargraph/language_server/message/base.rb +96 -89
- data/lib/solargraph/language_server/message/cancel_request.rb +13 -13
- data/lib/solargraph/language_server/message/client/register_capability.rb +15 -15
- data/lib/solargraph/language_server/message/client.rb +11 -11
- data/lib/solargraph/language_server/message/completion_item/resolve.rb +60 -58
- data/lib/solargraph/language_server/message/completion_item.rb +11 -11
- data/lib/solargraph/language_server/message/exit_notification.rb +13 -13
- data/lib/solargraph/language_server/message/extended/check_gem_version.rb +112 -100
- data/lib/solargraph/language_server/message/extended/document.rb +20 -20
- data/lib/solargraph/language_server/message/extended/document_gems.rb +32 -32
- data/lib/solargraph/language_server/message/extended/download_core.rb +19 -23
- data/lib/solargraph/language_server/message/extended/environment.rb +25 -25
- data/lib/solargraph/language_server/message/extended/search.rb +20 -20
- data/lib/solargraph/language_server/message/extended.rb +21 -21
- data/lib/solargraph/language_server/message/initialize.rb +191 -162
- data/lib/solargraph/language_server/message/initialized.rb +28 -27
- data/lib/solargraph/language_server/message/method_not_found.rb +16 -16
- data/lib/solargraph/language_server/message/method_not_implemented.rb +14 -14
- data/lib/solargraph/language_server/message/shutdown.rb +13 -13
- data/lib/solargraph/language_server/message/text_document/base.rb +19 -19
- data/lib/solargraph/language_server/message/text_document/code_action.rb +17 -17
- data/lib/solargraph/language_server/message/text_document/completion.rb +56 -59
- data/lib/solargraph/language_server/message/text_document/definition.rb +38 -38
- data/lib/solargraph/language_server/message/text_document/did_change.rb +15 -15
- data/lib/solargraph/language_server/message/text_document/did_close.rb +15 -15
- data/lib/solargraph/language_server/message/text_document/did_open.rb +15 -15
- data/lib/solargraph/language_server/message/text_document/did_save.rb +17 -17
- data/lib/solargraph/language_server/message/text_document/document_highlight.rb +16 -16
- data/lib/solargraph/language_server/message/text_document/document_symbol.rb +26 -23
- data/lib/solargraph/language_server/message/text_document/folding_range.rb +26 -26
- data/lib/solargraph/language_server/message/text_document/formatting.rb +131 -126
- data/lib/solargraph/language_server/message/text_document/hover.rb +58 -54
- data/lib/solargraph/language_server/message/text_document/on_type_formatting.rb +34 -34
- data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +11 -11
- data/lib/solargraph/language_server/message/text_document/references.rb +16 -16
- data/lib/solargraph/language_server/message/text_document/rename.rb +19 -19
- data/lib/solargraph/language_server/message/text_document/signature_help.rb +24 -29
- data/lib/solargraph/language_server/message/text_document/type_definition.rb +24 -0
- data/lib/solargraph/language_server/message/text_document.rb +28 -28
- data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +35 -30
- data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +40 -33
- data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +24 -24
- data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +23 -23
- data/lib/solargraph/language_server/message/workspace.rb +14 -14
- data/lib/solargraph/language_server/message.rb +94 -93
- data/lib/solargraph/language_server/message_types.rb +14 -14
- data/lib/solargraph/language_server/progress.rb +135 -0
- data/lib/solargraph/language_server/request.rb +24 -24
- data/lib/solargraph/language_server/symbol_kinds.rb +36 -36
- data/lib/solargraph/language_server/transport/adapter.rb +68 -53
- data/lib/solargraph/language_server/transport/data_reader.rb +74 -72
- data/lib/solargraph/language_server/transport.rb +13 -13
- data/lib/solargraph/language_server/uri_helpers.rb +49 -49
- data/lib/solargraph/language_server.rb +20 -19
- data/lib/solargraph/library.rb +663 -546
- data/lib/solargraph/location.rb +58 -37
- data/lib/solargraph/logging.rb +27 -27
- data/lib/solargraph/page.rb +89 -83
- data/lib/solargraph/parser/comment_ripper.rb +56 -52
- data/lib/solargraph/parser/node_methods.rb +83 -43
- data/lib/solargraph/parser/node_processor/base.rb +87 -77
- data/lib/solargraph/parser/node_processor.rb +45 -43
- data/lib/solargraph/parser/{legacy → parser_gem}/class_methods.rb +153 -135
- data/lib/solargraph/parser/{legacy → parser_gem}/flawed_builder.rb +18 -16
- data/lib/solargraph/parser/{legacy → parser_gem}/node_chainer.rb +164 -148
- data/lib/solargraph/parser/parser_gem/node_methods.rb +495 -0
- data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/alias_node.rb +23 -23
- data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +57 -0
- data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/begin_node.rb +15 -15
- data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/block_node.rb +43 -42
- data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/casgn_node.rb +35 -25
- data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/cvasgn_node.rb +23 -23
- data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/def_node.rb +50 -63
- data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/defs_node.rb +36 -36
- data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/gvasgn_node.rb +23 -23
- data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/ivasgn_node.rb +38 -38
- data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/lvasgn_node.rb +28 -28
- data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +53 -0
- data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/namespace_node.rb +39 -39
- data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/orasgn_node.rb +16 -16
- data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/resbody_node.rb +36 -36
- data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +42 -0
- data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/send_node.rb +259 -257
- data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/sym_node.rb +18 -18
- data/lib/solargraph/parser/parser_gem/node_processors.rb +56 -0
- data/lib/solargraph/parser/parser_gem.rb +12 -0
- data/lib/solargraph/parser/region.rb +66 -66
- data/lib/solargraph/parser/snippet.rb +15 -13
- data/lib/solargraph/parser.rb +22 -26
- data/lib/solargraph/pin/base.rb +378 -296
- data/lib/solargraph/pin/base_variable.rb +118 -84
- data/lib/solargraph/pin/block.rb +101 -72
- data/lib/solargraph/pin/callable.rb +147 -0
- data/lib/solargraph/pin/class_variable.rb +8 -8
- data/lib/solargraph/pin/closure.rb +57 -37
- data/lib/solargraph/pin/common.rb +70 -70
- data/lib/solargraph/pin/constant.rb +43 -43
- data/lib/solargraph/pin/conversions.rb +123 -96
- data/lib/solargraph/pin/delegated_method.rb +101 -0
- data/lib/solargraph/pin/documenting.rb +98 -105
- data/lib/solargraph/pin/duck_method.rb +16 -16
- data/lib/solargraph/pin/global_variable.rb +8 -8
- data/lib/solargraph/pin/instance_variable.rb +34 -30
- data/lib/solargraph/pin/keyword.rb +15 -15
- data/lib/solargraph/pin/keyword_param.rb +8 -8
- data/lib/solargraph/pin/local_variable.rb +67 -55
- data/lib/solargraph/pin/method.rb +527 -245
- data/lib/solargraph/pin/method_alias.rb +31 -31
- data/lib/solargraph/pin/namespace.rb +107 -91
- data/lib/solargraph/pin/parameter.rb +212 -201
- data/lib/solargraph/pin/proxy_type.rb +29 -29
- data/lib/solargraph/pin/reference/extend.rb +10 -10
- data/lib/solargraph/pin/reference/include.rb +10 -10
- data/lib/solargraph/pin/reference/override.rb +29 -29
- data/lib/solargraph/pin/reference/prepend.rb +10 -10
- data/lib/solargraph/pin/reference/require.rb +14 -14
- data/lib/solargraph/pin/reference/superclass.rb +10 -10
- data/lib/solargraph/pin/reference.rb +22 -14
- data/lib/solargraph/pin/search.rb +56 -56
- data/lib/solargraph/pin/signature.rb +17 -0
- data/lib/solargraph/pin/singleton.rb +11 -11
- data/lib/solargraph/pin/symbol.rb +47 -47
- data/lib/solargraph/pin.rb +41 -37
- data/lib/solargraph/position.rb +107 -100
- data/lib/solargraph/range.rb +98 -95
- data/lib/solargraph/rbs_map/conversions.rb +646 -0
- data/lib/solargraph/rbs_map/core_fills.rb +50 -0
- data/lib/solargraph/rbs_map/core_map.rb +28 -0
- data/lib/solargraph/rbs_map/stdlib_map.rb +33 -0
- data/lib/solargraph/rbs_map.rb +93 -0
- data/lib/solargraph/server_methods.rb +16 -16
- data/lib/solargraph/shell.rb +269 -226
- data/lib/solargraph/source/chain/array.rb +33 -0
- data/lib/solargraph/source/chain/block_symbol.rb +13 -0
- data/lib/solargraph/source/chain/block_variable.rb +13 -13
- data/lib/solargraph/source/chain/call.rb +303 -204
- data/lib/solargraph/source/chain/class_variable.rb +13 -13
- data/lib/solargraph/source/chain/constant.rb +89 -75
- data/lib/solargraph/source/chain/global_variable.rb +13 -13
- data/lib/solargraph/source/chain/hash.rb +33 -28
- data/lib/solargraph/source/chain/head.rb +19 -19
- data/lib/solargraph/source/chain/if.rb +28 -0
- data/lib/solargraph/source/chain/instance_variable.rb +13 -13
- data/lib/solargraph/source/chain/link.rb +98 -71
- data/lib/solargraph/source/chain/literal.rb +28 -23
- data/lib/solargraph/source/chain/or.rb +23 -23
- data/lib/solargraph/source/chain/q_call.rb +11 -11
- data/lib/solargraph/source/chain/variable.rb +13 -13
- data/lib/solargraph/source/chain/z_super.rb +30 -30
- data/lib/solargraph/source/chain.rb +252 -164
- data/lib/solargraph/source/change.rb +82 -79
- data/lib/solargraph/source/cursor.rb +167 -164
- data/lib/solargraph/source/source_chainer.rb +194 -191
- data/lib/solargraph/source/updater.rb +55 -54
- data/lib/solargraph/source.rb +495 -522
- data/lib/solargraph/source_map/clip.rb +232 -224
- data/lib/solargraph/source_map/completion.rb +23 -23
- data/lib/solargraph/source_map/data.rb +30 -0
- data/lib/solargraph/source_map/mapper.rb +255 -212
- data/lib/solargraph/source_map.rb +217 -180
- data/lib/solargraph/type_checker/checks.rb +120 -99
- data/lib/solargraph/type_checker/param_def.rb +35 -35
- data/lib/solargraph/type_checker/problem.rb +32 -32
- data/lib/solargraph/type_checker/rules.rb +62 -57
- data/lib/solargraph/type_checker.rb +672 -543
- data/lib/solargraph/version.rb +5 -5
- data/lib/solargraph/views/environment.erb +56 -58
- data/lib/solargraph/workspace/config.rb +239 -231
- data/lib/solargraph/workspace.rb +239 -215
- data/lib/solargraph/yard_map/cache.rb +25 -19
- data/lib/solargraph/yard_map/helpers.rb +16 -16
- data/lib/solargraph/yard_map/mapper/to_constant.rb +26 -25
- data/lib/solargraph/yard_map/mapper/to_method.rb +94 -78
- data/lib/solargraph/yard_map/mapper/to_namespace.rb +28 -27
- data/lib/solargraph/yard_map/mapper.rb +78 -77
- data/lib/solargraph/yard_map/to_method.rb +86 -79
- data/lib/solargraph/yard_map.rb +18 -460
- data/lib/solargraph/yard_tags.rb +20 -0
- data/lib/solargraph/yardoc.rb +52 -0
- data/lib/solargraph.rb +72 -69
- data/solargraph.gemspec +21 -10
- metadata +184 -115
- data/.travis.yml +0 -19
- data/lib/solargraph/api_map/bundler_methods.rb +0 -22
- data/lib/solargraph/compat.rb +0 -37
- data/lib/solargraph/convention/rspec.rb +0 -30
- data/lib/solargraph/documentor.rb +0 -76
- data/lib/solargraph/language_server/host/cataloger.rb +0 -56
- data/lib/solargraph/parser/legacy/node_methods.rb +0 -325
- data/lib/solargraph/parser/legacy/node_processors/alias_node.rb +0 -23
- data/lib/solargraph/parser/legacy/node_processors/args_node.rb +0 -35
- data/lib/solargraph/parser/legacy/node_processors/begin_node.rb +0 -15
- data/lib/solargraph/parser/legacy/node_processors/def_node.rb +0 -63
- data/lib/solargraph/parser/legacy/node_processors/sclass_node.rb +0 -21
- data/lib/solargraph/parser/legacy/node_processors.rb +0 -54
- data/lib/solargraph/parser/legacy.rb +0 -12
- data/lib/solargraph/parser/rubyvm/class_methods.rb +0 -144
- data/lib/solargraph/parser/rubyvm/node_chainer.rb +0 -160
- data/lib/solargraph/parser/rubyvm/node_methods.rb +0 -315
- data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +0 -85
- data/lib/solargraph/parser/rubyvm/node_processors/block_node.rb +0 -42
- data/lib/solargraph/parser/rubyvm/node_processors/casgn_node.rb +0 -22
- data/lib/solargraph/parser/rubyvm/node_processors/cvasgn_node.rb +0 -23
- data/lib/solargraph/parser/rubyvm/node_processors/defs_node.rb +0 -57
- data/lib/solargraph/parser/rubyvm/node_processors/gvasgn_node.rb +0 -23
- data/lib/solargraph/parser/rubyvm/node_processors/ivasgn_node.rb +0 -38
- data/lib/solargraph/parser/rubyvm/node_processors/kw_arg_node.rb +0 -39
- data/lib/solargraph/parser/rubyvm/node_processors/lit_node.rb +0 -20
- data/lib/solargraph/parser/rubyvm/node_processors/lvasgn_node.rb +0 -27
- data/lib/solargraph/parser/rubyvm/node_processors/namespace_node.rb +0 -39
- data/lib/solargraph/parser/rubyvm/node_processors/opt_arg_node.rb +0 -26
- data/lib/solargraph/parser/rubyvm/node_processors/orasgn_node.rb +0 -15
- data/lib/solargraph/parser/rubyvm/node_processors/resbody_node.rb +0 -45
- data/lib/solargraph/parser/rubyvm/node_processors/sclass_node.rb +0 -21
- data/lib/solargraph/parser/rubyvm/node_processors/scope_node.rb +0 -15
- data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +0 -277
- data/lib/solargraph/parser/rubyvm/node_processors/sym_node.rb +0 -18
- data/lib/solargraph/parser/rubyvm/node_processors.rb +0 -63
- data/lib/solargraph/parser/rubyvm.rb +0 -40
- data/lib/solargraph/yard_map/core_docs.rb +0 -170
- data/lib/solargraph/yard_map/core_fills.rb +0 -208
- data/lib/solargraph/yard_map/core_gen.rb +0 -76
- data/lib/solargraph/yard_map/rdoc_to_yard.rb +0 -140
- data/lib/solargraph/yard_map/stdlib_fills.rb +0 -43
- data/lib/yard-solargraph.rb +0 -33
- data/yardoc/2.2.2.tar.gz +0 -0
@@ -1,543 +1,672 @@
|
|
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
|
-
@
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
.concat
|
47
|
-
.concat
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
# @
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
# @
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
if
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
# @
|
421
|
-
def
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
if
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
def
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
def
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
end
|
542
|
-
|
543
|
-
|
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, nil]
|
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
|
+
# @type [Array<Range>]
|
33
|
+
@marked_ranges = []
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [SourceMap]
|
37
|
+
def source_map
|
38
|
+
@source_map ||= api_map.source_map(filename)
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Array<Problem>]
|
42
|
+
def problems
|
43
|
+
@problems ||= begin
|
44
|
+
without_ignored(
|
45
|
+
method_tag_problems
|
46
|
+
.concat variable_type_tag_problems
|
47
|
+
.concat const_problems
|
48
|
+
.concat call_problems
|
49
|
+
)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class << self
|
54
|
+
# @param filename [String]
|
55
|
+
# @param level [Symbol]
|
56
|
+
# @return [self]
|
57
|
+
def load filename, level = :normal
|
58
|
+
source = Solargraph::Source.load(filename)
|
59
|
+
api_map = Solargraph::ApiMap.new
|
60
|
+
api_map.map(source)
|
61
|
+
new(filename, api_map: api_map, level: level)
|
62
|
+
end
|
63
|
+
|
64
|
+
# @param code [String]
|
65
|
+
# @param filename [String, nil]
|
66
|
+
# @param level [Symbol]
|
67
|
+
# @return [self]
|
68
|
+
def load_string code, filename = nil, level = :normal
|
69
|
+
source = Solargraph::Source.load_string(code, filename)
|
70
|
+
api_map = Solargraph::ApiMap.new
|
71
|
+
api_map.map(source)
|
72
|
+
new(filename, api_map: api_map, level: level)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
# @return [Array<Problem>]
|
79
|
+
def method_tag_problems
|
80
|
+
result = []
|
81
|
+
# @param pin [Pin::Method]
|
82
|
+
source_map.pins_by_class(Pin::Method).each do |pin|
|
83
|
+
result.concat method_return_type_problems_for(pin)
|
84
|
+
result.concat method_param_type_problems_for(pin)
|
85
|
+
end
|
86
|
+
result
|
87
|
+
end
|
88
|
+
|
89
|
+
# @param pin [Pin::Method]
|
90
|
+
# @return [Array<Problem>]
|
91
|
+
def method_return_type_problems_for pin
|
92
|
+
return [] if pin.is_a?(Pin::MethodAlias)
|
93
|
+
result = []
|
94
|
+
declared = pin.typify(api_map).self_to_type(pin.full_context).qualify(api_map, pin.full_context.tag)
|
95
|
+
if declared.undefined?
|
96
|
+
if pin.return_type.undefined? && rules.require_type_tags?
|
97
|
+
if pin.attribute?
|
98
|
+
inferred = pin.probe(api_map).self_to_type(pin.full_context)
|
99
|
+
result.push Problem.new(pin.location, "Missing @return tag for #{pin.path}", pin: pin) unless inferred.defined?
|
100
|
+
else
|
101
|
+
result.push Problem.new(pin.location, "Missing @return tag for #{pin.path}", pin: pin)
|
102
|
+
end
|
103
|
+
elsif pin.return_type.defined? && !resolved_constant?(pin)
|
104
|
+
result.push Problem.new(pin.location, "Unresolved return type #{pin.return_type} for #{pin.path}", pin: pin)
|
105
|
+
elsif rules.must_tag_or_infer? && pin.probe(api_map).undefined?
|
106
|
+
result.push Problem.new(pin.location, "Untyped method #{pin.path} could not be inferred")
|
107
|
+
end
|
108
|
+
elsif rules.validate_tags?
|
109
|
+
unless pin.node.nil? || declared.void? || virtual_pin?(pin) || abstract?(pin)
|
110
|
+
inferred = pin.probe(api_map).self_to_type(pin.full_context)
|
111
|
+
if inferred.undefined?
|
112
|
+
unless rules.ignore_all_undefined? || external?(pin)
|
113
|
+
result.push Problem.new(pin.location, "#{pin.path} return type could not be inferred", pin: pin)
|
114
|
+
end
|
115
|
+
else
|
116
|
+
unless (rules.require_all_return_types_match_inferred? ? all_types_match?(api_map, inferred, declared) : any_types_match?(api_map, declared, inferred))
|
117
|
+
result.push Problem.new(pin.location, "Declared return type #{declared.rooted_tags} does not match inferred type #{inferred.rooted_tags} for #{pin.path}", pin: pin)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
result
|
123
|
+
end
|
124
|
+
|
125
|
+
# @todo This is not optimal. A better solution would probably be to mix
|
126
|
+
# namespace alias into types at the ApiMap level.
|
127
|
+
#
|
128
|
+
# @param pin [Pin::Base]
|
129
|
+
# @return [Boolean]
|
130
|
+
def resolved_constant? pin
|
131
|
+
return true if pin.typify(api_map).defined?
|
132
|
+
constant_pins = api_map.get_constants('', *pin.closure.gates)
|
133
|
+
.select { |p| p.name == pin.return_type.namespace }
|
134
|
+
return true if constant_pins.find { |p| p.typify(api_map).defined? }
|
135
|
+
# will need to probe when a constant name is assigned to a
|
136
|
+
# class/module (alias)
|
137
|
+
return true if constant_pins.find { |p| p.probe(api_map).defined? }
|
138
|
+
false
|
139
|
+
end
|
140
|
+
|
141
|
+
# @param pin [Pin::Base]
|
142
|
+
def virtual_pin? pin
|
143
|
+
pin.location && source_map.source.comment_at?(pin.location.range.ending)
|
144
|
+
end
|
145
|
+
|
146
|
+
# @param pin [Pin::Method]
|
147
|
+
# @return [Array<Problem>]
|
148
|
+
def method_param_type_problems_for pin
|
149
|
+
stack = api_map.get_method_stack(pin.namespace, pin.name, scope: pin.scope)
|
150
|
+
params = first_param_hash(stack)
|
151
|
+
result = []
|
152
|
+
if rules.require_type_tags?
|
153
|
+
pin.signatures.each do |sig|
|
154
|
+
sig.parameters.each do |par|
|
155
|
+
break if par.decl == :restarg || par.decl == :kwrestarg || par.decl == :blockarg
|
156
|
+
unless params[par.name]
|
157
|
+
if pin.attribute?
|
158
|
+
inferred = pin.probe(api_map).self_to_type(pin.full_context)
|
159
|
+
if inferred.undefined?
|
160
|
+
result.push Problem.new(pin.location, "Missing @param tag for #{par.name} on #{pin.path}", pin: pin)
|
161
|
+
end
|
162
|
+
else
|
163
|
+
result.push Problem.new(pin.location, "Missing @param tag for #{par.name} on #{pin.path}", pin: pin)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
# @todo Should be able to probe type of name and data here
|
170
|
+
# @param name [String]
|
171
|
+
# @param data [Hash{Symbol => BasicObject}]
|
172
|
+
params.each_pair do |name, data|
|
173
|
+
# @type [ComplexType]
|
174
|
+
type = data[:qualified]
|
175
|
+
if type.undefined?
|
176
|
+
result.push Problem.new(pin.location, "Unresolved type #{data[:tagged]} for #{name} param on #{pin.path}", pin: pin)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
result
|
180
|
+
end
|
181
|
+
|
182
|
+
# @return [Array<Pin::Base>]
|
183
|
+
def ignored_pins
|
184
|
+
@ignored_pins ||= []
|
185
|
+
end
|
186
|
+
|
187
|
+
# @return [Array<Problem>]
|
188
|
+
def variable_type_tag_problems
|
189
|
+
result = []
|
190
|
+
all_variables.each do |pin|
|
191
|
+
if pin.return_type.defined?
|
192
|
+
declared = pin.typify(api_map)
|
193
|
+
next if declared.duck_type?
|
194
|
+
if declared.defined?
|
195
|
+
if rules.validate_tags?
|
196
|
+
inferred = pin.probe(api_map)
|
197
|
+
if inferred.undefined?
|
198
|
+
next if rules.ignore_all_undefined?
|
199
|
+
if declared_externally?(pin)
|
200
|
+
ignored_pins.push pin
|
201
|
+
else
|
202
|
+
result.push Problem.new(pin.location, "Variable type could not be inferred for #{pin.name}", pin: pin)
|
203
|
+
end
|
204
|
+
else
|
205
|
+
unless any_types_match?(api_map, declared, inferred)
|
206
|
+
result.push Problem.new(pin.location, "Declared type #{declared} does not match inferred type #{inferred} for variable #{pin.name}", pin: pin)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
elsif declared_externally?(pin)
|
210
|
+
ignored_pins.push pin
|
211
|
+
end
|
212
|
+
elsif !pin.is_a?(Pin::Parameter) && !resolved_constant?(pin)
|
213
|
+
result.push Problem.new(pin.location, "Unresolved type #{pin.return_type} for variable #{pin.name}", pin: pin)
|
214
|
+
end
|
215
|
+
else
|
216
|
+
inferred = pin.probe(api_map)
|
217
|
+
if inferred.undefined? && declared_externally?(pin)
|
218
|
+
ignored_pins.push pin
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
result
|
223
|
+
end
|
224
|
+
|
225
|
+
# @return [Array<Pin::BaseVariable>]
|
226
|
+
def all_variables
|
227
|
+
source_map.pins_by_class(Pin::BaseVariable) + source_map.locals.select { |pin| pin.is_a?(Pin::LocalVariable) }
|
228
|
+
end
|
229
|
+
|
230
|
+
# @return [Array<Problem>]
|
231
|
+
def const_problems
|
232
|
+
return [] unless rules.validate_consts?
|
233
|
+
result = []
|
234
|
+
Solargraph::Parser::NodeMethods.const_nodes_from(source_map.source.node).each do |const|
|
235
|
+
rng = Solargraph::Range.from_node(const)
|
236
|
+
chain = Solargraph::Parser.chain(const, filename)
|
237
|
+
block_pin = source_map.locate_block_pin(rng.start.line, rng.start.column)
|
238
|
+
location = Location.new(filename, rng)
|
239
|
+
locals = source_map.locals_at(location)
|
240
|
+
pins = chain.define(api_map, block_pin, locals)
|
241
|
+
if pins.empty?
|
242
|
+
result.push Problem.new(location, "Unresolved constant #{Solargraph::Parser::NodeMethods.unpack_name(const)}")
|
243
|
+
@marked_ranges.push location.range
|
244
|
+
end
|
245
|
+
end
|
246
|
+
result
|
247
|
+
end
|
248
|
+
|
249
|
+
# @return [Array<Problem>]
|
250
|
+
def call_problems
|
251
|
+
result = []
|
252
|
+
Solargraph::Parser::NodeMethods.call_nodes_from(source_map.source.node).each do |call|
|
253
|
+
rng = Solargraph::Range.from_node(call)
|
254
|
+
next if @marked_ranges.any? { |d| d.contain?(rng.start) }
|
255
|
+
chain = Solargraph::Parser.chain(call, filename)
|
256
|
+
block_pin = source_map.locate_block_pin(rng.start.line, rng.start.column)
|
257
|
+
location = Location.new(filename, rng)
|
258
|
+
locals = source_map.locals_at(location)
|
259
|
+
type = chain.infer(api_map, block_pin, locals)
|
260
|
+
if type.undefined? && !rules.ignore_all_undefined?
|
261
|
+
base = chain
|
262
|
+
missing = chain
|
263
|
+
found = nil
|
264
|
+
closest = ComplexType::UNDEFINED
|
265
|
+
until base.links.first.undefined?
|
266
|
+
found = base.define(api_map, block_pin, locals).first
|
267
|
+
break if found
|
268
|
+
missing = base
|
269
|
+
base = base.base
|
270
|
+
end
|
271
|
+
closest = found.typify(api_map) if found
|
272
|
+
# @todo remove the internal_or_core? check at a higher-than-strict level
|
273
|
+
if !found || found.is_a?(Pin::BaseVariable) || (closest.defined? && internal_or_core?(found))
|
274
|
+
unless closest.generic? || ignored_pins.include?(found)
|
275
|
+
result.push Problem.new(location, "Unresolved call to #{missing.links.last.word}")
|
276
|
+
@marked_ranges.push rng
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
result.concat argument_problems_for(chain, api_map, block_pin, locals, location)
|
281
|
+
end
|
282
|
+
result
|
283
|
+
end
|
284
|
+
|
285
|
+
# @param chain [Solargraph::Source::Chain]
|
286
|
+
# @param api_map [Solargraph::ApiMap]
|
287
|
+
# @param block_pin [Solargraph::Pin::Base]
|
288
|
+
# @param locals [Array<Solargraph::Pin::Base>]
|
289
|
+
# @param location [Solargraph::Location]
|
290
|
+
# @return [Array<Problem>]
|
291
|
+
def argument_problems_for chain, api_map, block_pin, locals, location
|
292
|
+
result = []
|
293
|
+
base = chain
|
294
|
+
until base.links.length == 1 && base.undefined?
|
295
|
+
last_base_link = base.links.last
|
296
|
+
break unless last_base_link.is_a?(Solargraph::Source::Chain::Call)
|
297
|
+
|
298
|
+
arguments = last_base_link.arguments
|
299
|
+
|
300
|
+
pins = base.define(api_map, block_pin, locals)
|
301
|
+
|
302
|
+
first_pin = pins.first
|
303
|
+
if first_pin.is_a?(Pin::DelegatedMethod) && !first_pin.resolvable?(api_map)
|
304
|
+
# Do nothing, as we can't find the actual method implementation
|
305
|
+
elsif first_pin.is_a?(Pin::Method)
|
306
|
+
# @type [Pin::Method]
|
307
|
+
pin = first_pin
|
308
|
+
ap = if base.links.last.is_a?(Solargraph::Source::Chain::ZSuper)
|
309
|
+
arity_problems_for(pin, fake_args_for(block_pin), location)
|
310
|
+
elsif pin.path == 'Class#new'
|
311
|
+
fqns = if base.links.one?
|
312
|
+
block_pin.namespace
|
313
|
+
else
|
314
|
+
base.base.infer(api_map, block_pin, locals).namespace
|
315
|
+
end
|
316
|
+
init = api_map.get_method_stack(fqns, 'initialize').first
|
317
|
+
init ? arity_problems_for(init, arguments, location) : []
|
318
|
+
else
|
319
|
+
arity_problems_for(pin, arguments, location)
|
320
|
+
end
|
321
|
+
unless ap.empty?
|
322
|
+
result.concat ap
|
323
|
+
break
|
324
|
+
end
|
325
|
+
break if !rules.validate_calls? || base.links.first.is_a?(Solargraph::Source::Chain::ZSuper)
|
326
|
+
|
327
|
+
params = first_param_hash(pins)
|
328
|
+
|
329
|
+
all_errors = []
|
330
|
+
pin.signatures.sort { |sig| sig.parameters.length }.each do |sig|
|
331
|
+
errors = []
|
332
|
+
sig.parameters.each_with_index do |par, idx|
|
333
|
+
# @todo add logic mapping up restarg parameters with
|
334
|
+
# arguments (including restarg arguments). Use tuples
|
335
|
+
# when possible, and when not, ensure provably
|
336
|
+
# incorrect situations are detected.
|
337
|
+
break if par.decl == :restarg # bail out pending better arg processing
|
338
|
+
argchain = arguments[idx]
|
339
|
+
if argchain.nil?
|
340
|
+
if par.decl == :arg
|
341
|
+
final_arg = arguments.last
|
342
|
+
if final_arg && final_arg.node.type == :splat
|
343
|
+
argchain = final_arg
|
344
|
+
next # don't try to apply the type of the splat - unlikely to be specific enough
|
345
|
+
else
|
346
|
+
errors.push Problem.new(location, "Not enough arguments to #{pin.path}")
|
347
|
+
next
|
348
|
+
end
|
349
|
+
else
|
350
|
+
final_arg = arguments.last
|
351
|
+
argchain = final_arg if final_arg && [:kwsplat, :hash].include?(final_arg.node.type)
|
352
|
+
end
|
353
|
+
end
|
354
|
+
if argchain
|
355
|
+
if par.decl != :arg
|
356
|
+
errors.concat kwarg_problems_for sig, argchain, api_map, block_pin, locals, location, pin, params, idx
|
357
|
+
next
|
358
|
+
else
|
359
|
+
if argchain.node.type == :splat && argchain == arguments.last
|
360
|
+
final_arg = argchain
|
361
|
+
end
|
362
|
+
if (final_arg && final_arg.node.type == :splat)
|
363
|
+
# The final argument given has been seen and was a
|
364
|
+
# splat, which doesn't give us useful types or
|
365
|
+
# arities against positional parameters, so let's
|
366
|
+
# continue on in case there are any required
|
367
|
+
# kwargs we should warn about
|
368
|
+
next
|
369
|
+
end
|
370
|
+
|
371
|
+
if argchain.node.type == :splat && par != sig.parameters.last
|
372
|
+
# we have been given a splat and there are more
|
373
|
+
# arguments to come.
|
374
|
+
|
375
|
+
# @todo Improve this so that we can skip past the
|
376
|
+
# rest of the positional parameters here but still
|
377
|
+
# process the kwargs
|
378
|
+
break
|
379
|
+
end
|
380
|
+
ptype = params.key?(par.name) ? params[par.name][:qualified] : ComplexType::UNDEFINED
|
381
|
+
ptype = ptype.self_to_type(par.context)
|
382
|
+
if ptype.nil?
|
383
|
+
# @todo Some level (strong, I guess) should require the param here
|
384
|
+
else
|
385
|
+
argtype = argchain.infer(api_map, block_pin, locals)
|
386
|
+
if argtype.defined? && ptype.defined? && !any_types_match?(api_map, ptype, argtype)
|
387
|
+
errors.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}")
|
388
|
+
next
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|
392
|
+
elsif par.decl == :kwarg
|
393
|
+
errors.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}")
|
394
|
+
next
|
395
|
+
end
|
396
|
+
end
|
397
|
+
if errors.empty?
|
398
|
+
all_errors.clear
|
399
|
+
break
|
400
|
+
end
|
401
|
+
all_errors.concat errors
|
402
|
+
end
|
403
|
+
result.concat all_errors
|
404
|
+
end
|
405
|
+
base = base.base
|
406
|
+
end
|
407
|
+
result
|
408
|
+
end
|
409
|
+
|
410
|
+
# @param sig [Pin::Signature]
|
411
|
+
# @param argchain [Source::Chain]
|
412
|
+
# @param api_map [ApiMap]
|
413
|
+
# @param block_pin [Pin::Block]
|
414
|
+
# @param locals [Array<Pin::LocalVariable>]
|
415
|
+
# @param location [Location]
|
416
|
+
# @param pin [Pin::Method]
|
417
|
+
# @param params [Hash{String => Hash{Symbol => String, Solargraph::ComplexType}}]
|
418
|
+
# @param idx [Integer]
|
419
|
+
#
|
420
|
+
# @return [Array<Problem>]
|
421
|
+
def kwarg_problems_for sig, argchain, api_map, block_pin, locals, location, pin, params, idx
|
422
|
+
result = []
|
423
|
+
kwargs = convert_hash(argchain.node)
|
424
|
+
par = sig.parameters[idx]
|
425
|
+
argchain = kwargs[par.name.to_sym]
|
426
|
+
if par.decl == :kwrestarg || (par.decl == :optarg && idx == pin.parameters.length - 1 && par.asgn_code == '{}')
|
427
|
+
result.concat kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kwargs)
|
428
|
+
else
|
429
|
+
if argchain
|
430
|
+
data = params[par.name]
|
431
|
+
if data.nil?
|
432
|
+
# @todo Some level (strong, I guess) should require the param here
|
433
|
+
else
|
434
|
+
ptype = data[:qualified]
|
435
|
+
unless ptype.undefined?
|
436
|
+
argtype = argchain.infer(api_map, block_pin, locals)
|
437
|
+
if argtype.defined? && ptype && !any_types_match?(api_map, ptype, argtype)
|
438
|
+
result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}")
|
439
|
+
end
|
440
|
+
end
|
441
|
+
end
|
442
|
+
elsif par.decl == :kwarg
|
443
|
+
result.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}")
|
444
|
+
end
|
445
|
+
end
|
446
|
+
result
|
447
|
+
end
|
448
|
+
|
449
|
+
# @param api_map [ApiMap]
|
450
|
+
# @param block_pin [Pin::Block]
|
451
|
+
# @param locals [Array<Pin::LocalVariable>]
|
452
|
+
# @param location [Location]
|
453
|
+
# @param pin [Pin::Method]
|
454
|
+
# @param params [Hash{String => [nil, Hash]}]
|
455
|
+
# @param kwargs [Hash{Symbol => Source::Chain}]
|
456
|
+
# @return [Array<Problem>]
|
457
|
+
def kwrestarg_problems_for(api_map, block_pin, locals, location, pin, params, kwargs)
|
458
|
+
result = []
|
459
|
+
kwargs.each_pair do |pname, argchain|
|
460
|
+
next unless params.key?(pname.to_s)
|
461
|
+
ptype = params[pname.to_s][:qualified]
|
462
|
+
argtype = argchain.infer(api_map, block_pin, locals)
|
463
|
+
if argtype.defined? && ptype && !any_types_match?(api_map, ptype, argtype)
|
464
|
+
result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{pname} expected #{ptype}, received #{argtype}")
|
465
|
+
end
|
466
|
+
end
|
467
|
+
result
|
468
|
+
end
|
469
|
+
|
470
|
+
# @param pin [Pin::Method]
|
471
|
+
# @return [Hash{String => Hash{Symbol => String, ComplexType}}]
|
472
|
+
def param_hash(pin)
|
473
|
+
tags = pin.docstring.tags(:param)
|
474
|
+
return {} if tags.empty?
|
475
|
+
# @type [Hash{String => Hash{Symbol => String, ComplexType}}]
|
476
|
+
result = {}
|
477
|
+
tags.each do |tag|
|
478
|
+
next if tag.types.nil? || tag.types.empty?
|
479
|
+
result[tag.name.to_s] = {
|
480
|
+
tagged: tag.types.join(', '),
|
481
|
+
qualified: Solargraph::ComplexType.try_parse(*tag.types).qualify(api_map, pin.full_context.namespace)
|
482
|
+
}
|
483
|
+
end
|
484
|
+
result
|
485
|
+
end
|
486
|
+
|
487
|
+
# @param pins [Array<Pin::Method>]
|
488
|
+
# @return [Hash{String => Hash{Symbol => String, ComplexType}}]
|
489
|
+
def first_param_hash(pins)
|
490
|
+
pins.each do |pin|
|
491
|
+
result = param_hash(pin)
|
492
|
+
return result unless result.empty?
|
493
|
+
end
|
494
|
+
{}
|
495
|
+
end
|
496
|
+
|
497
|
+
# @param pin [Pin::Base]
|
498
|
+
def internal? pin
|
499
|
+
return false if pin.nil?
|
500
|
+
pin.location && api_map.bundled?(pin.location.filename)
|
501
|
+
end
|
502
|
+
|
503
|
+
# True if the pin is either internal (part of the workspace) or from the core/stdlib
|
504
|
+
# @param pin [Pin::Base]
|
505
|
+
def internal_or_core? pin
|
506
|
+
# @todo RBS pins are not necessarily core/stdlib pins
|
507
|
+
internal?(pin) || pin.source == :rbs
|
508
|
+
end
|
509
|
+
|
510
|
+
# @param pin [Pin::Base]
|
511
|
+
def external? pin
|
512
|
+
!internal? pin
|
513
|
+
end
|
514
|
+
|
515
|
+
# @param pin [Pin::BaseVariable]
|
516
|
+
def declared_externally? pin
|
517
|
+
return true if pin.assignment.nil?
|
518
|
+
chain = Solargraph::Parser.chain(pin.assignment, filename)
|
519
|
+
rng = Solargraph::Range.from_node(pin.assignment)
|
520
|
+
block_pin = source_map.locate_block_pin(rng.start.line, rng.start.column)
|
521
|
+
location = Location.new(filename, Range.from_node(pin.assignment))
|
522
|
+
locals = source_map.locals_at(location)
|
523
|
+
type = chain.infer(api_map, block_pin, locals)
|
524
|
+
if type.undefined? && !rules.ignore_all_undefined?
|
525
|
+
base = chain
|
526
|
+
missing = chain
|
527
|
+
found = nil
|
528
|
+
closest = ComplexType::UNDEFINED
|
529
|
+
until base.links.first.undefined?
|
530
|
+
found = base.define(api_map, block_pin, locals).first
|
531
|
+
break if found
|
532
|
+
missing = base
|
533
|
+
base = base.base
|
534
|
+
end
|
535
|
+
closest = found.typify(api_map) if found
|
536
|
+
if !found || closest.defined? || internal?(found)
|
537
|
+
return false
|
538
|
+
end
|
539
|
+
end
|
540
|
+
true
|
541
|
+
end
|
542
|
+
|
543
|
+
# @param pin [Pin::Method]
|
544
|
+
# @param arguments [Array<Source::Chain>]
|
545
|
+
# @param location [Location]
|
546
|
+
# @return [Array<Problem>]
|
547
|
+
def arity_problems_for pin, arguments, location
|
548
|
+
results = pin.signatures.map do |sig|
|
549
|
+
r = parameterized_arity_problems_for(pin, sig.parameters, arguments, location)
|
550
|
+
return [] if r.empty?
|
551
|
+
r
|
552
|
+
end
|
553
|
+
results.first
|
554
|
+
end
|
555
|
+
|
556
|
+
# @param pin [Pin::Method]
|
557
|
+
# @param parameters [Array<Pin::Parameter>]
|
558
|
+
# @param arguments [Array<Source::Chain>]
|
559
|
+
# @param location [Location]
|
560
|
+
# @return [Array<Problem>]
|
561
|
+
def parameterized_arity_problems_for(pin, parameters, arguments, location)
|
562
|
+
return [] unless pin.explicit?
|
563
|
+
return [] if parameters.empty? && arguments.empty?
|
564
|
+
return [] if pin.anon_splat?
|
565
|
+
unchecked = arguments.dup # creates copy of and unthaws array
|
566
|
+
add_params = 0
|
567
|
+
if unchecked.empty? && parameters.any? { |param| param.decl == :kwarg }
|
568
|
+
return [Problem.new(location, "Missing keyword arguments to #{pin.path}")]
|
569
|
+
end
|
570
|
+
settled_kwargs = 0
|
571
|
+
unless unchecked.empty?
|
572
|
+
if any_splatted_call?(unchecked.map(&:node))
|
573
|
+
settled_kwargs = parameters.count(&:keyword?)
|
574
|
+
else
|
575
|
+
kwargs = convert_hash(unchecked.last.node)
|
576
|
+
if parameters.any? { |param| [:kwarg, :kwoptarg].include?(param.decl) || param.kwrestarg? }
|
577
|
+
if kwargs.empty?
|
578
|
+
add_params += 1
|
579
|
+
else
|
580
|
+
unchecked.pop
|
581
|
+
parameters.each do |param|
|
582
|
+
next unless param.keyword?
|
583
|
+
if kwargs.key?(param.name.to_sym)
|
584
|
+
kwargs.delete param.name.to_sym
|
585
|
+
settled_kwargs += 1
|
586
|
+
elsif param.decl == :kwarg
|
587
|
+
return [] if arguments.last.links.last.is_a?(Solargraph::Source::Chain::Hash) && arguments.last.links.last.splatted?
|
588
|
+
return [Problem.new(location, "Missing keyword argument #{param.name} to #{pin.path}")]
|
589
|
+
end
|
590
|
+
end
|
591
|
+
kwargs.clear if parameters.any?(&:kwrestarg?)
|
592
|
+
unless kwargs.empty?
|
593
|
+
return [Problem.new(location, "Unrecognized keyword argument #{kwargs.keys.first} to #{pin.path}")]
|
594
|
+
end
|
595
|
+
end
|
596
|
+
end
|
597
|
+
end
|
598
|
+
end
|
599
|
+
req = required_param_count(parameters)
|
600
|
+
if req + add_params < unchecked.length
|
601
|
+
return [] if parameters.any?(&:rest?)
|
602
|
+
opt = optional_param_count(parameters)
|
603
|
+
return [] if unchecked.length <= req + opt
|
604
|
+
if req + add_params + 1 == unchecked.length && any_splatted_call?(unchecked.map(&:node)) && (parameters.map(&:decl) & [:kwarg, :kwoptarg, :kwrestarg]).any?
|
605
|
+
return []
|
606
|
+
end
|
607
|
+
return [] if arguments.length - req == parameters.select { |p| [:optarg, :kwoptarg].include?(p.decl) }.length
|
608
|
+
return [Problem.new(location, "Too many arguments to #{pin.path}")]
|
609
|
+
elsif unchecked.length < req - settled_kwargs && (arguments.empty? || (!arguments.last.splat? && !arguments.last.links.last.is_a?(Solargraph::Source::Chain::Hash)))
|
610
|
+
# HACK: Kernel#raise signature is incorrect in Ruby 2.7 core docs.
|
611
|
+
# See https://github.com/castwide/solargraph/issues/418
|
612
|
+
unless arguments.empty? && pin.path == 'Kernel#raise'
|
613
|
+
return [Problem.new(location, "Not enough arguments to #{pin.path}")]
|
614
|
+
end
|
615
|
+
end
|
616
|
+
[]
|
617
|
+
end
|
618
|
+
|
619
|
+
# @param parameters [Enumerable<Pin::Parameter>]
|
620
|
+
# @todo need to use generic types in method to choose correct
|
621
|
+
# signature and generate Integer as return type
|
622
|
+
# @sg-ignore
|
623
|
+
# @return [Integer]
|
624
|
+
def required_param_count(parameters)
|
625
|
+
parameters.sum { |param| %i[arg kwarg].include?(param.decl) ? 1 : 0 }
|
626
|
+
end
|
627
|
+
|
628
|
+
# @param parameters [Enumerable<Pin::Parameter>]
|
629
|
+
# @param pin [Pin::Method]
|
630
|
+
# @return [Integer]
|
631
|
+
def optional_param_count(parameters)
|
632
|
+
parameters.select { |p| p.decl == :optarg }.length
|
633
|
+
end
|
634
|
+
|
635
|
+
# @param pin [Pin::Method]
|
636
|
+
def abstract? pin
|
637
|
+
pin.docstring.has_tag?('abstract') ||
|
638
|
+
(pin.closure && pin.closure.docstring.has_tag?('abstract'))
|
639
|
+
end
|
640
|
+
|
641
|
+
# @param pin [Pin::Method]
|
642
|
+
# @return [Array<Source::Chain>]
|
643
|
+
def fake_args_for(pin)
|
644
|
+
args = []
|
645
|
+
with_opts = false
|
646
|
+
with_block = false
|
647
|
+
pin.parameters.each do |pin|
|
648
|
+
if [:kwarg, :kwoptarg, :kwrestarg].include?(pin.decl)
|
649
|
+
with_opts = true
|
650
|
+
elsif pin.decl == :block
|
651
|
+
with_block = true
|
652
|
+
elsif pin.decl == :restarg
|
653
|
+
args.push Solargraph::Source::Chain.new([Solargraph::Source::Chain::Variable.new(pin.name)], nil, true)
|
654
|
+
else
|
655
|
+
args.push Solargraph::Source::Chain.new([Solargraph::Source::Chain::Variable.new(pin.name)])
|
656
|
+
end
|
657
|
+
end
|
658
|
+
args.push Solargraph::Parser.chain_string('{}') if with_opts
|
659
|
+
args.push Solargraph::Parser.chain_string('&') if with_block
|
660
|
+
args
|
661
|
+
end
|
662
|
+
|
663
|
+
# @param problems [Array<Problem>]
|
664
|
+
# @return [Array<Problem>]
|
665
|
+
def without_ignored problems
|
666
|
+
problems.reject do |problem|
|
667
|
+
node = source_map.source.node_at(problem.location.range.start.line, problem.location.range.start.column)
|
668
|
+
node && source_map.source.comments_for(node)&.include?('@sg-ignore')
|
669
|
+
end
|
670
|
+
end
|
671
|
+
end
|
672
|
+
end
|