solargraph 0.58.2 → 0.59.0.dev.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.envrc +3 -0
- data/.github/workflows/linting.yml +4 -5
- data/.github/workflows/plugins.yml +41 -34
- data/.github/workflows/rspec.yml +44 -23
- data/.github/workflows/typecheck.yml +2 -2
- data/.rubocop.yml +32 -5
- data/.rubocop_todo.yml +50 -966
- data/Gemfile +3 -1
- data/README.md +3 -3
- data/Rakefile +26 -23
- data/bin/solargraph +2 -1
- data/lib/solargraph/api_map/cache.rb +3 -3
- data/lib/solargraph/api_map/constants.rb +13 -3
- data/lib/solargraph/api_map/index.rb +23 -18
- data/lib/solargraph/api_map/source_to_yard.rb +22 -9
- data/lib/solargraph/api_map/store.rb +33 -28
- data/lib/solargraph/api_map.rb +150 -82
- data/lib/solargraph/bench.rb +44 -45
- data/lib/solargraph/complex_type/conformance.rb +176 -0
- data/lib/solargraph/complex_type/type_methods.rb +28 -17
- data/lib/solargraph/complex_type/unique_type.rb +218 -57
- data/lib/solargraph/complex_type.rb +170 -57
- data/lib/solargraph/convention/data_definition/data_assignment_node.rb +61 -61
- data/lib/solargraph/convention/data_definition/data_definition_node.rb +7 -5
- data/lib/solargraph/convention/data_definition.rb +5 -2
- data/lib/solargraph/convention/gemfile.rb +15 -15
- data/lib/solargraph/convention/gemspec.rb +23 -23
- data/lib/solargraph/convention/rakefile.rb +17 -17
- data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +2 -1
- data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +4 -3
- data/lib/solargraph/convention/struct_definition.rb +8 -4
- data/lib/solargraph/convention.rb +78 -78
- data/lib/solargraph/converters/dd.rb +19 -17
- data/lib/solargraph/converters/dl.rb +17 -15
- data/lib/solargraph/converters/dt.rb +17 -15
- data/lib/solargraph/converters/misc.rb +3 -1
- data/lib/solargraph/diagnostics/require_not_found.rb +1 -0
- data/lib/solargraph/diagnostics/rubocop.rb +11 -10
- data/lib/solargraph/diagnostics/rubocop_helpers.rb +5 -3
- data/lib/solargraph/diagnostics/type_check.rb +11 -10
- data/lib/solargraph/diagnostics/update_errors.rb +37 -41
- data/lib/solargraph/doc_map.rb +133 -373
- data/lib/solargraph/equality.rb +4 -4
- data/lib/solargraph/gem_pins.rb +21 -20
- data/lib/solargraph/language_server/error_codes.rb +20 -20
- data/lib/solargraph/language_server/host/diagnoser.rb +1 -1
- data/lib/solargraph/language_server/host/dispatch.rb +3 -3
- data/lib/solargraph/language_server/host/message_worker.rb +4 -3
- data/lib/solargraph/language_server/host/sources.rb +2 -1
- data/lib/solargraph/language_server/host.rb +30 -22
- data/lib/solargraph/language_server/message/base.rb +97 -97
- data/lib/solargraph/language_server/message/client/register_capability.rb +13 -15
- data/lib/solargraph/language_server/message/completion_item/resolve.rb +58 -60
- data/lib/solargraph/language_server/message/extended/check_gem_version.rb +12 -18
- data/lib/solargraph/language_server/message/extended/document.rb +1 -0
- data/lib/solargraph/language_server/message/extended/document_gems.rb +32 -32
- data/lib/solargraph/language_server/message/extended/download_core.rb +20 -19
- data/lib/solargraph/language_server/message/extended/search.rb +20 -20
- data/lib/solargraph/language_server/message/initialize.rb +197 -191
- data/lib/solargraph/language_server/message/text_document/completion.rb +10 -8
- data/lib/solargraph/language_server/message/text_document/definition.rb +41 -32
- data/lib/solargraph/language_server/message/text_document/document_highlight.rb +23 -16
- data/lib/solargraph/language_server/message/text_document/document_symbol.rb +29 -19
- data/lib/solargraph/language_server/message/text_document/formatting.rb +8 -6
- data/lib/solargraph/language_server/message/text_document/hover.rb +5 -5
- data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +18 -11
- data/lib/solargraph/language_server/message/text_document/references.rb +23 -16
- data/lib/solargraph/language_server/message/text_document/rename.rb +26 -19
- data/lib/solargraph/language_server/message/text_document/signature_help.rb +3 -2
- data/lib/solargraph/language_server/message/text_document/type_definition.rb +25 -17
- data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +41 -35
- data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +48 -40
- data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +32 -26
- data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +27 -17
- data/lib/solargraph/language_server/message.rb +94 -94
- data/lib/solargraph/language_server/request.rb +29 -27
- data/lib/solargraph/language_server/transport/data_reader.rb +72 -74
- data/lib/solargraph/language_server/uri_helpers.rb +49 -49
- data/lib/solargraph/library.rb +85 -44
- data/lib/solargraph/location.rb +17 -14
- data/lib/solargraph/logging.rb +24 -4
- data/lib/solargraph/page.rb +92 -92
- data/lib/solargraph/parser/comment_ripper.rb +19 -4
- data/lib/solargraph/parser/flow_sensitive_typing.rb +326 -108
- data/lib/solargraph/parser/node_processor/base.rb +34 -4
- data/lib/solargraph/parser/node_processor.rb +8 -7
- data/lib/solargraph/parser/parser_gem/class_methods.rb +32 -14
- data/lib/solargraph/parser/parser_gem/flawed_builder.rb +19 -19
- data/lib/solargraph/parser/parser_gem/node_chainer.rb +50 -25
- data/lib/solargraph/parser/parser_gem/node_methods.rb +91 -70
- data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +4 -4
- data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +13 -11
- data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +9 -0
- data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +12 -12
- data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +10 -3
- data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +38 -37
- data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +36 -6
- data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +5 -3
- data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +1 -0
- data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +3 -1
- data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +3 -3
- data/lib/solargraph/parser/parser_gem/node_processors/or_node.rb +22 -0
- data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -1
- data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +4 -5
- data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +124 -113
- data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -29
- data/lib/solargraph/parser/parser_gem/node_processors/when_node.rb +23 -0
- data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +6 -2
- data/lib/solargraph/parser/parser_gem/node_processors.rb +4 -0
- data/lib/solargraph/parser/parser_gem.rb +14 -12
- data/lib/solargraph/parser/region.rb +9 -3
- data/lib/solargraph/parser/snippet.rb +3 -1
- data/lib/solargraph/parser.rb +25 -23
- data/lib/solargraph/pin/base.rb +126 -80
- data/lib/solargraph/pin/base_variable.rb +273 -24
- data/lib/solargraph/pin/block.rb +29 -6
- data/lib/solargraph/pin/breakable.rb +7 -1
- data/lib/solargraph/pin/callable.rb +65 -21
- data/lib/solargraph/pin/closure.rb +7 -10
- data/lib/solargraph/pin/common.rb +24 -6
- data/lib/solargraph/pin/compound_statement.rb +55 -0
- data/lib/solargraph/pin/constant.rb +43 -45
- data/lib/solargraph/pin/conversions.rb +10 -4
- data/lib/solargraph/pin/delegated_method.rb +19 -8
- data/lib/solargraph/pin/documenting.rb +4 -2
- data/lib/solargraph/pin/instance_variable.rb +5 -1
- data/lib/solargraph/pin/keyword.rb +0 -4
- data/lib/solargraph/pin/local_variable.rb +15 -59
- data/lib/solargraph/pin/method.rb +153 -104
- data/lib/solargraph/pin/method_alias.rb +8 -0
- data/lib/solargraph/pin/namespace.rb +19 -12
- data/lib/solargraph/pin/parameter.rb +100 -36
- data/lib/solargraph/pin/proxy_type.rb +4 -1
- data/lib/solargraph/pin/reference/override.rb +1 -1
- data/lib/solargraph/pin/reference/superclass.rb +2 -0
- data/lib/solargraph/pin/reference.rb +19 -0
- data/lib/solargraph/pin/search.rb +3 -2
- data/lib/solargraph/pin/signature.rb +15 -12
- data/lib/solargraph/pin/symbol.rb +2 -1
- data/lib/solargraph/pin/until.rb +2 -4
- data/lib/solargraph/pin/while.rb +2 -4
- data/lib/solargraph/pin.rb +2 -0
- data/lib/solargraph/pin_cache.rb +490 -73
- data/lib/solargraph/position.rb +14 -10
- data/lib/solargraph/range.rb +16 -15
- data/lib/solargraph/rbs_map/conversions.rb +343 -214
- data/lib/solargraph/rbs_map/core_fills.rb +91 -84
- data/lib/solargraph/rbs_map/core_map.rb +24 -17
- data/lib/solargraph/rbs_map/stdlib_map.rb +33 -5
- data/lib/solargraph/rbs_map.rb +77 -32
- data/lib/solargraph/server_methods.rb +16 -16
- data/lib/solargraph/shell.rb +128 -73
- data/lib/solargraph/source/chain/array.rb +39 -37
- data/lib/solargraph/source/chain/call.rb +96 -56
- data/lib/solargraph/source/chain/class_variable.rb +13 -13
- data/lib/solargraph/source/chain/constant.rb +5 -1
- data/lib/solargraph/source/chain/global_variable.rb +13 -13
- data/lib/solargraph/source/chain/hash.rb +8 -5
- data/lib/solargraph/source/chain/if.rb +12 -10
- data/lib/solargraph/source/chain/instance_variable.rb +24 -1
- data/lib/solargraph/source/chain/link.rb +99 -109
- data/lib/solargraph/source/chain/literal.rb +9 -6
- data/lib/solargraph/source/chain/or.rb +10 -4
- data/lib/solargraph/source/chain/q_call.rb +13 -11
- data/lib/solargraph/source/chain/variable.rb +15 -13
- data/lib/solargraph/source/chain/z_super.rb +28 -30
- data/lib/solargraph/source/chain.rb +49 -38
- data/lib/solargraph/source/change.rb +12 -5
- data/lib/solargraph/source/cursor.rb +23 -17
- data/lib/solargraph/source/encoding_fixes.rb +6 -7
- data/lib/solargraph/source/source_chainer.rb +56 -32
- data/lib/solargraph/source/updater.rb +5 -1
- data/lib/solargraph/source.rb +59 -35
- data/lib/solargraph/source_map/clip.rb +48 -29
- data/lib/solargraph/source_map/data.rb +4 -1
- data/lib/solargraph/source_map/mapper.rb +71 -42
- data/lib/solargraph/source_map.rb +21 -9
- data/lib/solargraph/type_checker/problem.rb +3 -1
- data/lib/solargraph/type_checker/rules.rb +81 -8
- data/lib/solargraph/type_checker.rb +195 -120
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/workspace/config.rb +13 -10
- data/lib/solargraph/workspace/gemspecs.rb +367 -0
- data/lib/solargraph/workspace/require_paths.rb +1 -0
- data/lib/solargraph/workspace.rb +149 -30
- data/lib/solargraph/yard_map/helpers.rb +8 -3
- data/lib/solargraph/yard_map/mapper/to_method.rb +13 -7
- data/lib/solargraph/yard_map/mapper/to_namespace.rb +2 -1
- data/lib/solargraph/yard_map/mapper.rb +13 -8
- data/lib/solargraph/yard_tags.rb +20 -20
- data/lib/solargraph/yardoc.rb +33 -23
- data/lib/solargraph.rb +29 -8
- data/rbs/fills/rubygems/0/dependency.rbs +193 -0
- data/rbs/fills/tuple/tuple.rbs +28 -0
- data/rbs/shims/ast/0/node.rbs +1 -1
- data/rbs/shims/diff-lcs/1.5/diff-lcs.rbs +11 -0
- data/solargraph.gemspec +36 -34
- metadata +38 -33
- data/lib/solargraph/type_checker/checks.rb +0 -124
- data/lib/solargraph/type_checker/param_def.rb +0 -37
- data/lib/solargraph/yard_map/to_method.rb +0 -89
|
@@ -4,11 +4,12 @@ module Solargraph
|
|
|
4
4
|
# A container for type data based on YARD type tags.
|
|
5
5
|
#
|
|
6
6
|
class ComplexType
|
|
7
|
-
GENERIC_TAG_NAME = 'generic'
|
|
7
|
+
GENERIC_TAG_NAME = 'generic'
|
|
8
8
|
# @!parse
|
|
9
9
|
# include TypeMethods
|
|
10
10
|
include Equality
|
|
11
11
|
|
|
12
|
+
autoload :Conformance, 'solargraph/complex_type/conformance'
|
|
12
13
|
autoload :TypeMethods, 'solargraph/complex_type/type_methods'
|
|
13
14
|
autoload :UniqueType, 'solargraph/complex_type/unique_type'
|
|
14
15
|
|
|
@@ -18,25 +19,24 @@ module Solargraph
|
|
|
18
19
|
# @type [Array<UniqueType>]
|
|
19
20
|
items = types.flat_map(&:items).uniq(&:to_s)
|
|
20
21
|
if items.any? { |i| i.name == 'false' } && items.any? { |i| i.name == 'true' }
|
|
21
|
-
items.delete_if { |i|
|
|
22
|
-
items.unshift(
|
|
22
|
+
items.delete_if { |i| %w[false true].include?(i.name) }
|
|
23
|
+
items.unshift(UniqueType::BOOLEAN)
|
|
23
24
|
end
|
|
25
|
+
# @type [Array<UniqueType>]
|
|
24
26
|
items = [UniqueType::UNDEFINED] if items.any?(&:undefined?)
|
|
27
|
+
# @todo shouldn't need this cast - if statement above adds an 'Array' type
|
|
28
|
+
# @type [Array<UniqueType>]
|
|
25
29
|
@items = items
|
|
26
30
|
end
|
|
27
31
|
|
|
28
|
-
# @sg-ignore Fix "Not enough arguments to Module#protected"
|
|
29
|
-
protected def equality_fields
|
|
30
|
-
[self.class, items]
|
|
31
|
-
end
|
|
32
|
-
|
|
33
32
|
# @param api_map [ApiMap]
|
|
34
|
-
# @param
|
|
33
|
+
# @param gates [Array<String>]
|
|
34
|
+
#
|
|
35
35
|
# @return [ComplexType]
|
|
36
36
|
def qualify api_map, *gates
|
|
37
37
|
red = reduce_object
|
|
38
38
|
types = red.items.map do |t|
|
|
39
|
-
next t if [
|
|
39
|
+
next t if %w[nil void undefined].include?(t.name)
|
|
40
40
|
next t if ['::Boolean'].include?(t.rooted_name)
|
|
41
41
|
t.qualify api_map, *gates
|
|
42
42
|
end
|
|
@@ -44,13 +44,16 @@ module Solargraph
|
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
# @param generics_to_resolve [Enumerable<String>]]
|
|
47
|
-
# @param context_type [UniqueType, nil]
|
|
47
|
+
# @param context_type [ComplexType, ComplexType::UniqueType, nil]
|
|
48
48
|
# @param resolved_generic_values [Hash{String => ComplexType}] Added to as types are encountered or resolved
|
|
49
49
|
# @return [self]
|
|
50
50
|
def resolve_generics_from_context generics_to_resolve, context_type, resolved_generic_values: {}
|
|
51
51
|
return self unless generic?
|
|
52
52
|
|
|
53
|
-
ComplexType.new(@items.map
|
|
53
|
+
ComplexType.new(@items.map do |i|
|
|
54
|
+
i.resolve_generics_from_context(generics_to_resolve, context_type,
|
|
55
|
+
resolved_generic_values: resolved_generic_values)
|
|
56
|
+
end)
|
|
54
57
|
end
|
|
55
58
|
|
|
56
59
|
# @return [UniqueType]
|
|
@@ -65,7 +68,7 @@ module Solargraph
|
|
|
65
68
|
(@items.length > 1 ? ')' : ''))
|
|
66
69
|
end
|
|
67
70
|
|
|
68
|
-
# @param dst [ComplexType]
|
|
71
|
+
# @param dst [ComplexType, ComplexType::UniqueType]
|
|
69
72
|
# @return [ComplexType]
|
|
70
73
|
def self_to_type dst
|
|
71
74
|
object_type_dst = dst.reduce_class_type
|
|
@@ -76,15 +79,19 @@ module Solargraph
|
|
|
76
79
|
end
|
|
77
80
|
|
|
78
81
|
# @yieldparam [UniqueType]
|
|
82
|
+
# @yieldreturn [UniqueType]
|
|
79
83
|
# @return [Array<UniqueType>]
|
|
84
|
+
# @sg-ignore Declared return type
|
|
85
|
+
# ::Array<::Solargraph::ComplexType::UniqueType> does not match
|
|
86
|
+
# inferred type ::Array<::Proc> for Solargraph::ComplexType#map
|
|
80
87
|
def map &block
|
|
81
|
-
@items.map
|
|
88
|
+
@items.map(&block)
|
|
82
89
|
end
|
|
83
90
|
|
|
84
91
|
# @yieldparam [UniqueType]
|
|
85
92
|
# @return [Enumerable<UniqueType>]
|
|
86
93
|
def each &block
|
|
87
|
-
@items.each
|
|
94
|
+
@items.each(&block)
|
|
88
95
|
end
|
|
89
96
|
|
|
90
97
|
# @yieldparam [UniqueType]
|
|
@@ -95,23 +102,17 @@ module Solargraph
|
|
|
95
102
|
return enum_for(__method__) unless block_given?
|
|
96
103
|
|
|
97
104
|
@items.each do |item|
|
|
98
|
-
item.each_unique_type
|
|
105
|
+
item.each_unique_type(&block)
|
|
99
106
|
end
|
|
100
107
|
end
|
|
101
108
|
|
|
102
|
-
# @param atype [ComplexType] type which may be assigned to this type
|
|
103
|
-
# @param api_map [ApiMap] The ApiMap that performs qualification
|
|
104
|
-
def can_assign?(api_map, atype)
|
|
105
|
-
any? { |ut| ut.can_assign?(api_map, atype) }
|
|
106
|
-
end
|
|
107
|
-
|
|
108
109
|
# @param new_name [String, nil]
|
|
109
110
|
# @param make_rooted [Boolean, nil]
|
|
110
111
|
# @param new_key_types [Array<ComplexType>, nil]
|
|
111
|
-
# @param
|
|
112
|
+
# @param make_rooted [Boolean, nil]
|
|
112
113
|
# @param new_subtypes [Array<ComplexType>, nil]
|
|
113
114
|
# @return [self]
|
|
114
|
-
def recreate
|
|
115
|
+
def recreate new_name: nil, make_rooted: nil, new_key_types: nil, new_subtypes: nil
|
|
115
116
|
ComplexType.new(map do |ut|
|
|
116
117
|
ut.recreate(new_name: new_name,
|
|
117
118
|
make_rooted: make_rooted,
|
|
@@ -132,13 +133,13 @@ module Solargraph
|
|
|
132
133
|
|
|
133
134
|
# @param index [Integer]
|
|
134
135
|
# @return [UniqueType]
|
|
135
|
-
def []
|
|
136
|
+
def [] index
|
|
136
137
|
@items[index]
|
|
137
138
|
end
|
|
138
139
|
|
|
139
140
|
# @return [Array<UniqueType>]
|
|
140
141
|
def select &block
|
|
141
|
-
@items.select
|
|
142
|
+
@items.select(&block)
|
|
142
143
|
end
|
|
143
144
|
|
|
144
145
|
# @return [String]
|
|
@@ -153,7 +154,9 @@ module Solargraph
|
|
|
153
154
|
end
|
|
154
155
|
|
|
155
156
|
# @param name [Symbol]
|
|
157
|
+
#
|
|
156
158
|
# @return [Object, nil]
|
|
159
|
+
# @param [Array<Object>] args
|
|
157
160
|
def method_missing name, *args, &block
|
|
158
161
|
return if @items.first.nil?
|
|
159
162
|
return @items.first.send(name, *args, &block) if respond_to_missing?(name)
|
|
@@ -162,7 +165,7 @@ module Solargraph
|
|
|
162
165
|
|
|
163
166
|
# @param name [Symbol]
|
|
164
167
|
# @param include_private [Boolean]
|
|
165
|
-
def respond_to_missing?
|
|
168
|
+
def respond_to_missing? name, include_private = false
|
|
166
169
|
TypeMethods.public_instance_methods.include?(name) || super
|
|
167
170
|
end
|
|
168
171
|
|
|
@@ -194,6 +197,65 @@ module Solargraph
|
|
|
194
197
|
rooted_tags
|
|
195
198
|
end
|
|
196
199
|
|
|
200
|
+
# @param api_map [ApiMap]
|
|
201
|
+
# @param expected [ComplexType, ComplexType::UniqueType]
|
|
202
|
+
# @param situation [:method_call, :return_type, :assignment]
|
|
203
|
+
# @param rules [Array<:allow_subtype_skew, :allow_empty_params, :allow_reverse_match, :allow_any_match, :allow_undefined, :allow_unresolved_generic, :allow_unmatched_interface>]
|
|
204
|
+
#
|
|
205
|
+
# allow_subtype_skew: if not provided, check if any subtypes of
|
|
206
|
+
# the expected type match the inferred type
|
|
207
|
+
#
|
|
208
|
+
# allow_reverse_match: check if any subtypes
|
|
209
|
+
# of the expected type match the inferred type
|
|
210
|
+
#
|
|
211
|
+
# allow_empty_params: allow a general inferred type without
|
|
212
|
+
# parameters to conform to a more specific expected type
|
|
213
|
+
#
|
|
214
|
+
# allow_any_match: any unique type matched in the inferred
|
|
215
|
+
# qualifies as a match
|
|
216
|
+
#
|
|
217
|
+
# allow_undefined: treat undefined as a wildcard that matches
|
|
218
|
+
# anything
|
|
219
|
+
#
|
|
220
|
+
# @param variance [:invariant, :covariant, :contravariant]
|
|
221
|
+
# @return [Boolean]
|
|
222
|
+
def conforms_to? api_map, expected,
|
|
223
|
+
situation,
|
|
224
|
+
rules = [],
|
|
225
|
+
variance: erased_variance(situation)
|
|
226
|
+
expected = expected.downcast_to_literal_if_possible
|
|
227
|
+
inferred = downcast_to_literal_if_possible
|
|
228
|
+
|
|
229
|
+
return duck_types_match?(api_map, expected, inferred) if expected.duck_type?
|
|
230
|
+
|
|
231
|
+
if rules.include? :allow_any_match
|
|
232
|
+
inferred.any? do |inf|
|
|
233
|
+
inf.conforms_to?(api_map, expected, situation, rules,
|
|
234
|
+
variance: variance)
|
|
235
|
+
end
|
|
236
|
+
else
|
|
237
|
+
inferred.all? do |inf|
|
|
238
|
+
inf.conforms_to?(api_map, expected, situation, rules,
|
|
239
|
+
variance: variance)
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# @param api_map [ApiMap]
|
|
245
|
+
# @param expected [ComplexType, UniqueType]
|
|
246
|
+
# @param inferred [ComplexType, UniqueType]
|
|
247
|
+
# @return [Boolean]
|
|
248
|
+
def duck_types_match? api_map, expected, inferred
|
|
249
|
+
raise ArgumentError, 'Expected type must be duck type' unless expected.duck_type?
|
|
250
|
+
expected.each do |exp|
|
|
251
|
+
next unless exp.duck_type?
|
|
252
|
+
quack = exp.to_s[1..]
|
|
253
|
+
# @sg-ignore Need to add nil check here
|
|
254
|
+
return false if api_map.get_method_stack(inferred.namespace, quack, scope: inferred.scope).empty?
|
|
255
|
+
end
|
|
256
|
+
true
|
|
257
|
+
end
|
|
258
|
+
|
|
197
259
|
# @return [String]
|
|
198
260
|
def rooted_tags
|
|
199
261
|
map(&:rooted_tag).join(', ')
|
|
@@ -201,14 +263,14 @@ module Solargraph
|
|
|
201
263
|
|
|
202
264
|
# @yieldparam [UniqueType]
|
|
203
265
|
def all? &block
|
|
204
|
-
@items.all?
|
|
266
|
+
@items.all?(&block)
|
|
205
267
|
end
|
|
206
268
|
|
|
207
269
|
# @yieldparam [UniqueType]
|
|
208
270
|
# @yieldreturn [Boolean]
|
|
209
271
|
# @return [Boolean]
|
|
210
272
|
def any? &block
|
|
211
|
-
@items.compact.any?
|
|
273
|
+
@items.compact.any?(&block)
|
|
212
274
|
end
|
|
213
275
|
|
|
214
276
|
def selfy?
|
|
@@ -228,8 +290,10 @@ module Solargraph
|
|
|
228
290
|
# @yieldparam t [UniqueType]
|
|
229
291
|
# @yieldreturn [UniqueType]
|
|
230
292
|
# @return [ComplexType]
|
|
231
|
-
def transform
|
|
232
|
-
|
|
293
|
+
def transform new_name = nil, &transform_type
|
|
294
|
+
if new_name&.start_with?('::')
|
|
295
|
+
raise "Please remove leading :: and set rooted with recreate() instead - #{new_name}"
|
|
296
|
+
end
|
|
233
297
|
ComplexType.new(map { |ut| ut.transform(new_name, &transform_type) })
|
|
234
298
|
end
|
|
235
299
|
|
|
@@ -252,6 +316,13 @@ module Solargraph
|
|
|
252
316
|
@items.any?(&:nil_type?)
|
|
253
317
|
end
|
|
254
318
|
|
|
319
|
+
# @return [ComplexType]
|
|
320
|
+
def without_nil
|
|
321
|
+
new_items = @items.reject(&:nil_type?)
|
|
322
|
+
return ComplexType::UNDEFINED if new_items.empty?
|
|
323
|
+
ComplexType.new(new_items)
|
|
324
|
+
end
|
|
325
|
+
|
|
255
326
|
# @return [Array<ComplexType>]
|
|
256
327
|
def all_params
|
|
257
328
|
@items.first.all_params || []
|
|
@@ -260,7 +331,7 @@ module Solargraph
|
|
|
260
331
|
# @return [ComplexType]
|
|
261
332
|
def reduce_class_type
|
|
262
333
|
new_items = items.flat_map do |type|
|
|
263
|
-
next type unless [
|
|
334
|
+
next type unless %w[Module Class].include?(type.name)
|
|
264
335
|
next type if type.all_params.empty?
|
|
265
336
|
|
|
266
337
|
type.all_params
|
|
@@ -274,6 +345,13 @@ module Solargraph
|
|
|
274
345
|
all?(&:all_rooted?)
|
|
275
346
|
end
|
|
276
347
|
|
|
348
|
+
# @param other [ComplexType, UniqueType]
|
|
349
|
+
def erased_version_of? other
|
|
350
|
+
return false if items.length != 1 || other.items.length != 1
|
|
351
|
+
|
|
352
|
+
@items.first.erased_version_of?(other.items.first)
|
|
353
|
+
end
|
|
354
|
+
|
|
277
355
|
# every top-level type has resolved to be fully qualified; see
|
|
278
356
|
# #all_rooted? to check their subtypes as well
|
|
279
357
|
def rooted?
|
|
@@ -282,12 +360,46 @@ module Solargraph
|
|
|
282
360
|
|
|
283
361
|
attr_reader :items
|
|
284
362
|
|
|
285
|
-
|
|
286
|
-
|
|
363
|
+
# @param exclude_types [ComplexType, nil]
|
|
364
|
+
# @param api_map [ApiMap]
|
|
365
|
+
# @return [ComplexType, self]
|
|
366
|
+
def exclude exclude_types, api_map
|
|
367
|
+
return self if exclude_types.nil?
|
|
368
|
+
|
|
369
|
+
types = items - exclude_types.items
|
|
370
|
+
types = [ComplexType::UniqueType::UNDEFINED] if types.empty?
|
|
371
|
+
ComplexType.new(types)
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
# @see https://en.wikipedia.org/wiki/Intersection_type
|
|
375
|
+
#
|
|
376
|
+
# @param intersection_type [ComplexType, ComplexType::UniqueType, nil]
|
|
377
|
+
# @param api_map [ApiMap]
|
|
378
|
+
# @return [self, ComplexType::UniqueType]
|
|
379
|
+
def intersect_with intersection_type, api_map
|
|
380
|
+
return self if intersection_type.nil?
|
|
381
|
+
return intersection_type if undefined?
|
|
382
|
+
types = []
|
|
383
|
+
# try to find common types via conformance
|
|
384
|
+
items.each do |ut|
|
|
385
|
+
intersection_type.each do |int_type|
|
|
386
|
+
if int_type.conforms_to?(api_map, ut, :assignment)
|
|
387
|
+
types << int_type
|
|
388
|
+
elsif ut.conforms_to?(api_map, int_type, :assignment)
|
|
389
|
+
types << ut
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
types = [ComplexType::UniqueType::UNDEFINED] if types.empty?
|
|
394
|
+
ComplexType.new(types)
|
|
287
395
|
end
|
|
288
396
|
|
|
289
397
|
protected
|
|
290
398
|
|
|
399
|
+
def equality_fields
|
|
400
|
+
[self.class, items]
|
|
401
|
+
end
|
|
402
|
+
|
|
291
403
|
# @return [ComplexType]
|
|
292
404
|
def reduce_object
|
|
293
405
|
new_items = items.flat_map do |ut|
|
|
@@ -307,30 +419,29 @@ module Solargraph
|
|
|
307
419
|
# @example
|
|
308
420
|
# ComplexType.parse 'String', 'Foo', 'nil' #=> [String, Foo, nil]
|
|
309
421
|
#
|
|
310
|
-
# @
|
|
311
|
-
#
|
|
312
|
-
#
|
|
313
|
-
#
|
|
314
|
-
#
|
|
315
|
-
# used internally.
|
|
316
|
-
#
|
|
422
|
+
# @param partial [Boolean] if true, method is receiving a string
|
|
423
|
+
# that will be used inside another ComplexType. It returns
|
|
424
|
+
# arrays of ComplexTypes instead of a single cohesive one.
|
|
425
|
+
# Consumers should not need to use this parameter; it should
|
|
426
|
+
# only be used internally.
|
|
317
427
|
# @param strings [Array<String>] The type definitions to parse
|
|
318
428
|
# @return [ComplexType]
|
|
319
429
|
# # @overload parse(*strings, partial: false)
|
|
320
430
|
# # @todo Need ability to use a literal true as a type below
|
|
321
431
|
# # @param partial [Boolean] True if the string is part of a another type
|
|
322
432
|
# # @return [Array<UniqueType>]
|
|
323
|
-
# @
|
|
433
|
+
# @sg-ignore To be able to select the right signature above,
|
|
324
434
|
# Chain::Call needs to know the decl type (:arg, :optarg,
|
|
325
435
|
# :kwarg, etc) of the arguments given, instead of just having
|
|
326
436
|
# an array of Chains as the arguments.
|
|
327
437
|
def parse *strings, partial: false
|
|
328
|
-
# @type [Hash{Array<String> => ComplexType}]
|
|
438
|
+
# @type [Hash{Array<String> => ComplexType, Array<ComplexType::UniqueType>}]
|
|
329
439
|
@cache ||= {}
|
|
330
440
|
unless partial
|
|
331
441
|
cached = @cache[strings]
|
|
332
442
|
return cached unless cached.nil?
|
|
333
443
|
end
|
|
444
|
+
# @types [Array<ComplexType::UniqueType>]
|
|
334
445
|
types = []
|
|
335
446
|
key_types = nil
|
|
336
447
|
strings.each do |type_string|
|
|
@@ -342,15 +453,16 @@ module Solargraph
|
|
|
342
453
|
# @param char [String]
|
|
343
454
|
type_string&.each_char do |char|
|
|
344
455
|
if char == '='
|
|
345
|
-
#raise ComplexTypeError, "Invalid = in type #{type_string}" unless curly_stack > 0
|
|
456
|
+
# raise ComplexTypeError, "Invalid = in type #{type_string}" unless curly_stack > 0
|
|
346
457
|
elsif char == '<'
|
|
347
458
|
point_stack += 1
|
|
348
459
|
elsif char == '>'
|
|
349
|
-
if subtype_string.end_with?('=') && curly_stack
|
|
460
|
+
if subtype_string.end_with?('=') && curly_stack.positive?
|
|
350
461
|
subtype_string += char
|
|
351
462
|
elsif base.end_with?('=')
|
|
352
|
-
raise ComplexTypeError,
|
|
463
|
+
raise ComplexTypeError, 'Invalid hash thing' unless key_types.nil?
|
|
353
464
|
# types.push ComplexType.new([UniqueType.new(base[0..-2].strip)])
|
|
465
|
+
# @sg-ignore Need to add nil check here
|
|
354
466
|
types.push UniqueType.parse(base[0..-2].strip, subtype_string)
|
|
355
467
|
# @todo this should either expand key_type's type
|
|
356
468
|
# automatically or complain about not being
|
|
@@ -361,7 +473,7 @@ module Solargraph
|
|
|
361
473
|
subtype_string.clear
|
|
362
474
|
next
|
|
363
475
|
else
|
|
364
|
-
raise ComplexTypeError, "Invalid close in type #{type_string}" if point_stack
|
|
476
|
+
raise ComplexTypeError, "Invalid close in type #{type_string}" if point_stack.zero?
|
|
365
477
|
point_stack -= 1
|
|
366
478
|
subtype_string += char
|
|
367
479
|
end
|
|
@@ -371,34 +483,37 @@ module Solargraph
|
|
|
371
483
|
elsif char == '}'
|
|
372
484
|
curly_stack -= 1
|
|
373
485
|
subtype_string += char
|
|
374
|
-
raise ComplexTypeError, "Invalid close in type #{type_string}" if curly_stack
|
|
486
|
+
raise ComplexTypeError, "Invalid close in type #{type_string}" if curly_stack.negative?
|
|
375
487
|
next
|
|
376
488
|
elsif char == '('
|
|
377
489
|
paren_stack += 1
|
|
378
490
|
elsif char == ')'
|
|
379
491
|
paren_stack -= 1
|
|
380
492
|
subtype_string += char
|
|
381
|
-
raise ComplexTypeError, "Invalid close in type #{type_string}" if paren_stack
|
|
493
|
+
raise ComplexTypeError, "Invalid close in type #{type_string}" if paren_stack.negative?
|
|
382
494
|
next
|
|
383
|
-
elsif char == ',' && point_stack
|
|
495
|
+
elsif char == ',' && point_stack.zero? && curly_stack.zero? && paren_stack.zero?
|
|
384
496
|
# types.push ComplexType.new([UniqueType.new(base.strip, subtype_string.strip)])
|
|
385
497
|
types.push UniqueType.parse(base.strip, subtype_string.strip)
|
|
386
498
|
base.clear
|
|
387
499
|
subtype_string.clear
|
|
388
500
|
next
|
|
389
501
|
end
|
|
390
|
-
if point_stack
|
|
502
|
+
if point_stack.zero? && curly_stack.zero? && paren_stack.zero?
|
|
391
503
|
base.concat char
|
|
392
504
|
else
|
|
393
505
|
subtype_string.concat char
|
|
394
506
|
end
|
|
395
507
|
end
|
|
396
|
-
|
|
508
|
+
if point_stack != 0 || curly_stack != 0 || paren_stack != 0
|
|
509
|
+
raise ComplexTypeError,
|
|
510
|
+
"Unclosed subtype in #{type_string}"
|
|
511
|
+
end
|
|
397
512
|
# types.push ComplexType.new([UniqueType.new(base, subtype_string)])
|
|
398
513
|
types.push UniqueType.parse(base.strip, subtype_string.strip)
|
|
399
514
|
end
|
|
400
515
|
unless key_types.nil?
|
|
401
|
-
raise ComplexTypeError,
|
|
516
|
+
raise ComplexTypeError, 'Invalid use of key/value parameters' unless partial
|
|
402
517
|
return key_types if types.empty?
|
|
403
518
|
return [key_types, types]
|
|
404
519
|
end
|
|
@@ -410,7 +525,7 @@ module Solargraph
|
|
|
410
525
|
# @param strings [Array<String>]
|
|
411
526
|
# @return [ComplexType]
|
|
412
527
|
def try_parse *strings
|
|
413
|
-
parse
|
|
528
|
+
parse(*strings)
|
|
414
529
|
rescue ComplexTypeError => e
|
|
415
530
|
Solargraph.logger.info "Error parsing complex type `#{strings.join(', ')}`: #{e.message}"
|
|
416
531
|
ComplexType::UNDEFINED
|
|
@@ -435,9 +550,7 @@ module Solargraph
|
|
|
435
550
|
# @param dst [String]
|
|
436
551
|
# @return [String]
|
|
437
552
|
def reduce_class dst
|
|
438
|
-
while dst =~ /^(Class|Module)
|
|
439
|
-
dst = dst.sub(/^(Class|Module)\</, '').sub(/\>$/, '')
|
|
440
|
-
end
|
|
553
|
+
dst = dst.sub(/^(Class|Module)</, '').sub(/>$/, '') while dst =~ /^(Class|Module)<(.*?)>$/
|
|
441
554
|
dst
|
|
442
555
|
end
|
|
443
556
|
end
|
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Solargraph
|
|
4
|
-
module Convention
|
|
5
|
-
module DataDefinition
|
|
6
|
-
# A node wrapper for a Data definition via const assignment.
|
|
7
|
-
# @example
|
|
8
|
-
# MyData = Data.new(:bar, :baz) do
|
|
9
|
-
# def foo
|
|
10
|
-
# end
|
|
11
|
-
# end
|
|
12
|
-
class DataAssignmentNode < DataDefintionNode
|
|
13
|
-
class << self
|
|
14
|
-
# @example
|
|
15
|
-
# s(:casgn, nil, :Foo,
|
|
16
|
-
# s(:block,
|
|
17
|
-
# s(:send,
|
|
18
|
-
# s(:const, nil, :Data), :define,
|
|
19
|
-
# s(:sym, :bar),
|
|
20
|
-
# s(:sym, :baz)),
|
|
21
|
-
# s(:args),
|
|
22
|
-
# s(:def, :foo,
|
|
23
|
-
# s(:args),
|
|
24
|
-
# s(:send, nil, :bar))))
|
|
25
|
-
# @param node [::Parser::AST::Node]
|
|
26
|
-
def match?
|
|
27
|
-
return false unless node&.type == :casgn
|
|
28
|
-
return false if node.children[2].nil?
|
|
29
|
-
|
|
30
|
-
data_node = if node.children[2].type == :block
|
|
31
|
-
node.children[2].children[0]
|
|
32
|
-
else
|
|
33
|
-
node.children[2]
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
data_definition_node?(data_node)
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def class_name
|
|
41
|
-
if node.children[0]
|
|
42
|
-
Parser::NodeMethods.unpack_name(node.children[0]) + "::#{node.children[1]}"
|
|
43
|
-
else
|
|
44
|
-
node.children[1].to_s
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
private
|
|
49
|
-
|
|
50
|
-
# @return [Parser::AST::Node]
|
|
51
|
-
def data_node
|
|
52
|
-
if node.children[2].type == :block
|
|
53
|
-
node.children[2].children[0]
|
|
54
|
-
else
|
|
55
|
-
node.children[2]
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
module Convention
|
|
5
|
+
module DataDefinition
|
|
6
|
+
# A node wrapper for a Data definition via const assignment.
|
|
7
|
+
# @example
|
|
8
|
+
# MyData = Data.new(:bar, :baz) do
|
|
9
|
+
# def foo
|
|
10
|
+
# end
|
|
11
|
+
# end
|
|
12
|
+
class DataAssignmentNode < DataDefintionNode
|
|
13
|
+
class << self
|
|
14
|
+
# @example
|
|
15
|
+
# s(:casgn, nil, :Foo,
|
|
16
|
+
# s(:block,
|
|
17
|
+
# s(:send,
|
|
18
|
+
# s(:const, nil, :Data), :define,
|
|
19
|
+
# s(:sym, :bar),
|
|
20
|
+
# s(:sym, :baz)),
|
|
21
|
+
# s(:args),
|
|
22
|
+
# s(:def, :foo,
|
|
23
|
+
# s(:args),
|
|
24
|
+
# s(:send, nil, :bar))))
|
|
25
|
+
# @param node [::Parser::AST::Node]
|
|
26
|
+
def match? node
|
|
27
|
+
return false unless node&.type == :casgn
|
|
28
|
+
return false if node.children[2].nil?
|
|
29
|
+
|
|
30
|
+
data_node = if node.children[2].type == :block
|
|
31
|
+
node.children[2].children[0]
|
|
32
|
+
else
|
|
33
|
+
node.children[2]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
data_definition_node?(data_node)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def class_name
|
|
41
|
+
if node.children[0]
|
|
42
|
+
Parser::NodeMethods.unpack_name(node.children[0]) + "::#{node.children[1]}"
|
|
43
|
+
else
|
|
44
|
+
node.children[1].to_s
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
# @return [Parser::AST::Node]
|
|
51
|
+
def data_node
|
|
52
|
+
if node.children[2].type == :block
|
|
53
|
+
node.children[2].children[0]
|
|
54
|
+
else
|
|
55
|
+
node.children[2]
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -27,7 +27,7 @@ module Solargraph
|
|
|
27
27
|
# s(:send, nil, :bar)))
|
|
28
28
|
#
|
|
29
29
|
# @param node [Parser::AST::Node]
|
|
30
|
-
def match?
|
|
30
|
+
def match? node
|
|
31
31
|
return false unless node&.type == :class
|
|
32
32
|
|
|
33
33
|
data_definition_node?(node.children[1])
|
|
@@ -37,7 +37,7 @@ module Solargraph
|
|
|
37
37
|
|
|
38
38
|
# @param data_node [Parser::AST::Node]
|
|
39
39
|
# @return [Boolean]
|
|
40
|
-
def data_definition_node?
|
|
40
|
+
def data_definition_node? data_node
|
|
41
41
|
return false unless data_node.is_a?(::Parser::AST::Node)
|
|
42
42
|
return false unless data_node&.type == :send
|
|
43
43
|
return false unless data_node.children[0]&.type == :const
|
|
@@ -49,7 +49,7 @@ module Solargraph
|
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
# @param node [Parser::AST::Node]
|
|
52
|
-
def initialize
|
|
52
|
+
def initialize node
|
|
53
53
|
@node = node
|
|
54
54
|
end
|
|
55
55
|
|
|
@@ -66,7 +66,7 @@ module Solargraph
|
|
|
66
66
|
end.compact
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
-
# @return [Parser::AST::Node]
|
|
69
|
+
# @return [Parser::AST::Node, nil]
|
|
70
70
|
def body_node
|
|
71
71
|
node.children[2]
|
|
72
72
|
end
|
|
@@ -81,9 +81,11 @@ module Solargraph
|
|
|
81
81
|
node.children[1]
|
|
82
82
|
end
|
|
83
83
|
|
|
84
|
+
# @sg-ignore Need to add nil check here
|
|
84
85
|
# @return [Array<Parser::AST::Node>]
|
|
85
86
|
def data_attribute_nodes
|
|
86
|
-
|
|
87
|
+
# @sg-ignore Need to add nil check here
|
|
88
|
+
data_node.children[2..]
|
|
87
89
|
end
|
|
88
90
|
end
|
|
89
91
|
end
|
|
@@ -17,6 +17,7 @@ module Solargraph
|
|
|
17
17
|
type: :class,
|
|
18
18
|
location: loc,
|
|
19
19
|
closure: region.closure,
|
|
20
|
+
# @sg-ignore flow sensitive typing needs to handle attrs
|
|
20
21
|
name: data_definition_node.class_name,
|
|
21
22
|
comments: comments_for(node),
|
|
22
23
|
visibility: :public,
|
|
@@ -39,6 +40,7 @@ module Solargraph
|
|
|
39
40
|
# Solargraph::SourceMap::Clip#complete_keyword_parameters does not seem to currently take into account [Pin::Method#signatures] hence we only one for :kwarg
|
|
40
41
|
pins.push initialize_method_pin
|
|
41
42
|
|
|
43
|
+
# @sg-ignore flow sensitive typing needs to handle attrs
|
|
42
44
|
data_definition_node.attributes.map do |attribute_node, attribute_name|
|
|
43
45
|
initialize_method_pin.parameters.push(
|
|
44
46
|
Pin::Parameter.new(
|
|
@@ -51,6 +53,7 @@ module Solargraph
|
|
|
51
53
|
end
|
|
52
54
|
|
|
53
55
|
# define attribute readers and instance variables
|
|
56
|
+
# @sg-ignore flow sensitive typing needs to handle attrs
|
|
54
57
|
data_definition_node.attributes.each do |attribute_node, attribute_name|
|
|
55
58
|
name = attribute_name.to_s
|
|
56
59
|
method_pin = Pin::Method.new(
|
|
@@ -78,7 +81,7 @@ module Solargraph
|
|
|
78
81
|
|
|
79
82
|
private
|
|
80
83
|
|
|
81
|
-
# @return [DataDefintionNode, nil]
|
|
84
|
+
# @return [DataDefinition::DataDefintionNode, DataDefinition::DataAssignmentNode, nil]
|
|
82
85
|
def data_definition_node
|
|
83
86
|
@data_definition_node ||= if DataDefintionNode.match?(node)
|
|
84
87
|
DataDefintionNode.new(node)
|
|
@@ -90,7 +93,7 @@ module Solargraph
|
|
|
90
93
|
# @param attribute_node [Parser::AST::Node]
|
|
91
94
|
# @param attribute_name [String]
|
|
92
95
|
# @return [String, nil]
|
|
93
|
-
def attribute_comments
|
|
96
|
+
def attribute_comments attribute_node, attribute_name
|
|
94
97
|
data_comments = comments_for(attribute_node)
|
|
95
98
|
return if data_comments.nil? || data_comments.empty?
|
|
96
99
|
|