diff --git a/Steepfile b/Steepfile index eb09449d8..10c321185 100644 --- a/Steepfile +++ b/Steepfile @@ -8,8 +8,7 @@ target :lib do "lib/rbs/test.rb" ) - library "pathname", "json", "logger", "monitor", "tsort", "uri", 'dbm', 'pstore', 'singleton', 'shellwords', 'fileutils', 'find', 'digest', 'abbrev', 'prettyprint' - signature 'stdlib/yaml/0' + library "pathname", "json", "logger", "monitor", "tsort", "uri", 'dbm', 'pstore', 'singleton', 'shellwords', 'fileutils', 'find', 'digest', 'abbrev', 'prettyprint', 'yaml' signature "stdlib/strscan/0/" signature "stdlib/optparse/0/" signature "stdlib/rdoc/0/" diff --git a/lib/rbs/diff.rb b/lib/rbs/diff.rb index deb46ac61..47a4d6d84 100644 --- a/lib/rbs/diff.rb +++ b/lib/rbs/diff.rb @@ -53,13 +53,13 @@ def build_methods(path) builder.build_instance(@type_name).methods rescue => e RBS.logger.warn("#{path}: (#{e.class}) #{e.message}") - {} + {} #: Hash[Symbol, Definition::Method] end singleton_methods = begin builder.build_singleton(@type_name).methods rescue => e RBS.logger.warn("#{path}: (#{e.class}) #{e.message}") - {} + {} #: Hash[Symbol, Definition::Method] end constant_children = begin @@ -67,7 +67,7 @@ def build_methods(path) constant_resolver.children(@type_name) rescue => e RBS.logger.warn("#{path}: (#{e.class}) #{e.message}") - {} + {} #: Hash[Symbol, Constant] end [ instance_methods, singleton_methods, constant_children ] @@ -101,7 +101,8 @@ def definition_method_to_s(key, kind, definition_method) prefix = kind == :instance ? "" : "self." if definition_method.alias_of - "alias #{prefix}#{key} #{prefix}#{definition_method.alias_of.defs.first.member.name}" + first_def = definition_method.alias_of.defs.first #: Definition::Method::TypeDef + "alias #{prefix}#{key} #{prefix}#{first_def.member.name}" else "def #{prefix}#{key}: #{definition_method.method_types.join(" | ")}" end diff --git a/lib/rbs/prototype/runtime/value_object_generator.rb b/lib/rbs/prototype/runtime/value_object_generator.rb index 975f772a7..8d89f0ebd 100644 --- a/lib/rbs/prototype/runtime/value_object_generator.rb +++ b/lib/rbs/prototype/runtime/value_object_generator.rb @@ -33,7 +33,9 @@ def build_decl # def self.members: () -> [ :foo, :bar ] # def members: () -> [ :foo, :bar ] def build_s_members - [:singleton, :instance].map do |kind| + ( + [:singleton, :instance] #: Array[AST::Members::MethodDefinition::kind] + ).map do |kind| AST::Members::MethodDefinition.new( name: :members, overloads: [ diff --git a/sig/diff.rbs b/sig/diff.rbs index 4f3b99269..ee11b205a 100644 --- a/sig/diff.rbs +++ b/sig/diff.rbs @@ -15,9 +15,12 @@ module RBS def each_diff: () { (String before, String after) -> void } -> void private def each_diff_by: (Symbol kind, Hash[::Symbol, Definition::Method] before_methods, Hash[::Symbol, Definition::Method] after_methods) { (String before, String after) -> void } -> void - private def build_methods: (Array[String] path) -> [ Hash[::Symbol, Definition::Method], Hash[::Symbol, Definition::Method] ] + private def build_methods: (Array[String] path) -> [ Hash[::Symbol, Definition::Method], Hash[::Symbol, Definition::Method] , Hash[Symbol, Constant]] private def build_env: (Array[String] path) -> Environment private def build_builder: (Environment env) -> DefinitionBuilder private def definition_method_to_s: (Symbol key, Symbol kind, Definition::Method definition_method) -> String? + private def each_diff_methods: (Symbol kind, Hash[Symbol, Definition::Method], Hash[Symbol, Definition::Method]) { (String before, String after) -> void } -> void + private def each_diff_constants: (Hash[Symbol, Constant] before_constants, Hash[Symbol, Constant] after_constants) { (String before, String after) -> void } -> void + private def constant_to_s: (Constant?) -> String end end diff --git a/sig/prototype/runtime.rbs b/sig/prototype/runtime.rbs index 5866ea4be..0221518be 100644 --- a/sig/prototype/runtime.rbs +++ b/sig/prototype/runtime.rbs @@ -2,12 +2,17 @@ module RBS module Prototype class Runtime module Reflection + self.@object_class: UnboundMethod def self.object_class: (Module value) -> Class + self.@constants_of: UnboundMethod def self.constants_of: (Module mod, ?bool inherit) -> Array[Symbol] end module Helpers + @module_name_method: UnboundMethod + @untyped: Types::Bases::Any + private def const_name: (Module const) -> String? @@ -19,12 +24,15 @@ module RBS def to_type_name: (String name, ?full_name: bool) -> TypeName - def untyped: () -> untyped + def untyped: () -> Types::Bases::Any end class ValueObjectBase include Helpers + # @target_class stores the singleton object of `Data` or `Struct` subclass + @target_class: untyped + def build_decl: () -> AST::Declarations::Class private @@ -33,6 +41,10 @@ module RBS def build_s_members: () -> Array[AST::Members::MethodDefinition] + def build_super_class: () -> AST::Declarations::Class::Super + + def add_decl_members: (AST::Declarations::Class) -> void + def initialize: (Class target_class) -> void end @@ -77,9 +89,9 @@ module RBS def skip_mixin?: (type_name: TypeName, module_name: TypeName, mixin_class: mixin_class) -> bool - def skip_singleton_method?: (module_name: TypeName, name: Symbol) -> bool + def skip_singleton_method?: (module_name: TypeName, method: UnboundMethod, accessibility: Definition::accessibility) -> bool - def skip_instance_method?: (module_name: TypeName, name: Symbol) -> bool + def skip_instance_method?: (module_name: TypeName, method: UnboundMethod, accessibility: Definition::accessibility) -> bool def skip_constant?: (module_name: String, name: Symbol) -> bool @@ -147,6 +159,8 @@ module RBS def generate_module: (Module mod) -> void + def generate_mixin: (Module mod, AST::Declarations::Class | AST::Declarations::Module, TypeName, TypeName) -> void + # Generate/find outer module declarations # This is broken down into another method to comply with `DRY` # This generates/finds declarations in nested form & returns the last array of declarations @@ -158,6 +172,8 @@ module RBS def block_from_ast_of: (UnboundMethod method) -> Types::Block? + def block_from_body: (RubyVM::AbstractSyntaxTree::Node) -> Types::Block? + def can_alias?: (Module, UnboundMethod) -> bool def type_params: (Module) -> Array[AST::TypeParam] diff --git a/steep/Gemfile b/steep/Gemfile index d09c409ca..0956766fd 100644 --- a/steep/Gemfile +++ b/steep/Gemfile @@ -1,4 +1,4 @@ source "https://rubygems.org" -gem "rbs", "~> 3.2.2" -gem "steep", "~> 1.6.0" +gem "rbs" +gem "steep" diff --git a/steep/Gemfile.lock b/steep/Gemfile.lock index 9dca27ab5..fd4ecdc65 100644 --- a/steep/Gemfile.lock +++ b/steep/Gemfile.lock @@ -1,6 +1,7 @@ GEM remote: https://rubygems.org/ specs: + abbrev (0.1.1) activesupport (7.1.1) base64 bigdecimal @@ -39,7 +40,8 @@ GEM rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) - rbs (3.2.2) + rbs (3.3.0) + abbrev ruby2_keywords (0.0.5) securerandom (0.3.0) steep (1.6.0) @@ -68,8 +70,8 @@ PLATFORMS ruby DEPENDENCIES - rbs (~> 3.2.2) - steep (~> 1.6.0) + rbs + steep BUNDLED WITH 2.4.18 diff --git a/test/rbs/cli_test.rb b/test/rbs/cli_test.rb index b35ff3b08..cde84b345 100644 --- a/test/rbs/cli_test.rb +++ b/test/rbs/cli_test.rb @@ -15,7 +15,17 @@ def stderr @stderr ||= StringIO.new end - def run_rbs(*commands, bundler: true) + # Run `rbs collection` with fresh bundler environment + # + # You need this method to test `rbs collection` features. + # `rbs collection` loads gems from Bundler context, so re-using the currenr Bundler context (used to develop rbs gem) causes issues. + # + # - If `bundler: true` is given, it runs `rbs collection` command with `bundle exec` + # - If `bundler: false` is given, it runs `rbs collection` command without `bundle exec` + # + # We cannot run tests that uses this method in ruby CI. + # + def run_rbs_collection(*commands, bundler:) stdout, stderr, status = Bundler.with_unbundled_env do bundle_exec = [] @@ -26,7 +36,7 @@ def run_rbs(*commands, bundler: true) rbs_path << (":" + rblib) end - Open3.capture3({ "RUBYLIB" => rbs_path }, *bundle_exec, "#{__dir__}/../../exe/rbs", *commands, chdir: Dir.pwd) + Open3.capture3({ "RUBYLIB" => rbs_path }, *bundle_exec, "#{__dir__}/../../exe/rbs", "collection", *commands, chdir: Dir.pwd) end if block_given? @@ -849,7 +859,7 @@ def test_collection_install 2.2.0 LOCK - _stdout, _stderr = run_rbs("collection", "install") + _stdout, _stderr = run_rbs_collection("install", bundler: true) rbs_collection_lock = dir.join('rbs_collection.lock.yaml') assert rbs_collection_lock.exist? @@ -861,7 +871,7 @@ def test_collection_install Dir.mkdir("child") Dir.chdir("child") do - _stdout, _stderr = run_rbs("collection", "install") + _stdout, _stderr = run_rbs_collection("install", bundler: true) assert rbs_collection_lock.exist? assert collection_dir.exist? end @@ -891,7 +901,7 @@ def test_collection_install_frozen YAML dir.join('rbs_collection.lock.yaml').write(lock_content) - run_rbs("collection", "install", "--frozen", bundler: false) + run_rbs_collection("install", "--frozen", bundler: false) refute dir.join(RBS::Collection::Config::PATH).exist? assert dir.join('gem_rbs_collection/ast').exist? @@ -933,7 +943,7 @@ def test_collection_update 2.2.0 LOCK - run_rbs("collection", "update", bundler: true) + run_rbs_collection("update", bundler: true) assert dir.join('rbs_collection.lock.yaml').exist? assert dir.join('gem_rbs_collection/ast').exist? @@ -997,7 +1007,7 @@ def test_collection_install_gemspec RUBY (dir/"sig").mkdir - stdout, _ = run_rbs("collection", "install", bundler: true) + stdout, _ = run_rbs_collection("install", bundler: true) assert_match(/Installing ast:(\d(\.\d)*)/, stdout) refute_match(/^Using hola:(\d(\.\d)*)/, stdout) @@ -1027,15 +1037,17 @@ def x: () -> untyped end RBS - stdout, stderr = run_rbs('subtract', minuend.to_s, subtrahend.to_s) - assert_empty stderr - assert_equal <<~RBS, stdout - use A::B + with_cli do |cli| + cli.run(['subtract', minuend.to_s, subtrahend.to_s]) + assert_empty stderr.string + assert_equal <<~RBS, stdout.string + use A::B - class C - def y: () -> untyped - end - RBS + class C + def y: () -> untyped + end + RBS + end end end @@ -1065,15 +1077,17 @@ def y: () -> untyped end RBS - stdout, stderr = run_rbs('subtract', minuend.to_s, '--subtrahend', subtrahend_1.to_s, '--subtrahend', subtrahend_2.to_s) - assert_empty stderr - assert_equal <<~RBS, stdout - use A::B + with_cli do |cli| + cli.run(['subtract', minuend.to_s, '--subtrahend', subtrahend_1.to_s, '--subtrahend', subtrahend_2.to_s]) + assert_empty stderr.string + assert_equal <<~RBS, stdout.string + use A::B - class C - def z: () -> untyped - end - RBS + class C + def z: () -> untyped + end + RBS + end end end @@ -1096,16 +1110,18 @@ def x: () -> untyped end RBS - stdout, stderr = run_rbs('subtract', '--write', minuend.to_s, subtrahend.to_s) - assert_empty stderr - assert_empty stdout - assert_equal minuend.read, <<~RBS - use A::B - - class C - def y: () -> untyped - end - RBS + with_cli do |cli| + cli.run(['subtract', '--write', minuend.to_s, subtrahend.to_s]) + assert_empty stderr.string + assert_empty stdout.string + assert_equal minuend.read, <<~RBS + use A::B + + class C + def y: () -> untyped + end + RBS + end end end @@ -1127,10 +1143,12 @@ def x: () -> untyped end RBS - stdout, stderr = run_rbs('subtract', '--write', minuend.to_s, subtrahend.to_s) - assert_empty stderr - assert_empty stdout - assert_equal minuend.exist?, false + with_cli do |cli| + cli.run(['subtract', '--write', minuend.to_s, subtrahend.to_s]) + assert_empty stderr.string + assert_empty stdout.string + refute_predicate minuend, :exist? + end end end @@ -1181,36 +1199,40 @@ class Foo def test_diff_markdown mktmp_diff_case do |dir1, dir2| - stdout, stderr = run_rbs('diff', '--format', 'markdown', '--type-name', 'Foo', '--before', dir1.to_s, '--after', dir2.to_s) - - assert_equal <<~MARKDOWN, stdout - | before | after | - | --- | --- | - | `def qux: (untyped) -> untyped` | `-` | - | `def quux: () -> void` | `alias quux bar` | - | `def self.baz: () -> (::Integer \\| ::String)` | `def self.baz: (::Integer) -> ::Integer?` | - | `CONST: ::Array[::Integer]` | `CONST: ::Array[::String]` | - MARKDOWN + with_cli do |cli| + cli.run(['diff', '--format', 'markdown', '--type-name', 'Foo', '--before', dir1.to_s, '--after', dir2.to_s]) + + assert_equal <<~MARKDOWN, stdout.string + | before | after | + | --- | --- | + | `def qux: (untyped) -> untyped` | `-` | + | `def quux: () -> void` | `alias quux bar` | + | `def self.baz: () -> (::Integer \\| ::String)` | `def self.baz: (::Integer) -> ::Integer?` | + | `CONST: ::Array[::Integer]` | `CONST: ::Array[::String]` | + MARKDOWN + end end end def test_diff_diff mktmp_diff_case do |dir1, dir2| - stdout, stderr = run_rbs('diff', '--format', 'diff', '--type-name', 'Foo', '--before', dir1.to_s, '--after', dir2.to_s) + with_cli do |cli| + cli.run(['diff', '--format', 'diff', '--type-name', 'Foo', '--before', dir1.to_s, '--after', dir2.to_s]) - assert_equal <<~DIFF, stdout - - def qux: (untyped) -> untyped - + - + assert_equal <<~DIFF, stdout.string + - def qux: (untyped) -> untyped + + - - - def quux: () -> void - + alias quux bar + - def quux: () -> void + + alias quux bar - - def self.baz: () -> (::Integer | ::String) - + def self.baz: (::Integer) -> ::Integer? + - def self.baz: () -> (::Integer | ::String) + + def self.baz: (::Integer) -> ::Integer? - - CONST: ::Array[::Integer] - + CONST: ::Array[::String] - DIFF + - CONST: ::Array[::Integer] + + CONST: ::Array[::String] + DIFF + end end end end