objc-dependency-tree-generator 0.0.8 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/objc_dependency_tree_generator +2 -2
- data/lib/dependency_tree.rb +119 -0
- data/lib/helpers/objc_dependency_tree_generator_helper.rb +58 -0
- data/lib/helpers/swift_primitives.rb +282 -0
- data/lib/{objc_dependencies_generator.rb → objc/objc_dependencies_generator.rb} +0 -0
- data/lib/objc_dependency_tree_generator.rb +106 -89
- data/lib/sourcekitten/sourcekitten_dependencies_generator.rb +229 -0
- data/lib/swift-ast-dump/swift_ast_dependencies_generator.rb +216 -0
- data/lib/swift-ast-dump/swift_ast_parser.rb +217 -0
- data/lib/{swift_dependencies_generator.rb → swift/swift_dependencies_generator.rb} +0 -0
- data/lib/tree_serializer.rb +64 -0
- metadata +14 -8
- data/lib/objc_dependency_tree_generator_helper.rb +0 -67
File without changes
|
@@ -1,13 +1,16 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
+
|
2
3
|
require 'optparse'
|
3
4
|
require 'yaml'
|
4
5
|
require 'json'
|
5
|
-
require 'objc_dependency_tree_generator_helper'
|
6
|
-
require 'swift_dependencies_generator'
|
7
|
-
require 'objc_dependencies_generator'
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
require 'helpers/objc_dependency_tree_generator_helper'
|
7
|
+
require 'swift/swift_dependencies_generator'
|
8
|
+
require 'objc/objc_dependencies_generator'
|
9
|
+
require 'sourcekitten/sourcekitten_dependencies_generator'
|
10
|
+
require 'dependency_tree'
|
11
|
+
require 'tree_serializer'
|
12
|
+
|
13
|
+
class DependencyTreeGenerator
|
11
14
|
def initialize(options)
|
12
15
|
@options = options
|
13
16
|
@options[:derived_data_project_pattern] = '*-*' unless @options[:derived_data_project_pattern]
|
@@ -19,125 +22,139 @@ class ObjCDependencyTreeGenerator
|
|
19
22
|
def self.parse_command_line_options
|
20
23
|
options = {}
|
21
24
|
|
22
|
-
#Defaults
|
25
|
+
# Defaults
|
23
26
|
options[:derived_data_paths] = ['~/Library/Developer/Xcode/DerivedData', '~/Library/Caches/appCode*/DerivedData']
|
24
27
|
options[:project_name] = ''
|
25
28
|
options[:output_format] = 'json'
|
29
|
+
options[:verbose] = true
|
30
|
+
options[:swift_ast_show_parsed_tree] = false
|
31
|
+
options[:ignore_primitive_types] = true
|
32
|
+
options[:show_inheritance_only] = false
|
26
33
|
|
27
|
-
|
28
|
-
parser = OptionParser.new do |o|
|
34
|
+
OptionParser.new do |o|
|
29
35
|
o.separator 'General options:'
|
30
|
-
o.on('-p PATH', '--path'
|
36
|
+
o.on('-p PATH', '--path', 'Path to directory where are your .o files were placed by the compiler', Array) do |directory|
|
31
37
|
options[:search_directories] = Array(options[:search_directories]) | Array(directory)
|
32
|
-
|
33
|
-
o.on('-D DERIVED_DATA', 'Path to directory where DerivedData is')
|
38
|
+
end
|
39
|
+
o.on('-D DERIVED_DATA', 'Path to directory where DerivedData is') do |derived_data|
|
34
40
|
options[:derived_data_paths] = [derived_data]
|
35
41
|
options[:derived_data_project_pattern] = '*'
|
36
|
-
|
37
|
-
o.on('-
|
42
|
+
end
|
43
|
+
o.on('-output PROJECT_NAME', 'Search project .o files by specified project name') do |project_name|
|
38
44
|
options[:project_name] = project_name
|
39
|
-
|
40
|
-
o.on('-t TARGET_NAME', '--target' 'Target of project', Array)
|
45
|
+
end
|
46
|
+
o.on('-t TARGET_NAME', '--target', 'Target of project', Array) do |target_name|
|
41
47
|
options[:target_names] = Array(options[:target_names]) | Array(target_name)
|
42
|
-
|
43
|
-
o.on('-e PREFIXES', "Prefixes of classes those will be exсluded from visualization. \n\t\t\t\t\tNS|UI\n\t\t\t\t\tUI|CA|MF")
|
48
|
+
end
|
49
|
+
o.on('-e PREFIXES', "Prefixes of classes those will be exсluded from visualization. \n\t\t\t\t\tNS|UI\n\t\t\t\t\tUI|CA|MF") do |exclusion_prefixes|
|
44
50
|
options[:exclusion_prefixes] = exclusion_prefixes
|
45
|
-
|
51
|
+
end
|
46
52
|
|
47
|
-
o.on('-d', '--use-dwarf-info', 'Use DWARF Information also')
|
53
|
+
o.on('-d', '--use-dwarf-info', 'Use DWARF Information also') do |v|
|
48
54
|
options[:use_dwarf] = v
|
49
|
-
|
50
|
-
|
55
|
+
end
|
56
|
+
|
57
|
+
o.on('-w', '--swift-dependencies', 'Generate swift project dependencies') do |v|
|
51
58
|
options[:swift_dependencies] = v
|
52
|
-
|
53
|
-
o.on('-
|
59
|
+
end
|
60
|
+
o.on('-k FILENAME', 'Generate dependencies from source kitten output (json)') do |v|
|
61
|
+
options[:sourcekitten_dependencies_file] = v
|
62
|
+
end
|
63
|
+
|
64
|
+
o.on('--ast-file FILENAME', 'Generate dependencies from the swift ast dump output (ast)') do |v|
|
65
|
+
options[:swift_ast_dump_file] = v
|
66
|
+
end
|
67
|
+
|
68
|
+
o.on('--ast-show-parsed-tree', 'Show ast parsing info (for swift ast parser only)') do |_v|
|
69
|
+
options[:swift_ast_show_parsed_tree] = true
|
70
|
+
end
|
71
|
+
|
72
|
+
o.on('--inheritance-only', 'Show only inheritance dependencies') do
|
73
|
+
options[:show_inheritance_only] = true
|
74
|
+
end
|
75
|
+
|
76
|
+
o.on('-f FORMAT', 'Output format. json by default. Possible values are [dot|json-pretty|json|json-var|yaml]') do |f|
|
54
77
|
options[:output_format] = f
|
55
|
-
|
78
|
+
end
|
79
|
+
o.on('-o OUTPUT_FILE', '--output', 'target of output') do |f|
|
80
|
+
options[:target_file_name] = f
|
81
|
+
end
|
56
82
|
|
57
83
|
o.separator 'Common options:'
|
58
|
-
o.on_tail('-h', 'Prints this help')
|
84
|
+
o.on_tail('-h', 'Prints this help') do
|
85
|
+
puts o
|
86
|
+
exit
|
87
|
+
end
|
59
88
|
o.parse!
|
60
|
-
|
61
89
|
end
|
62
90
|
|
63
91
|
options
|
92
|
+
end
|
64
93
|
|
94
|
+
def find_objecte_files_directories
|
95
|
+
find_project_output_directory(
|
96
|
+
@options[:derived_data_paths],
|
97
|
+
@options[:project_name],
|
98
|
+
@options[:derived_data_project_pattern],
|
99
|
+
@options[:target_names],
|
100
|
+
@options[:verbose]
|
101
|
+
)
|
65
102
|
end
|
66
103
|
|
67
|
-
def
|
68
|
-
|
69
|
-
|
70
|
-
|
104
|
+
def build_dependency_tree
|
105
|
+
tree = generate_depdendency_tree
|
106
|
+
tree.filter { |item, _| is_valid_dest?(item, @exclusion_prefixes) } if @options[:ignore_primitive_types]
|
107
|
+
tree.filter_links { |_ , _ , type | type == DependencyLinkType::INHERITANCE } if @options[:show_inheritance_only]
|
108
|
+
tree
|
109
|
+
end
|
71
110
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
@options[:target_names])
|
78
|
-
return {} unless @object_files_directories
|
79
|
-
end
|
111
|
+
def generate_depdendency_tree
|
112
|
+
return build_sourcekitten_dependency_tree if @options[:sourcekitten_dependencies_file]
|
113
|
+
return build_ast_dependency_tree if @options[:swift_ast_dump_file]
|
114
|
+
return tree_from_object_files_directory
|
115
|
+
end
|
80
116
|
|
81
|
-
|
82
|
-
|
83
|
-
exit 1
|
84
|
-
end
|
117
|
+
def tree_from_object_files_directory
|
118
|
+
tree = DependencyTree.new
|
85
119
|
|
120
|
+
return tree if !@options || @options.empty?
|
121
|
+
@object_files_directories ||= find_objecte_files_directories
|
122
|
+
return tree unless @object_files_directories
|
86
123
|
|
87
|
-
|
88
|
-
links_block = lambda { |source, dest|
|
89
|
-
links[source] = {} unless links[source]
|
90
|
-
if source != dest and is_valid_dest?(dest, @exclusion_prefixes)
|
91
|
-
links[source][dest] = 'set up'
|
92
|
-
end
|
93
|
-
}
|
94
|
-
|
124
|
+
update_tree_block = lambda { |source, target| tree.add(source, target) }
|
95
125
|
if @options[:swift_dependencies]
|
96
|
-
SwiftDependenciesGenerator.new.generate_dependencies(@object_files_directories, &
|
126
|
+
SwiftDependenciesGenerator.new.generate_dependencies(@object_files_directories, &update_tree_block)
|
97
127
|
else
|
98
|
-
ObjcDependenciesGenerator.new.generate_dependencies(@object_files_directories, @options[:use_dwarf], &
|
128
|
+
ObjcDependenciesGenerator.new.generate_dependencies(@object_files_directories, @options[:use_dwarf], &update_tree_block)
|
99
129
|
end
|
130
|
+
tree
|
131
|
+
end
|
132
|
+
|
133
|
+
def build_ast_dependency_tree
|
134
|
+
require_relative 'swift-ast-dump/swift_ast_dependencies_generator'
|
135
|
+
generator = SwiftAstDependenciesGenerator.new(
|
136
|
+
@options[:swift_ast_dump_file],
|
137
|
+
@options[:swift_ast_show_parsed_tree]
|
138
|
+
)
|
139
|
+
generator.generate_dependencies
|
140
|
+
end
|
100
141
|
|
101
|
-
|
142
|
+
def build_sourcekitten_dependency_tree
|
143
|
+
generator = SourcekittenDependenciesGenerator.new(
|
144
|
+
@options[:sourcekitten_dependencies_file]
|
145
|
+
)
|
146
|
+
generator.generate_dependencies
|
102
147
|
end
|
103
148
|
|
104
149
|
def dependencies_to_s
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
s+= <<-THEEND
|
109
|
-
var dependencies =
|
110
|
-
THEEND
|
111
|
-
end
|
150
|
+
tree = build_dependency_tree
|
151
|
+
serializer = TreeSerializer.new(tree)
|
152
|
+
output = serializer.serialize(@options[:output_format])
|
112
153
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
links_count = 0
|
117
|
-
links.each do |source, dest_hash|
|
118
|
-
links_count = links_count + dest_hash.length
|
119
|
-
dest_hash.each do |dest, _|
|
120
|
-
json_links += [{'source' => source, 'dest' => dest}]
|
121
|
-
end
|
122
|
-
end
|
123
|
-
json_result['links'] = json_links
|
124
|
-
json_result['source_files_count'] = links.length
|
125
|
-
json_result['links_count'] = links_count
|
126
|
-
|
127
|
-
if @options[:output_format] == 'dot'
|
128
|
-
indent = "\t"
|
129
|
-
s = "digraph dependencies {\n#{indent}node [fontname=monospace, fontsize=9, shape=box, style=rounded]\n"
|
130
|
-
json_links.each do |link|
|
131
|
-
s += "#{indent}\"#{link['source']}\" -> \"#{link['dest']}\"\n"
|
132
|
-
end
|
133
|
-
s += "}\n"
|
154
|
+
if @options[:target_file_name]
|
155
|
+
File.open(@options[:target_file_name], 'w').write(output.to_s)
|
134
156
|
else
|
135
|
-
|
136
|
-
s = s + json_result.to_json if @options[:output_format] == 'json' || @options[:output_format] == 'json-var'
|
137
|
-
s = s + json_result.to_yaml if @options[:output_format] == 'yaml'
|
157
|
+
output
|
138
158
|
end
|
139
|
-
s
|
140
159
|
end
|
141
|
-
|
142
|
-
|
143
|
-
end
|
160
|
+
end
|
@@ -0,0 +1,229 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'helpers/objc_dependency_tree_generator_helper'
|
3
|
+
require 'rexml/document'
|
4
|
+
require 'helpers/swift_primitives'
|
5
|
+
|
6
|
+
module SKDeclarationType
|
7
|
+
SWIFT_EXTENSION = 'source.lang.swift.decl.extension'.freeze
|
8
|
+
SWIFT_PROTOCOL = 'source.lang.swift.decl.protocol'.freeze
|
9
|
+
SWIFT_STRUCT = 'source.lang.swift.decl.struct'.freeze
|
10
|
+
SWIFT_CLASS = 'source.lang.swift.decl.class'.freeze
|
11
|
+
OBJC_PROTOCOL = 'sourcekitten.source.lang.objc.decl.protocol'.freeze
|
12
|
+
OBJC_STRUCT = 'sourcekitten.source.lang.objc.decl.struct'.freeze
|
13
|
+
OBJC_CLASS = 'sourcekitten.source.lang.objc.decl.class'.freeze
|
14
|
+
|
15
|
+
|
16
|
+
INSTANCE_VARIABLE = 'source.lang.swift.decl.var.instance'.freeze
|
17
|
+
STATIC_VARIABLE = 'source.lang.swift.decl.var.static'.freeze
|
18
|
+
INSTANCE_METHOD = 'source.lang.swift.decl.function.method.instance'.freeze
|
19
|
+
CLASS_METHOD = 'source.lang.swift.decl.function.method.class'.freeze
|
20
|
+
CALL = 'source.lang.swift.expr.call'.freeze
|
21
|
+
ARGUMENT = 'source.lang.swift.expr.argument'.freeze
|
22
|
+
DICTIONARY = 'source.lang.swift.expr.dictionary'.freeze
|
23
|
+
ARRAY = 'source.lang.swift.expr.array'.freeze
|
24
|
+
end
|
25
|
+
|
26
|
+
module SKKey
|
27
|
+
SUBSTRUCTURE = 'key.substructure'.freeze
|
28
|
+
KIND = 'key.kind'.freeze
|
29
|
+
INHERITED_TYPES = 'key.inheritedtypes'.freeze
|
30
|
+
NAME = 'key.name'.freeze
|
31
|
+
TYPE_NAME = 'key.typename'.freeze
|
32
|
+
ANNOTATED_DECLARATION = 'key.annotated_decl'.freeze
|
33
|
+
FULLY_ANNOTATED_DECLARATION = 'key.fully_annotated_decl'.freeze
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
class SourcekittenDependenciesGenerator
|
38
|
+
|
39
|
+
def initialize(source_kitten_json)
|
40
|
+
@source_kitten_json = source_kitten_json
|
41
|
+
end
|
42
|
+
# @return [DependencyTree]
|
43
|
+
def generate_dependencies
|
44
|
+
|
45
|
+
@tree = DependencyTree.new
|
46
|
+
@context = []
|
47
|
+
|
48
|
+
file = File.read(@source_kitten_json)
|
49
|
+
parsed_files = JSON.parse(file)
|
50
|
+
|
51
|
+
parsed_files.each do |parsed_file|
|
52
|
+
parsed_file.each do |_path, contents|
|
53
|
+
substructures = contents[SKKey::SUBSTRUCTURE] || next
|
54
|
+
substructures.each { |substructure| parse_structure(substructure) }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
@tree
|
59
|
+
end
|
60
|
+
|
61
|
+
def in_context(name)
|
62
|
+
@context.push(name)
|
63
|
+
yield
|
64
|
+
@context.pop
|
65
|
+
end
|
66
|
+
|
67
|
+
# @param [Hash] item array of sourcekitten substructure of class, protocol, whatever
|
68
|
+
# @param [DependencyItemType] type type of items to register
|
69
|
+
def register_item(item, type)
|
70
|
+
item_name = item[SKKey::NAME]
|
71
|
+
@tree.register(item_name, type)
|
72
|
+
|
73
|
+
inherited_types = item[SKKey::INHERITED_TYPES] || return
|
74
|
+
|
75
|
+
inherited_types.each do |inherited_type|
|
76
|
+
inherited_type_name = inherited_type[SKKey::NAME]
|
77
|
+
@tree.add(item_name, inherited_type_name)
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
# @param [Hash] element SourceKitten source
|
83
|
+
def parse_structure(element)
|
84
|
+
|
85
|
+
kind = element[SKKey::KIND]
|
86
|
+
item_name = element[SKKey::NAME]
|
87
|
+
|
88
|
+
case kind
|
89
|
+
when SKDeclarationType::SWIFT_EXTENSION
|
90
|
+
process_element(element, DependencyItemType::UNKNOWN)
|
91
|
+
|
92
|
+
when SKDeclarationType::SWIFT_PROTOCOL, SKDeclarationType::OBJC_PROTOCOL
|
93
|
+
process_element(element, DependencyItemType::PROTOCOL)
|
94
|
+
|
95
|
+
when SKDeclarationType::SWIFT_STRUCT, SKDeclarationType::OBJC_STRUCT
|
96
|
+
process_element(element, DependencyItemType::STRUCTURE)
|
97
|
+
|
98
|
+
when SKDeclarationType::SWIFT_CLASS, SKDeclarationType::OBJC_CLASS
|
99
|
+
process_element(element, DependencyItemType::CLASS)
|
100
|
+
register_types_from_annotated_declaration(element)
|
101
|
+
|
102
|
+
when SKDeclarationType::INSTANCE_VARIABLE, SKDeclarationType::INSTANCE_METHOD, SKDeclarationType::STATIC_VARIABLE, SKDeclarationType::CLASS_METHOD
|
103
|
+
@context.each do |el_name|
|
104
|
+
register_types_from_annotated_declaration(element, el_name)
|
105
|
+
end
|
106
|
+
parse_substructures(element)
|
107
|
+
|
108
|
+
when SKDeclarationType::CALL
|
109
|
+
if item_name
|
110
|
+
object_names = potential_object_names(item_name)
|
111
|
+
object_names.each { |on| @context.each { |el_name| @tree.add(el_name, on, DependencyLinkType::CALL)} }
|
112
|
+
end
|
113
|
+
parse_substructures(element)
|
114
|
+
|
115
|
+
when SKDeclarationType::ARGUMENT, SKDeclarationType::DICTIONARY, SKDeclarationType::ARRAY
|
116
|
+
parse_substructures(element)
|
117
|
+
|
118
|
+
else
|
119
|
+
# do nothing
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# @param [Hash] item array of sourcekitten substructure of class, protocol, whatever
|
124
|
+
# @param [DependencyItemType] type type of items to register
|
125
|
+
def process_element(element, type)
|
126
|
+
register_item(element, type)
|
127
|
+
item_name = element[SKKey::NAME] || return
|
128
|
+
link_items_from_context(item_name)
|
129
|
+
in_context(item_name) do
|
130
|
+
parse_substructures(element)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def parse_substructures(element)
|
135
|
+
sub_structures = element[SKKey::SUBSTRUCTURE] || return
|
136
|
+
sub_structures.each { |it| parse_structure(it) }
|
137
|
+
end
|
138
|
+
|
139
|
+
def link_items_from_context(item_name)
|
140
|
+
@context.each { |el_name| @tree.add(el_name, item_name) }
|
141
|
+
end
|
142
|
+
|
143
|
+
def register_types_from_annotated_declaration(element, name = nil)
|
144
|
+
item_name = name || element[SKKey::NAME] || return
|
145
|
+
annotated_decl = element[SKKey::ANNOTATED_DECLARATION] || return
|
146
|
+
|
147
|
+
doc = REXML::Document.new(annotated_decl)
|
148
|
+
|
149
|
+
has_typealiases = false
|
150
|
+
doc.each_element('//Declaration/Type') do |el|
|
151
|
+
dependency_type = el.text.to_s
|
152
|
+
|
153
|
+
# get el type
|
154
|
+
attribute_el = el.attribute('usr') || next
|
155
|
+
|
156
|
+
if is_typealias(element, dependency_type)
|
157
|
+
has_typealiases = true
|
158
|
+
next
|
159
|
+
end
|
160
|
+
|
161
|
+
attribute = attribute_el.to_s
|
162
|
+
if attribute.start_with? 's:P'
|
163
|
+
@tree.add(item_name, dependency_type)
|
164
|
+
@tree.register(dependency_type, DependencyItemType::PROTOCOL)
|
165
|
+
elsif attribute.start_with? 'c:objc(pl)'
|
166
|
+
@tree.add(item_name, dependency_type)
|
167
|
+
@tree.register(dependency_type, DependencyItemType::PROTOCOL)
|
168
|
+
elsif attribute.start_with? 'c:objc(cs)'
|
169
|
+
@tree.add(item_name, dependency_type)
|
170
|
+
@tree.register(dependency_type, DependencyItemType::CLASS)
|
171
|
+
elsif attribute.start_with? 's:C'
|
172
|
+
@tree.add(item_name, dependency_type)
|
173
|
+
@tree.register(dependency_type, DependencyItemType::CLASS)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
register_types_from_type_name(element, item_name) if has_typealiases
|
178
|
+
end
|
179
|
+
|
180
|
+
def register_types_from_type_name(element, item_name)
|
181
|
+
type_name_string = element[SKKey::TYPE_NAME] || return
|
182
|
+
|
183
|
+
generics = generic_parameters(element)
|
184
|
+
type_names(type_name_string)
|
185
|
+
.select { |type_name| !generics.include?(type_name) }
|
186
|
+
.each { |type_name| @tree.add(item_name, type_name) }
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
# @return [Array<String>] array of generic names for element
|
191
|
+
def generic_parameters(element)
|
192
|
+
fully_annotated_decl = element[SKKey::FULLY_ANNOTATED_DECLARATION]
|
193
|
+
return [] if fully_annotated_decl.nil?
|
194
|
+
generics = []
|
195
|
+
doc = REXML::Document.new(fully_annotated_decl)
|
196
|
+
doc.each_element('//decl.generic_type_param.name') do |el|
|
197
|
+
generics.push(el.text.to_s)
|
198
|
+
end
|
199
|
+
generics
|
200
|
+
end
|
201
|
+
|
202
|
+
def is_typealias(element, name)
|
203
|
+
fully_annotated_decl = element[SKKey::FULLY_ANNOTATED_DECLARATION]
|
204
|
+
return false if fully_annotated_decl.nil?
|
205
|
+
doc = REXML::Document.new(fully_annotated_decl)
|
206
|
+
doc.each_element('//ref.typealias') do |el|
|
207
|
+
return true if el.text.to_s == name
|
208
|
+
end
|
209
|
+
|
210
|
+
false
|
211
|
+
end
|
212
|
+
|
213
|
+
# Returns an array of strings, which represents type names
|
214
|
+
# @param [String] type_name_string sourcekitten type name
|
215
|
+
# @return [Array<String>] array of types, found in this type
|
216
|
+
def type_names(type_name_string)
|
217
|
+
type_name_string
|
218
|
+
.split(/\W+/)
|
219
|
+
.select { |t| !t.empty? }
|
220
|
+
.select { |t| !is_primitive_swift_type?(t) }
|
221
|
+
end
|
222
|
+
|
223
|
+
def potential_object_names(call_string)
|
224
|
+
(call_string.scan(/^[A-Z_][A-Za-z0-9_]+/) + call_string.scan(/\s[A-Z_][A-Za-z0-9_]+/))
|
225
|
+
.select { |t| !t.empty? }
|
226
|
+
.select { |t| !is_primitive_swift_type?(t) }
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|