steep 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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