steep 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -1
  3. data/bin/smoke_runner.rb +1 -1
  4. data/lib/steep.rb +6 -4
  5. data/lib/steep/ast/builtin.rb +96 -0
  6. data/lib/steep/ast/location.rb +9 -5
  7. data/lib/steep/ast/namespace.rb +80 -0
  8. data/lib/steep/ast/signature/env.rb +37 -31
  9. data/lib/steep/ast/types/boolean.rb +2 -2
  10. data/lib/steep/ast/types/hash.rb +50 -0
  11. data/lib/steep/ast/types/literal.rb +12 -10
  12. data/lib/steep/ast/types/name.rb +135 -94
  13. data/lib/steep/ast/types/nil.rb +3 -1
  14. data/lib/steep/ast/types/proc.rb +3 -1
  15. data/lib/steep/drivers/check.rb +4 -4
  16. data/lib/steep/drivers/utils/validator.rb +11 -16
  17. data/lib/steep/interface/builder.rb +201 -146
  18. data/lib/steep/interface/instantiated.rb +8 -0
  19. data/lib/steep/names.rb +86 -0
  20. data/lib/steep/parser.y +1093 -668
  21. data/lib/steep/source.rb +2 -2
  22. data/lib/steep/subtyping/check.rb +199 -63
  23. data/lib/steep/subtyping/constraints.rb +2 -5
  24. data/lib/steep/subtyping/variable_variance.rb +2 -2
  25. data/lib/steep/type_construction.rb +194 -175
  26. data/lib/steep/type_inference/block_params.rb +9 -21
  27. data/lib/steep/type_inference/constant_env.rb +26 -30
  28. data/lib/steep/type_inference/send_args.rb +4 -7
  29. data/lib/steep/type_inference/type_env.rb +3 -3
  30. data/lib/steep/version.rb +1 -1
  31. data/smoke/alias/a.rb +1 -1
  32. data/smoke/alias/b.rb +1 -1
  33. data/smoke/class/i.rbi +1 -1
  34. data/smoke/hash/a.rbi +8 -0
  35. data/smoke/hash/c.rb +18 -0
  36. data/smoke/hash/d.rb +6 -0
  37. data/smoke/hello/hello.rb +2 -2
  38. data/smoke/interface/a.rb +14 -0
  39. data/smoke/interface/a.rbi +12 -0
  40. data/smoke/module/a.rb +1 -1
  41. data/smoke/module/a.rbi +3 -3
  42. data/smoke/module/b.rb +1 -1
  43. data/smoke/stdout/a.rb +2 -2
  44. data/stdlib/builtin.rbi +6 -7
  45. data/steep.gemspec +1 -1
  46. metadata +14 -7
  47. data/lib/steep/module_name.rb +0 -116
  48. data/lib/steep/type_name.rb +0 -93
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 188a058370f3907cd216a30b481e78ff0c2573639baef3e052f192e9ec0c118d
4
- data.tar.gz: bacaac26779348dc7e1e4d36c3fa49d0842576429085d7ea496983fa684d21b3
3
+ metadata.gz: 562a6dfa36fc5da260d7dfc0cbc2a92f44000038aeb45447d79185cf700dfa9b
4
+ data.tar.gz: 48a656998959cce3a87cd6c59f4115b753336b33f54e2d961d656efd3e47b0c2
5
5
  SHA512:
6
- metadata.gz: a6ab5520ff7251a49da2673180c00b4d8be64b42e89b67153e1ca65f70dbf66c14f82384feff68a1a1ec19f8a20899802493f23e2e6f13e858b3962239416dd6
7
- data.tar.gz: 1a28b00375d3091600728241e218a305844fcb62ac52f3f99d190b8a01d3cda7f251a65d1efa10d2576073f84c53b9346615b9549cd7702b699a658c7d588bb0
6
+ metadata.gz: 6d22bbd0e6a251db6431a7f90a59b087ebf879d1664af7195aa889ade8d0c3a5191908740501bd97a5315dc041a8704946361f821f1c5d9e6ed79e2e07a80451
7
+ data.tar.gz: 3cd0b8bdd13aed0035f6a3f0363bb96f75ee67ed4d88fb0912bff7e828ebdcc0f2244a4eb3280c83c306b983270c97848b618b410c2002a887b1dc44aac6fe5b
data/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.6.0 (2018-09-23)
6
+
7
+ * Update ast_utils
8
+ * Introduce *hash* type `{ id: Integer, name: String }` (#54)
9
+ * Revise signature syntax; use `<` instead of `<:` for inheritance (#53)
10
+ * Interface and alias name can be namespaced (#52)
11
+ * Grammar formatting (#51 @iliabylich)
12
+
5
13
  ## 0.5.1 (2018-08-11)
6
14
 
7
15
  * Relax dependency requirements (#49, #50)
@@ -12,7 +20,7 @@
12
20
  * Introduce *incompatible* method (#45)
13
21
  * Add type alias (#44)
14
22
  * Steep is MIT license (#43)
15
- * Improved block parameter typing (#41)
23
+ * Improved block parameter typing (#41)
16
24
  * Support optional block
17
25
  * Support attributes in module
18
26
  * Support `:xstr` node
data/bin/smoke_runner.rb CHANGED
@@ -33,7 +33,7 @@ ARGV.each do |arg|
33
33
  if file.extname == ".rb"
34
34
  buffer = ::Parser::Source::Buffer.new(file.to_s)
35
35
  buffer.source = file.read
36
- parser = ::Parser::CurrentRuby.new
36
+ parser = ::Parser::Ruby25.new
37
37
 
38
38
  _, comments, _ = parser.tokenize(buffer)
39
39
  comments.each do |comment|
data/lib/steep.rb CHANGED
@@ -1,13 +1,15 @@
1
1
  require "steep/version"
2
2
 
3
3
  require "pathname"
4
+ require "parser/ruby25"
4
5
  require "ast_utils"
5
6
  require "active_support/core_ext/object/try"
6
7
  require "logger"
7
8
  require "active_support/tagged_logging"
8
9
  require "rainbow"
9
10
 
10
- require "steep/module_name"
11
+ require "steep/ast/namespace"
12
+ require "steep/names"
11
13
  require "steep/ast/location"
12
14
  require "steep/ast/types/helper"
13
15
  require "steep/ast/types/any"
@@ -26,6 +28,7 @@ require "steep/ast/types/literal"
26
28
  require "steep/ast/types/boolean"
27
29
  require "steep/ast/types/tuple"
28
30
  require "steep/ast/types/proc"
31
+ require "steep/ast/types/hash"
29
32
  require "steep/ast/method_type"
30
33
  require "steep/ast/type_params"
31
34
  require "steep/ast/signature/class"
@@ -40,8 +43,7 @@ require "steep/ast/signature/alias"
40
43
  require "steep/ast/annotation"
41
44
  require "steep/ast/annotation/collection"
42
45
  require "steep/ast/buffer"
43
-
44
- require "steep/type_name"
46
+ require "steep/ast/builtin"
45
47
 
46
48
  require "steep/interface/method_type"
47
49
  require "steep/interface/method"
@@ -87,7 +89,7 @@ module Steep
87
89
  unless @logger
88
90
  @logger = ActiveSupport::TaggedLogging.new(Logger.new(STDERR))
89
91
  @logger.push_tags "Steep #{VERSION}"
90
- @logger.level = Logger::ERROR
92
+ @logger.level = Logger::WARN
91
93
  end
92
94
 
93
95
  @logger
@@ -0,0 +1,96 @@
1
+ module Steep
2
+ module AST
3
+ module Builtin
4
+ class Type
5
+ attr_reader :module_name
6
+ attr_reader :arity
7
+
8
+ def initialize(module_name, arity: 0)
9
+ @module_name = Names::Module.parse(module_name)
10
+ @arity = arity
11
+ end
12
+
13
+ def instance_type(*args)
14
+ arity == args.size or raise "Mulformed instance type: name=#{module_name}, args=#{args}"
15
+ Types::Name::Instance.new(name: module_name, args: args)
16
+ end
17
+
18
+ def class_type(constructor: nil)
19
+ Types::Name::Class.new(name: module_name, constructor: constructor)
20
+ end
21
+
22
+ def module_type
23
+ Types::Name::Module.new(name: module_name)
24
+ end
25
+
26
+ def instance_type?(type, args: nil)
27
+ if type.is_a?(Types::Name::Instance)
28
+ if args
29
+ arity == args.size or raise "Mulformed instance type: name=#{module_name}, args=#{args}"
30
+ type.name == module_name && type.args == args
31
+ else
32
+ type.name == module_name && type.args.size == arity
33
+ end
34
+ else
35
+ false
36
+ end
37
+ end
38
+
39
+ NONE = ::Object.new
40
+
41
+ def class_type?(type, constructor: NONE)
42
+ if type.is_a?(Types::Name::Class)
43
+ unless constructor.equal?(NONE)
44
+ type.name == module_name && type.name.constructor == constructor
45
+ else
46
+ type.name == module_name
47
+ end
48
+ else
49
+ false
50
+ end
51
+ end
52
+
53
+ def module_type?(type)
54
+ if type.is_a?(Types::Name::Module)
55
+ type.name == module_name
56
+ else
57
+ false
58
+ end
59
+ end
60
+ end
61
+
62
+ Object = Type.new("::Object")
63
+ BasicObject = Type.new("::BasicObject")
64
+ Array = Type.new("::Array", arity: 1)
65
+ Range = Type.new("::Range", arity: 1)
66
+ Hash = Type.new("::Hash", arity: 2)
67
+ Module = Type.new("::Module")
68
+ Class = Type.new("::Class")
69
+ Integer = Type.new("::Integer")
70
+ Float = Type.new("::Float")
71
+ String = Type.new("::String")
72
+ Symbol = Type.new("::Symbol")
73
+ TrueClass = Type.new("::TrueClass")
74
+ FalseClass = Type.new("::FalseClass")
75
+ Regexp = Type.new("::Regexp")
76
+ NilClass = Type.new("::NilClass")
77
+ Proc = Type.new("::Proc")
78
+
79
+ def self.nil_type
80
+ AST::Types::Nil.new
81
+ end
82
+
83
+ def self.any_type
84
+ AST::Types::Any.new
85
+ end
86
+
87
+ def self.bool_type
88
+ AST::Types::Boolean.new
89
+ end
90
+
91
+ def self.optional(type)
92
+ AST::Types::Union.build(types: [type, nil_type])
93
+ end
94
+ end
95
+ end
96
+ end
@@ -59,11 +59,15 @@ module Steep
59
59
  end
60
60
 
61
61
  def +(other)
62
- raise "Invalid concat: buffer=#{buffer.name}, other.buffer=#{other.buffer.name}" unless other.buffer == buffer
63
-
64
- self.class.new(buffer: buffer,
65
- start_pos: start_pos,
66
- end_pos: other.end_pos)
62
+ if other
63
+ raise "Invalid concat: buffer=#{buffer.name}, other.buffer=#{other.buffer.name}" unless other.buffer == buffer
64
+
65
+ self.class.new(buffer: buffer,
66
+ start_pos: start_pos,
67
+ end_pos: other.end_pos)
68
+ else
69
+ self
70
+ end
67
71
  end
68
72
 
69
73
  def self.concat(*locations)
@@ -0,0 +1,80 @@
1
+ module Steep
2
+ module AST
3
+ class Namespace
4
+ attr_reader :path
5
+
6
+ def initialize(path:, absolute:)
7
+ @path = path
8
+ @absolute = absolute
9
+ end
10
+
11
+ def self.empty
12
+ new(path: [], absolute: false)
13
+ end
14
+
15
+ def self.root
16
+ new(path: [], absolute: true)
17
+ end
18
+
19
+ def +(other)
20
+ if other.absolute?
21
+ other
22
+ else
23
+ self.class.new(path: path + other.path, absolute: absolute?)
24
+ end
25
+ end
26
+
27
+ def append(component)
28
+ self.class.new(path: path + [component], absolute: absolute?)
29
+ end
30
+
31
+ def parent
32
+ raise "Parent with empty namespace" if empty?
33
+ self.class.new(path: path.take(path.size - 1), absolute: absolute?)
34
+ end
35
+
36
+ def absolute?
37
+ @absolute
38
+ end
39
+
40
+ def relative?
41
+ !absolute?
42
+ end
43
+
44
+ def absolute!
45
+ self.class.new(path: path, absolute: true)
46
+ end
47
+
48
+ def empty?
49
+ path.empty?
50
+ end
51
+
52
+ def ==(other)
53
+ other.is_a?(Namespace) && other.path == path && other.absolute? == absolute?
54
+ end
55
+
56
+ alias eql? ==
57
+
58
+ def hash
59
+ self.class.hath ^ path.hash ^ absolute?.hash
60
+ end
61
+
62
+ def to_s
63
+ if empty?
64
+ absolute? ? "::" : ""
65
+ else
66
+ s = path.join("::")
67
+ absolute? ? "::#{s}::" : "#{s}::"
68
+ end
69
+ end
70
+
71
+ def self.parse(string)
72
+ if string.start_with?("::")
73
+ new(path: string.split("::").drop(1).map(&:to_sym), absolute: true)
74
+ else
75
+ new(path: string.split("::").map(&:to_sym), absolute: false)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -20,15 +20,22 @@ module Steep
20
20
  @aliases = {}
21
21
  end
22
22
 
23
+ def assert_absolute_name(name)
24
+ name.namespace.absolute? or raise "Absolute name expected: #{name}"
25
+ end
26
+
23
27
  def add(sig)
24
28
  case sig
25
29
  when Signature::Class
26
- raise "Duplicated class: #{sig.name}" if classes.key?(sig.name.absolute!) || modules.key?(sig.name.absolute!)
27
- classes[sig.name.absolute!] = sig
30
+ assert_absolute_name sig.name
31
+ raise "Duplicated class: #{sig.name}" if classes.key?(sig.name) || modules.key?(sig.name)
32
+ classes[sig.name] = sig
28
33
  when Signature::Module
29
- raise "Duplicated module: #{sig.name}" if classes.key?(sig.name.absolute!) || modules.key?(sig.name.absolute!)
34
+ assert_absolute_name sig.name
35
+ raise "Duplicated module: #{sig.name}" if classes.key?(sig.name) || modules.key?(sig.name)
30
36
  modules[sig.name.absolute!] = sig
31
37
  when Signature::Interface
38
+ assert_absolute_name sig.name
32
39
  raise "Duplicated interface: #{sig.name}" if interfaces.key?(sig.name)
33
40
  interfaces[sig.name] = sig
34
41
  when Signature::Extension
@@ -38,11 +45,13 @@ module Steep
38
45
  end
39
46
  extensions[sig.module_name.absolute!] << sig
40
47
  when Signature::Const
41
- constants[sig.name.absolute!] = sig
48
+ assert_absolute_name sig.name
49
+ constants[sig.name] = sig
42
50
  when Signature::Gvar
43
51
  raise "Duplicated global: #{sig.name}" if globals.key?(sig.name)
44
52
  globals[sig.name] = sig
45
53
  when Signature::Alias
54
+ assert_absolute_name sig.name
46
55
  raise "Duplicated alias: #{sig.name}" if aliases.key?(sig.name)
47
56
  aliases[sig.name] = sig
48
57
  else
@@ -50,15 +59,15 @@ module Steep
50
59
  end
51
60
  end
52
61
 
53
- def find_module(name, current_module: nil)
62
+ def find_module(name, current_module: AST::Namespace.root)
54
63
  find_name(modules, name, current_module: current_module) or raise "Unknown module: #{name}"
55
64
  end
56
65
 
57
- def find_class(name, current_module: nil)
66
+ def find_class(name, current_module: AST::Namespace.root)
58
67
  find_name(classes, name, current_module: current_module) or raise "Unknown class: #{name}"
59
68
  end
60
69
 
61
- def find_class_or_module(name, current_module: nil)
70
+ def find_class_or_module(name, current_module: AST::Namespace.root)
62
71
  sig =
63
72
  find_name(modules, name, current_module: current_module) ||
64
73
  find_name(classes, name, current_module: current_module)
@@ -66,11 +75,11 @@ module Steep
66
75
  sig or raise "Unknown class/module: #{name}}"
67
76
  end
68
77
 
69
- def find_extensions(name, current_module: nil)
78
+ def find_extensions(name, current_module: AST::Namespace.root)
70
79
  find_name(extensions, name, current_module: current_module) || []
71
80
  end
72
81
 
73
- def find_const(name, current_module: nil)
82
+ def find_const(name, current_module: Namespace.root)
74
83
  find_name(constants, name, current_module: current_module)
75
84
  end
76
85
 
@@ -78,42 +87,39 @@ module Steep
78
87
  globals[name]
79
88
  end
80
89
 
81
- def find_alias(name)
82
- aliases[name]
90
+ def find_alias(name, namespace:)
91
+ find_name(aliases, name, current_module: namespace) or raise "Unknown alias: #{name}"
83
92
  end
84
93
 
85
94
  def find_name(hash, name, current_module:)
86
- if current_module
87
- hash[current_module + name] || find_name(hash, name, current_module: current_module.parent)
95
+ current_module.absolute? or raise "Current namespace should be absolute: #{current_module}"
96
+
97
+ if (object = hash[name.in_namespace(current_module)])
98
+ object
88
99
  else
89
- hash[name.absolute!]
100
+ unless current_module.empty?
101
+ find_name(hash, name, current_module: current_module.parent)
102
+ end
90
103
  end
91
104
  end
92
105
 
93
- def find_interface(name)
94
- interfaces[name] or raise "Unknown interface: #{name}"
95
- end
96
-
97
- def module?(type_name, current_module: nil)
98
- name = type_name.map_module_name {|m| current_module ? current_module + m : m.absolute! }.name
99
- modules.key?(name)
106
+ def find_interface(name, namespace: Namespace.root)
107
+ find_name(interfaces, name, current_module: namespace) or raise "Unknown interface: #{name}"
100
108
  end
101
109
 
102
- def class?(type_name, current_module: nil)
103
- name = type_name.map_module_name {|m| current_module ? current_name + m : m.absolute! }.name
110
+ def class_name?(name)
111
+ assert_absolute_name name
104
112
  classes.key?(name)
105
113
  end
106
114
 
107
- def class_name?(name, current_module: nil)
108
- classes.key?(current_module ? current_module + name : name.absolute!)
109
- end
110
-
111
- def module_name?(name, current_module: nil)
112
- modules.key?(current_module ? current_module + name : name.absolute!)
115
+ def module_name?(name)
116
+ assert_absolute_name name
117
+ modules.key?(name)
113
118
  end
114
119
 
115
- def const_name?(name, current_module: nil)
116
- constants.key?(current_module ? current_module + name : name.absolute!)
120
+ def const_name?(name)
121
+ assert_absolute_name name
122
+ constants.key?(name)
117
123
  end
118
124
 
119
125
  def each(&block)
@@ -41,8 +41,8 @@ module Steep
41
41
  def back_type
42
42
  Union.build(types:
43
43
  [
44
- Name.new_instance(name: "::TrueClass", location: location),
45
- Name.new_instance(name: "::FalseClass", location: location)
44
+ Builtin::TrueClass.instance_type,
45
+ Builtin::FalseClass.instance_type
46
46
  ],
47
47
  location: location)
48
48
  end
@@ -0,0 +1,50 @@
1
+ module Steep
2
+ module AST
3
+ module Types
4
+ class Hash
5
+ attr_reader :location
6
+ attr_reader :elements
7
+
8
+ def initialize(elements:, location: nil)
9
+ @elements = elements
10
+ @location = location
11
+ end
12
+
13
+ def ==(other)
14
+ other.is_a?(Hash) && other.elements == elements
15
+ end
16
+
17
+ def hash
18
+ self.class.hash ^ elements.hash
19
+ end
20
+
21
+ alias eql? ==
22
+
23
+ def subst(s)
24
+ self.class.new(location: location,
25
+ elements: elements.transform_values {|type| type.subst(s) })
26
+ end
27
+
28
+ def to_s
29
+ "{ #{elements.map {|key, value| "#{key.inspect} => #{value}" }.join(", ")} }"
30
+ end
31
+
32
+ def free_variables
33
+ elements.each_value.with_object(Set.new) do |type, set|
34
+ set.merge(type.free_variables)
35
+ end
36
+ end
37
+
38
+ include Helper::ChildrenLevel
39
+
40
+ def level
41
+ [0] + level_of_children(elements.values)
42
+ end
43
+
44
+ def with_location(new_location)
45
+ self.class.new(elements: elements, location: new_location)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end