sord 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5de7160886a2e2fd6df02d245102315d772029a951075d885a88ac253ed1ea0a
4
- data.tar.gz: a14f5c19796cfd9033111c76ef144541a3dd9304d1421341fcb3fb9e9f1e65f3
3
+ metadata.gz: 2ea0cd169aa8fb78913ee6547177fcf31665bd776846f29652f3a1c156caf856
4
+ data.tar.gz: 9ac77d33766f851ffdaac0c3fe34e238a432e57ea8d4518ff1e095382ac9dc4a
5
5
  SHA512:
6
- metadata.gz: 3ec7636f302c059c5a1fe826cfa077c786410b1d3378783feac63ce77a7600df516fc1f1794f869996d0aa64f13de2a42eceda806d7378efece157c0254a58a6
7
- data.tar.gz: 208a0d864121ca13eeeb2c5713cd98768e4710dc9cf2bdacb9fd13471e97e2dc580f9b31530dee847b4de8e9d62072f1a1cef640c4f768f4537754058ad253c3
6
+ metadata.gz: 9ae4e4cfba74c8bfcdb7f91296d18f1aa4ccfd6545d407b8997df4516a181e5b4aa85277bbdef623aed16de060a3479a9c5b7c6b4b34658ae328dd62f9cba929
7
+ data.tar.gz: cba4988403819d61a068adb45105941951a172d96997258f92848b014e51f657d65dacd7963d27dd1b3803baf3c78cf5a3764593201954c47e4abc4ebe4b9c8c
data/.gitignore CHANGED
@@ -10,3 +10,4 @@
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
12
  .vscode/
13
+ Gemfile.lock
data/CHANGELOG.md ADDED
@@ -0,0 +1,92 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
5
+
6
+ ## [0.7.0]
7
+ ### Added
8
+ - A warning message is now shown if the YARD registry has no objects. (#31)
9
+ - Integer, Float and Symbol literals are now supported as types. (#26)
10
+ - Add support for multi-method YARD duck types. (#38)
11
+ - Namespaces are now indented properly. (#41)
12
+ - Individual method and namespace counts are now shown, rather than just an overall object count. (#36)
13
+
14
+ ### Changed
15
+ - Paths to log message items are now bold rather than white, so that they can be seen on white terminals. (#28)
16
+ - Alias methods are now ignored. (#34)
17
+ - Remove Gemfile.lock. (#33)
18
+ - YARD is executed when Sord is executed. To disable this behaviour, use `--no-regenerate`. (#31)
19
+
20
+ ### Fixed
21
+ - Resolved crash when a @return tag gave no type. (#35)
22
+
23
+ ## [0.6.0] - 2019-06-23
24
+ ### Added
25
+ - Namespaces are now nested inside each other in the RBI file, fixing many constant scoping issues. (#25)
26
+
27
+ ### Changed
28
+ - Move unfinished tasks from README to GitHub issues.
29
+
30
+ ### Fixed
31
+ - Fix typo of 'duck' as 'ducl' (#24)
32
+
33
+ ## [0.5.0] - 2019-06-23
34
+ ### Added
35
+ - Hash rocket syntax for hash types is now supported. (#18)
36
+ - Arrays with multiple element types are handled correctly. (#21)
37
+
38
+ ### Fixed
39
+ - Move a dependency from Gemfile to Gemspec for consistency. (#19)
40
+ - Fix bug where splat-args (`*a`) were always called `args` in signatures. (#20)
41
+
42
+ ## [0.4.1] - 2019-06-22
43
+ ### Fixed
44
+ - The changelog for this version is the same as 0.4.0, but resolving an issue where some changes were not published correctly to RubyGems.
45
+
46
+ ## [0.4.0] - 2019-06-22 [YANKED]
47
+ ### Added
48
+ - Commander is now used for the CLI, which enables a `--help` switch.
49
+ - Add a `--no-comments` switch for disabling comments in the RBI file.
50
+
51
+ ### Changed
52
+ - Sord now exits as early as possible if no filename is specified. (#17)
53
+
54
+ ### Fixed
55
+ - Remove & in block parameter names in signatures, fixing a syntax error in RBIs. (#16)
56
+
57
+ ## [0.3.0] - 2019-06-22
58
+ ### Added
59
+ - `self` now resolves to a type in signatures.
60
+ - `true` and `false` are now converted to `T::Boolean` in signatures.
61
+ - If a `T.any` contains `nil`, it now instead wraps that part of the signature in a `T.nilable` instead.
62
+ - Add GitHub issue templates.
63
+
64
+ ### Changed
65
+ - `.vscode` is now git-ignored.
66
+ - Method definitions now have a semicolon before the end for consistency with Sorbet's RBIs. (#6)
67
+ - `params` is now omitted from signatures if a method has no parameters. (#4)
68
+
69
+ ### Fixed
70
+ - Fix kwargs in signatures by removing the duplicate colon from their identifier. (#12)
71
+ - Fix kwargs in definitions by not inserting an equal-to symbol for their defaults. (#11)
72
+
73
+ ## [0.2.1] - 2019-06-22
74
+ ### Fixed
75
+ - Fix exception on launch due to forgetting to initialise a class variable. (#1)
76
+
77
+ ## [0.2.0] - 2019-06-22
78
+ ### Added
79
+ - Add RSpec tests.
80
+ - Add the Logging class with prettier output.
81
+ - Generic types can now take more than one type parameter.
82
+ - Add documentation for all classes.
83
+ - Add a README.
84
+ - Add Sorbet directory and typing mode comments (`srb init`).
85
+
86
+ ### Changed
87
+ - Sord now requires a command-line argument to save the RBI to.
88
+
89
+ ## [0.1.0] 2019-06-21
90
+ ### Added
91
+ - First release.
92
+
data/README.md CHANGED
@@ -21,10 +21,12 @@ Sord has the following features:
21
21
 
22
22
  Install Sord with `gem install sord`.
23
23
 
24
+ **NOTE**: You need to run `yard` before you generate the `.rbi` file or
25
+ Sord won't have any information to work with.
26
+
24
27
  Sord is a command line tool. To use it, open a terminal in the root directory
25
- of your project, and run `yard` to generate a YARD registry if you haven't
26
- already. Then, invoke `sord`, passing a path of where you'd like to save your
27
- RBI to (this file will be overwritten):
28
+ of your project and invoke `sord`, passing a path where you'd like to save your
29
+ `.rbi` (this file will be overwritten):
28
30
 
29
31
  ```
30
32
  sord defs.rbi
@@ -34,6 +36,13 @@ Sord will print information about what it's inferred as it runs. It is best to
34
36
  fix any issues in the YARD documentation, as any edits made to the resulting
35
37
  RBI file will be replaced if you re-run Sord.
36
38
 
39
+ ### Flags
40
+
41
+ Sord also takes some flags to alter the generated `.rbi` file:
42
+
43
+ - `--no-comments`: Generates the `.rbi` file without any comments about
44
+ warnings/inferences/errors.
45
+
37
46
  ## Example
38
47
 
39
48
  Say we have this file, called `test.rb`:
@@ -78,24 +87,24 @@ files! Note the `.rbi` file extension.) In doing this, Sord prints:
78
87
  The `test.rbi` file then contains a complete RBI file for `test.rb`:
79
88
 
80
89
  ```ruby
81
- # typed: true
90
+ # typed: strong
82
91
  module Example
83
- end
84
- class Example::Person
92
+ class Person
85
93
  sig { params(name: String, age: Integer).returns(Example::Person) }
86
- def initialize(name, age) end
87
- sig { params().returns(String) }
88
- def name() end
94
+ def initialize(name, age); end
95
+ sig { returns(String) }
96
+ def name(); end
89
97
  # sord infer - inferred type of parameter "value" as String using getter's return type
90
98
  sig { params(value: String).returns(String) }
91
- def name=(value) end
92
- sig { params().returns(Integer) }
93
- def age() end
99
+ def name=(value); end
100
+ sig { returns(Integer) }
101
+ def age(); end
94
102
  # sord infer - inferred type of parameter "value" as Integer using getter's return type
95
103
  sig { params(value: Integer).returns(Integer) }
96
- def age=(value) end
104
+ def age=(value); end
97
105
  sig { params(possible_names: T::Array[String], possible_ages: T::Array[Integer]).returns(Example::Person) }
98
- def self.construct_randomly(possible_names, possible_ages) end
106
+ def self.construct_randomly(possible_names, possible_ages); end
107
+ end
99
108
  end
100
109
  ```
101
110
 
data/exe/sord CHANGED
@@ -10,16 +10,27 @@ default_command :gen
10
10
  command :gen do |c|
11
11
  c.syntax = 'sord gen <output-file> [options]'
12
12
  c.description = 'Generates an RBI file from this directory\'s YARD docs'
13
- c.option '--[no-]comments', 'Disables informational/warning comments in the RBI file'
13
+ c.option '--[no-]comments', 'Controls informational/warning comments in the RBI file'
14
+ c.option '--[no-]regenerate', 'Controls whether YARD is executed before Sord runs'
14
15
 
15
16
  c.action do |args, options|
16
- options.default comments: true
17
+ options.default comments: true, regenerate: true
17
18
 
18
19
  if args.length != 1
19
20
  Sord::Logging.error('Must specify filename')
20
21
  exit 1
21
22
  end
22
23
 
24
+ begin
25
+ Sord::Logging.info('Running YARD...')
26
+ `yard`
27
+ rescue Errno::ENOENT
28
+ Sord::Logging.error('The YARD tool could not be found on your PATH.')
29
+ Sord::Logging.error('You may need to run \'gem install yard\'.')
30
+ Sord::Logging.error('If documentation has already been generated, pass --no-regenerate to Sord.')
31
+ exit 1
32
+ end
33
+
23
34
  Sord::RbiGenerator.new(options).run(args.first)
24
35
  end
25
36
  end
data/lib/sord/logging.rb CHANGED
@@ -7,6 +7,11 @@ module Sord
7
7
  # The callables should take three parameters: (kind, msg, item).
8
8
  @@hooks = []
9
9
 
10
+ # @return [Array<Proc>] The hooks registered on the logger.
11
+ def self.hooks
12
+ @@hooks
13
+ end
14
+
10
15
  # Whether log messages should be printed or not.
11
16
  @@silent = false
12
17
 
@@ -33,14 +38,15 @@ module Sord
33
38
  # @param [YARD::CodeObjects::Base] item The CodeObject which this log
34
39
  # is associated with, if any. This is shown before the log message if it is
35
40
  # specified.
36
- def self.generic(kind, header, msg, item)
41
+ # @param [Integer] indent_level The level at which to indent the code.
42
+ def self.generic(kind, header, msg, item, indent_level = 0)
37
43
  if item
38
- puts "#{header} (#{item.path.light_white}) #{msg}" unless silent?
44
+ puts "#{header} (#{item.path.bold}) #{msg}" unless silent?
39
45
  else
40
46
  puts "#{header} #{msg}" unless silent?
41
47
  end
42
48
 
43
- invoke_hooks(kind, msg, item)
49
+ invoke_hooks(kind, msg, item, indent_level)
44
50
  end
45
51
 
46
52
  # Print a warning message. This should be used for things which require the
@@ -49,8 +55,20 @@ module Sord
49
55
  # @param [YARD::CodeObjects::Base] item The CodeObject which this log
50
56
  # is associated with, if any. This is shown before the log message if it is
51
57
  # specified.
52
- def self.warn(msg, item=nil)
53
- generic(:warn, '[WARN ]'.yellow, msg, item)
58
+ # @param [Integer] indent_level The level at which to indent the code.
59
+ def self.warn(msg, item = nil, indent_level = 0)
60
+ generic(:warn, '[WARN ]'.yellow, msg, item, indent_level)
61
+ end
62
+
63
+ # Print an info message. This should be used for generic informational
64
+ # messages which the user doesn't need to act on.
65
+ # @param [String] msg The log message to write.
66
+ # @param [YARD::CodeObjects::Base] item The CodeObject which this log
67
+ # is associated with, if any. This is shown before the log message if it is
68
+ # specified.
69
+ # @param [Integer] indent_level The level at which to indent the code.
70
+ def self.info(msg, item = nil, indent_level = 0)
71
+ generic(:info, '[INFO ]', msg, item, indent_level)
54
72
  end
55
73
 
56
74
  # Print a duck-typing message. This should be used when the YARD
@@ -60,8 +78,9 @@ module Sord
60
78
  # @param [YARD::CodeObjects::Base] item The CodeObject which this log
61
79
  # is associated with, if any. This is shown before the log message if it is
62
80
  # specified.
63
- def self.duck(msg, item=nil)
64
- generic(:duck, '[DUCK ]'.cyan, msg, item)
81
+ # @param [Integer] indent_level The level at which to indent the code.
82
+ def self.duck(msg, item = nil, indent_level = 0)
83
+ generic(:duck, '[DUCK ]'.cyan, msg, item, indent_level)
65
84
  end
66
85
 
67
86
  # Print an error message. This should be used for things which require the
@@ -70,8 +89,9 @@ module Sord
70
89
  # @param [YARD::CodeObjects::Base] item The CodeObject which this log
71
90
  # is associated with, if any. This is shown before the log message if it is
72
91
  # specified.
73
- def self.error(msg, item=nil)
74
- generic(:error, '[ERROR]'.red, msg, item)
92
+ # @param [Integer] indent_level The level at which to indent the code.
93
+ def self.error(msg, item = nil, indent_level = 0)
94
+ generic(:error, '[ERROR]'.red, msg, item, indent_level)
75
95
  end
76
96
 
77
97
  # Print an infer message. This should be used when the user should be told
@@ -81,8 +101,9 @@ module Sord
81
101
  # @param [YARD::CodeObjects::Base] item The CodeObject which this log
82
102
  # is associated with, if any. This is shown before the log message if it is
83
103
  # specified.
84
- def self.infer(msg, item=nil)
85
- generic(:infer, '[INFER]'.light_blue, msg, item)
104
+ # @param [Integer] indent_level The level at which to indent the code.
105
+ def self.infer(msg, item = nil, indent_level = 0)
106
+ generic(:infer, '[INFER]'.light_blue, msg, item, indent_level)
86
107
  end
87
108
 
88
109
  # Print an omit message. This should be used as a special type of warning
@@ -92,8 +113,9 @@ module Sord
92
113
  # @param [YARD::CodeObjects::Base] item The CodeObject which this log
93
114
  # is associated with, if any. This is shown before the log message if it is
94
115
  # specified.
95
- def self.omit(msg, item=nil)
96
- generic(:omit, '[OMIT ]'.magenta, msg, item)
116
+ # @param [Integer] indent_level The level at which to indent the code.
117
+ def self.omit(msg, item = nil, indent_level = 0)
118
+ generic(:omit, '[OMIT ]'.magenta, msg, item, indent_level)
97
119
  end
98
120
 
99
121
  # Print a done message. This should be used when a process completes
@@ -101,7 +123,8 @@ module Sord
101
123
  # @param [YARD::CodeObjects::Base] item The CodeObject which this log
102
124
  # is associated with, if any. This is shown before the log message if it is
103
125
  # specified.
104
- def self.done(msg, item=nil)
126
+ # @param [Integer] indent_level The level at which to indent the code.
127
+ def self.done(msg, item = nil, indent_level = 0)
105
128
  generic(:done, '[DONE ]'.green, msg, item)
106
129
  end
107
130
 
@@ -111,9 +134,10 @@ module Sord
111
134
  # @param [YARD::CodeObjects::Base] item The CodeObject which this log
112
135
  # is associated with, if any. This is shown before the log message if it is
113
136
  # specified.
114
- def self.invoke_hooks(kind, msg, item)
137
+ # @param [Integer] indent_level The level at which to indent the code.
138
+ def self.invoke_hooks(kind, msg, item, indent_level = 0)
115
139
  @@hooks.each do |hook|
116
- hook.(kind, msg, item) rescue nil
140
+ hook.(kind, msg, item, indent_level) rescue nil
117
141
  end
118
142
  end
119
143
 
@@ -123,6 +147,7 @@ module Sord
123
147
  # @yieldparam [YARD::CodeObjects::Base] item The CodeObject which this log
124
148
  # is associated with, if any. This is shown before the log message if it is
125
149
  # specified.
150
+ # @yieldparam [Integer] indent_level The level at which to indent the code.
126
151
  # @yieldreturn [void]
127
152
  def self.add_hook(&blk)
128
153
  @@hooks << blk
@@ -12,52 +12,81 @@ module Sord
12
12
 
13
13
  # @return [Integer] The number of objects this generator has processed so
14
14
  # far.
15
- attr_reader :object_count
15
+ def object_count
16
+ @namespace_count + @method_count
17
+ end
18
+
19
+ # @return [Array<Array(String, YARD::CodeObjects::Base, Integer)>] The
20
+ # errors encountered by by the generator. Each element is of the form
21
+ # [message, item, line].
22
+ attr_reader :warnings
16
23
 
17
24
  # Create a new RBI generator.
18
25
  # @param [Hash] options
19
26
  # @return [RbiGenerator]
20
27
  def initialize(options)
21
28
  @rbi_contents = ['# typed: strong']
22
- @object_count = 0
29
+ @namespace_count = 0
30
+ @method_count = 0
31
+ @warnings = []
23
32
 
24
33
  # Hook the logger so that messages are added as comments to the RBI file
25
- Logging.add_hook do |type, msg, item|
26
- rbi_contents << " # sord #{type} - #{msg}"
34
+ Logging.add_hook do |type, msg, item, indent_level = 0|
35
+ rbi_contents << "#{' ' * (indent_level + 1)}# sord #{type} - #{msg}"
27
36
  end if options.comments
37
+
38
+ # Hook the logger so that warnings are collected
39
+ Logging.add_hook do |type, msg, item, indent_level = 0|
40
+ warnings << [msg, item, rbi_contents.length] \
41
+ if type == :warn
42
+ end
28
43
  end
29
44
 
30
- # Increment the object counter.
45
+ # Increment the namespace counter.
31
46
  # @return [void]
32
- def count_object
33
- @object_count += 1
47
+ def count_namespace
48
+ @namespace_count += 1
49
+ end
50
+
51
+ # Increment the method counter.
52
+ # @return [void]
53
+ def count_method
54
+ @method_count += 1
34
55
  end
35
56
 
36
57
  # Given a YARD CodeObject, add lines defining its mixins (that is, extends
37
58
  # and includes) to the current RBI file.
38
59
  # @param [YARD::CodeObjects::Base] item
60
+ # @param [Integer] indent_level
39
61
  # @return [void]
40
- def add_mixins(item)
62
+ def add_mixins(item, indent_level)
41
63
  extends = item.instance_mixins
42
64
  includes = item.class_mixins
43
65
 
44
66
  extends.each do |this_extend|
45
- rbi_contents << " extend #{this_extend.path}"
67
+ rbi_contents << "#{' ' * (indent_level + 1)}extend #{this_extend.path}"
46
68
  end
47
69
  includes.each do |this_include|
48
- rbi_contents << " include #{this_include.path}"
70
+ rbi_contents << "#{' ' * (indent_level + 1)}include #{this_include.path}"
49
71
  end
50
72
  end
51
73
 
52
74
  # Given a YARD NamespaceObject, add lines defining its methods and their
53
75
  # signatures to the current RBI file.
54
76
  # @param [YARD::CodeObjects::NamespaceObject] item
77
+ # @param [Integer] indent_level
55
78
  # @return [void]
56
- def add_methods(item)
79
+ def add_methods(item, indent_level)
57
80
  # TODO: block documentation
58
81
 
59
82
  item.meths.each do |meth|
60
- count_object
83
+ count_method
84
+
85
+ # If the method is an alias, skip it so we don't define it as a
86
+ # separate method. Sorbet will handle it automatically.
87
+ if meth.is_alias?
88
+ next
89
+ end
61
90
 
62
91
  parameter_list = meth.parameters.map do |name, default|
63
92
  # Handle these three main cases:
@@ -95,19 +124,19 @@ module Sord
95
124
  getter = item.meths.find { |m| m.path == getter_path }
96
125
 
97
126
  unless getter
98
- Logging.omit("no YARD type given for #{name.inspect}, using T.untyped", meth)
127
+ Logging.omit("no YARD type given for #{name.inspect}, using T.untyped", meth, indent_level)
99
128
  next "#{name}: T.untyped"
100
129
  end
101
130
 
102
131
  inferred_type = TypeConverter.yard_to_sorbet(
103
132
  getter.tags('return').flat_map(&:types), meth)
104
133
 
105
- Logging.infer("inferred type of parameter #{name.inspect} as #{inferred_type} using getter's return type", meth)
134
+ Logging.infer("inferred type of parameter #{name.inspect} as #{inferred_type} using getter's return type", meth, indent_level)
106
135
  # Get rid of : on keyword arguments.
107
136
  name = name.chop if name.end_with?(':')
108
137
  "#{name}: #{inferred_type}"
109
138
  else
110
- Logging.omit("no YARD type given for #{name.inspect}, using T.untyped", meth)
139
+ Logging.omit("no YARD type given for #{name.inspect}, using T.untyped", meth, indent_level)
111
140
  # Get rid of : on keyword arguments.
112
141
  name = name.chop if name.end_with?(':')
113
142
  "#{name}: T.untyped"
@@ -117,7 +146,7 @@ module Sord
117
146
  return_tags = meth.tags('return')
118
147
  returns = if return_tags.length == 0
119
148
  "void"
120
- elsif return_tags.length == 1 && return_tags.first.types.first.downcase == "void"
149
+ elsif return_tags.length == 1 && return_tags&.first&.types&.first&.downcase == "void"
121
150
  "void"
122
151
  else
123
152
  "returns(#{TypeConverter.yard_to_sorbet(meth.tag('return').types, meth)})"
@@ -125,31 +154,32 @@ module Sord
125
154
 
126
155
  prefix = meth.scope == :class ? 'self.' : ''
127
156
 
128
- sig = sig_params_list.empty? ? " sig { #{returns} }" : " sig { params(#{sig_params_list}).#{returns} }"
157
+ sig = sig_params_list.empty? ? "#{' ' * (indent_level + 1)}sig { #{returns} }" : "#{' ' * (indent_level + 1)}sig { params(#{sig_params_list}).#{returns} }"
129
158
  rbi_contents << sig
130
159
 
131
- rbi_contents << " def #{prefix}#{meth.name}(#{parameter_list}); end"
160
+ rbi_contents << "#{' ' * (indent_level + 1)}def #{prefix}#{meth.name}(#{parameter_list}); end"
132
161
  end
133
162
  end
134
163
 
135
164
  # Given a YARD NamespaceObject, add lines defining its mixins, methods
136
165
  # and children to the RBI file.
137
166
  # @param [YARD::CodeObjects::NamespaceObject] item
138
- def add_namespace(item)
139
- count_object
167
+ # @param [Integer] indent_level
168
+ def add_namespace(item, indent_level = 0)
169
+ count_namespace
140
170
 
141
171
  if item.type == :class && item.superclass.to_s != "Object"
142
- rbi_contents << "class #{item.name} < #{item.superclass.path}"
172
+ rbi_contents << "#{' ' * indent_level}class #{item.name} < #{item.superclass.path}"
143
173
  else
144
- rbi_contents << "#{item.type} #{item.name}"
174
+ rbi_contents << "#{' ' * indent_level}#{item.type} #{item.name}"
145
175
  end
146
- add_mixins(item)
147
- add_methods(item)
176
+ add_mixins(item, indent_level)
177
+ add_methods(item, indent_level)
148
178
 
149
179
  item.children.select { |x| [:class, :module].include?(x.type) }
150
- .each { |child| add_namespace(child) }
180
+ .each { |child| add_namespace(child, indent_level + 1) }
151
181
 
152
- rbi_contents << "end"
182
+ rbi_contents << "#{' ' * indent_level}end"
153
183
  end
154
184
 
155
185
  # Generates the RBI file and writes it to the given file path.
@@ -171,11 +201,29 @@ module Sord
171
201
  # Write the file
172
202
  File.write(filename, rbi_contents.join(?\n))
173
203
 
174
- Logging.done("Processed #{object_count} objects")
204
+ if object_count.zero?
205
+ Logging.warn("No objects processed.")
206
+ Logging.warn("Have you definitely generated the YARD documentation for this project?")
207
+ Logging.warn("Run `yard` to generate docs.")
208
+ end
209
+
210
+ Logging.done("Processed #{object_count} objects (#{@namespace_count} namespaces and #{@method_count} methods)")
211
+
212
+ Logging.hooks.clear
213
+
214
+ unless warnings.empty?
215
+ Logging.warn("There were #{warnings.length} important warnings in the RBI file, listed below.")
216
+ Logging.warn("The types which caused them have been replaced with SORD_ERROR_ constants.")
217
+ Logging.warn("Please edit the file near the line numbers given to fix these errors.")
218
+ Logging.warn("Alternatively, edit your YARD documentation so that your types are valid and re-run Sord.")
219
+ warnings.each do |(msg, item, line)|
220
+ puts " #{"Line #{line} |".light_black} (#{item.path.bold}) #{msg}"
221
+ end
222
+ end
175
223
  rescue
176
224
  Logging.error($!)
177
225
  $@.each do |line|
178
- puts " #{line}".light_white
226
+ puts " #{line}"
179
227
  end
180
228
  end
181
229
  end
@@ -1,3 +1,4 @@
1
+ require 'yaml'
1
2
  require 'sord/logging'
2
3
 
3
4
  module Sord
@@ -7,14 +8,24 @@ module Sord
7
8
  # "Foo", "Foo::Bar", and "::Foo::Bar" are all matches, whereas "Foo.Bar"
8
9
  # or "Foo#bar" are not.
9
10
  SIMPLE_TYPE_REGEX =
10
- /(?:\:\:)?[a-zA-Z_][a-zA-Z_0-9]*(?:\:\:[a-zA-Z_][a-zA-Z_0-9]*)*/
11
+ /(?:\:\:)?[a-zA-Z_][\w]*(?:\:\:[a-zA-Z_][\w]*)*/
11
12
 
12
13
  # A regular expression which matches a Ruby namespace immediately followed
13
- # by another Ruby namespace in angle brackets. This is the format usually
14
- # used in YARD to model generic types, such as "Array<String>",
15
- # "Hash<String, Symbol>", "Hash{String => Symbol}", etc.
14
+ # by another Ruby namespace in angle brackets or curly braces.
15
+ # This is the format usually used in YARD to model generic
16
+ # types, such as "Array<String>", "Hash<String, Symbol>",
17
+ # "Hash{String => Symbol}", etc.
16
18
  GENERIC_TYPE_REGEX =
17
19
  /(#{SIMPLE_TYPE_REGEX})\s*[<{]\s*(.*)\s*[>}]/
20
+
21
+ # Match duck types which require the object implement one or more methods,
22
+ # like '#foo', '#foo & #bar', '#foo&#bar&#baz', and '#foo&#bar&#baz&#foo_bar'.
23
+ DUCK_TYPE_REGEX =
24
+ /^\##{SIMPLE_TYPE_REGEX}(?:( ?\& ?\#)*[a-zA-Z_][a-zA-Z_0-9]*)*$/
25
+
26
+ # A regular expression which matches ordered lists in the format of
27
+ # either "Array(String, Symbol)" or "(String, Symbol)".
28
+ ORDERED_LIST_REGEX = /^(?:Array|)\((.*)\s*\)$/
18
29
 
19
30
  # An array of built-in generic types supported by Sorbet.
20
31
  SORBET_SUPPORTED_GENERIC_TYPES = %w{Array Set Enumerable Enumerator Range Hash}
@@ -33,11 +44,11 @@ module Sord
33
44
  while character_pointer < params.length
34
45
  should_buffer = true
35
46
 
36
- current_bracketing_level += 1 if ['<', '{'].include?(params[character_pointer])
47
+ current_bracketing_level += 1 if ['<', '{', '('].include?(params[character_pointer])
37
48
  # Decrease bracketing level by 1 when encountering `>` or `}`, unless
38
49
  # the previous character is `=` (to prevent hash rockets from causing
39
50
  # nesting problems).
40
- current_bracketing_level -= 1 if ['>', '}'].include?(params[character_pointer]) && params[character_pointer - 1] != '='
51
+ current_bracketing_level -= 1 if ['>', '}', ')'].include?(params[character_pointer]) && params[character_pointer - 1] != '='
41
52
 
42
53
  # Handle commas as separators.
43
54
  # e.g. Hash<Symbol, String>
@@ -98,7 +109,7 @@ module Sord
98
109
  Logging.warn("#{yard} is probably not a type, but using anyway", item)
99
110
  end
100
111
  yard
101
- when /^\##{SIMPLE_TYPE_REGEX}$/
112
+ when DUCK_TYPE_REGEX
102
113
  Logging.duck("#{yard} looks like a duck type, replacing with T.untyped", item)
103
114
  'T.untyped'
104
115
  when /^#{GENERIC_TYPE_REGEX}$/
@@ -117,7 +128,19 @@ module Sord
117
128
  Logging.warn("unsupported generic type #{generic_type.inspect} in #{yard.inspect}", item)
118
129
  "SORD_ERROR_#{generic_type.gsub(/[^0-9A-Za-z_]/i, '')}"
119
130
  end
131
+ # Converts ordered lists like Array(Symbol, String) or (Symbol, String)
132
+ # into Sorbet Tuples like [Symbol, String].
133
+ when ORDERED_LIST_REGEX
134
+ type_parameters = $1
135
+ parameters = split_type_parameters(type_parameters)
136
+ .map { |x| yard_to_sorbet(x, item) }
137
+ "[#{parameters.join(', ')}]"
120
138
  else
139
+ # Check for literals
140
+ from_yaml = YAML.load(yard) rescue nil
141
+ return from_yaml.class.to_s \
142
+ if [Symbol, Float, Integer].include?(from_yaml.class)
143
+
121
144
  Logging.warn("#{yard.inspect} does not appear to be a type", item)
122
145
  "SORD_ERROR_#{yard.gsub(/[^0-9A-Za-z_]/i, '')}"
123
146
  end
data/lib/sord/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # typed: strong
2
2
  module Sord
3
- VERSION = "0.6.0"
3
+ VERSION = '0.7.0'
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sord
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Christiansen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-06-23 00:00:00.000000000 Z
11
+ date: 2019-06-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: yard
@@ -148,9 +148,9 @@ files:
148
148
  - ".github/ISSUE_TEMPLATE/feature-request.md"
149
149
  - ".gitignore"
150
150
  - ".rspec"
151
+ - CHANGELOG.md
151
152
  - CODE_OF_CONDUCT.md
152
153
  - Gemfile
153
- - Gemfile.lock
154
154
  - LICENSE.txt
155
155
  - README.md
156
156
  - Rakefile
data/Gemfile.lock DELETED
@@ -1,57 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- sord (0.4.1)
5
- colorize
6
- commander (~> 4.4)
7
- sorbet-runtime
8
- yard
9
-
10
- GEM
11
- remote: https://rubygems.org/
12
- specs:
13
- colorize (0.8.1)
14
- commander (4.4.7)
15
- highline (~> 2.0.0)
16
- diff-lcs (1.3)
17
- docile (1.3.2)
18
- highline (2.0.2)
19
- json (2.2.0)
20
- rake (10.5.0)
21
- rspec (3.8.0)
22
- rspec-core (~> 3.8.0)
23
- rspec-expectations (~> 3.8.0)
24
- rspec-mocks (~> 3.8.0)
25
- rspec-core (3.8.1)
26
- rspec-support (~> 3.8.0)
27
- rspec-expectations (3.8.4)
28
- diff-lcs (>= 1.2.0, < 2.0)
29
- rspec-support (~> 3.8.0)
30
- rspec-mocks (3.8.1)
31
- diff-lcs (>= 1.2.0, < 2.0)
32
- rspec-support (~> 3.8.0)
33
- rspec-support (3.8.2)
34
- simplecov (0.16.1)
35
- docile (~> 1.1)
36
- json (>= 1.8, < 3)
37
- simplecov-html (~> 0.10.0)
38
- simplecov-html (0.10.2)
39
- sorbet (0.4.4254)
40
- sorbet-static (= 0.4.4254)
41
- sorbet-runtime (0.4.4294)
42
- sorbet-static (0.4.4254-x86_64-linux)
43
- yard (0.9.19)
44
-
45
- PLATFORMS
46
- ruby
47
-
48
- DEPENDENCIES
49
- bundler (~> 2.0)
50
- rake (~> 10.0)
51
- rspec (~> 3.0)
52
- simplecov
53
- sorbet
54
- sord!
55
-
56
- BUNDLED WITH
57
- 2.0.1