diff --git a/lib/tapioca/commands/dsl.rb b/lib/tapioca/commands/dsl.rb index e0bafa813..ce6f515ef 100644 --- a/lib/tapioca/commands/dsl.rb +++ b/lib/tapioca/commands/dsl.rb @@ -115,8 +115,13 @@ def execute if @auto_strictness say("") - update_gem_rbis_strictnesses([], gem_dir: @gem_dir, dsl_dir: @outpath.to_s) - say("") + validate_rbi_files( + command: default_command(:dsl, @requested_constants.join(" ")), + gem_dir: @gem_dir, + dsl_dir: @outpath.to_s, + auto_strictness: @auto_strictness, + compilers: pipeline.compilers + ) end say("All operations performed in working directory.", [:green, :bold]) diff --git a/lib/tapioca/commands/gem.rb b/lib/tapioca/commands/gem.rb index 66f6e5837..260f9ff15 100644 --- a/lib/tapioca/commands/gem.rb +++ b/lib/tapioca/commands/gem.rb @@ -79,8 +79,13 @@ def execute end if anything_done - gem_names = gem_queue.map(&:name) - update_gem_rbis_strictnesses(gem_names, gem_dir: @outpath.to_s, dsl_dir: @dsl_dir) if @auto_strictness + validate_rbi_files( + command: default_command(:gem, @gem_names.join(" ")), + gem_dir: @outpath.to_s, + dsl_dir: @dsl_dir, + auto_strictness: @auto_strictness, + gems: bundle.dependencies + ) say("All operations performed in working directory.", [:green, :bold]) say("Please review changes and commit them.", [:green, :bold]) @@ -104,7 +109,13 @@ def sync(should_verify: false) ].any? if anything_done - update_gem_rbis_strictnesses([], gem_dir: @outpath.to_s, dsl_dir: @dsl_dir) if @auto_strictness + validate_rbi_files( + command: default_command(:gem), + gem_dir: @outpath.to_s, + dsl_dir: @dsl_dir, + auto_strictness: @auto_strictness, + gems: bundle.dependencies + ) say("All operations performed in working directory.", [:green, :bold]) say("Please review changes and commit them.", [:green, :bold]) diff --git a/lib/tapioca/helpers/rbi_helper.rb b/lib/tapioca/helpers/rbi_helper.rb index 78d0a8025..834ef316c 100644 --- a/lib/tapioca/helpers/rbi_helper.rb +++ b/lib/tapioca/helpers/rbi_helper.rb @@ -9,17 +9,24 @@ module RBIHelper requires_ancestor { Thor::Shell } requires_ancestor { SorbetHelper } - sig { params(gem_names: T::Array[String], gem_dir: String, dsl_dir: String).void } - def update_gem_rbis_strictnesses(gem_names, gem_dir: DEFAULT_GEM_DIR, dsl_dir: DEFAULT_DSL_DIR) - return unless File.directory?(dsl_dir) - + sig do + params( + command: String, + gem_dir: String, + dsl_dir: String, + auto_strictness: T::Boolean, + gems: T::Array[Gemfile::GemSpec], + compilers: T::Enumerable[Class] + ).void + end + def validate_rbi_files(command:, gem_dir:, dsl_dir:, auto_strictness:, gems: [], compilers: []) error_url_base = Spoom::Sorbet::Errors::DEFAULT_ERROR_URL_BASE - say("Typechecking RBI files... ") + say("Checking generated RBI files... ") res = sorbet( "--no-config", "--error-url-base=#{error_url_base}", - "--isolate-error-code 4010", + "--stop-after namer", dsl_dir, gem_dir ) @@ -28,10 +35,62 @@ def update_gem_rbis_strictnesses(gem_names, gem_dir: DEFAULT_GEM_DIR, dsl_dir: D errors = Spoom::Sorbet::Errors::Parser.parse_string(res.err) if errors.empty? - say("No error found", [:green, :bold]) + say(" No errors found\n\n", [:green, :bold]) return end + parse_errors = errors.select { |error| error.code < 4000 } + + if parse_errors.any? + say_error(<<~ERR, :red) + + ##### INTERNAL ERROR ##### + + There are parse errors in the generated RBI files. + + This seems related to a bug in Tapioca. + Please open an issue at https://github.com/Shopify/tapioca/issues/new with the following information: + + Tapioca v#{Tapioca::VERSION} + + Command: + #{command} + + ERR + + say_error(<<~ERR, :red) if gems.any? + Gems: + #{gems.map { |gem| " #{gem.name} (#{gem.version})" }.join("\n")} + + ERR + + say_error(<<~ERR, :red) if compilers.any? + Compilers: + #{compilers.map { |compiler| " #{compiler.name}" }.join("\n")} + + ERR + + say_error(<<~ERR, :red) + Errors: + #{parse_errors.map { |error| " #{error}" }.join("\n")} + + ########################## + + ERR + end + + if auto_strictness + redef_errors = errors.select { |error| error.code == 4010 } + update_gem_rbis_strictnesses(redef_errors, gem_dir) + end + + Kernel.exit(1) if parse_errors.any? + end + + private + + sig { params(errors: T::Array[Spoom::Sorbet::Errors::Error], gem_dir: String).void } + def update_gem_rbis_strictnesses(errors, gem_dir) files = [] errors.each do |error| @@ -48,10 +107,8 @@ def update_gem_rbis_strictnesses(gem_names, gem_dir: DEFAULT_GEM_DIR, dsl_dir: D files .uniq .sort - .select do |file| - name = gem_name_from_rbi_path(file) - file.start_with?(gem_dir) && (gem_names.empty? || gem_names.include?(name)) - end.each do |file| + .select { |file| file.start_with?(gem_dir) } + .each do |file| Spoom::Sorbet::Sigils.change_sigil_in_file(file, "false") say("\n Changed strictness of #{file} to `typed: false` (conflicting with DSL files)", [:yellow, :bold]) end diff --git a/spec/tapioca/cli/dsl_spec.rb b/spec/tapioca/cli/dsl_spec.rb index d462b96fa..966237282 100644 --- a/spec/tapioca/cli/dsl_spec.rb +++ b/spec/tapioca/cli/dsl_spec.rb @@ -94,8 +94,8 @@ class Post Done - Typechecking RBI files... Done - No error found + Checking generated RBI files... Done + No errors found All operations performed in working directory. Please review changes and commit them. @@ -207,8 +207,8 @@ class Comment Done - Typechecking RBI files... Done - No error found + Checking generated RBI files... Done + No errors found All operations performed in working directory. Please review changes and commit them. @@ -290,8 +290,8 @@ class Role Done - Typechecking RBI files... Done - No error found + Checking generated RBI files... Done + No errors found All operations performed in working directory. Please review changes and commit them. @@ -357,8 +357,8 @@ class Comment Done - Typechecking RBI files... Done - No error found + Checking generated RBI files... Done + No errors found All operations performed in working directory. Please review changes and commit them. @@ -409,8 +409,8 @@ class Comment Done - Typechecking RBI files... Done - No error found + Checking generated RBI files... Done + No errors found All operations performed in working directory. Please review changes and commit them. @@ -444,8 +444,8 @@ class Post Done - Typechecking RBI files... Done - No error found + Checking generated RBI files... Done + No errors found All operations performed in working directory. Please review changes and commit them. @@ -517,8 +517,8 @@ class Post Done - Typechecking RBI files... Done - No error found + Checking generated RBI files... Done + No errors found All operations performed in working directory. Please review changes and commit them. @@ -632,8 +632,8 @@ class User; end Done - Typechecking RBI files... Done - No error found + Checking generated RBI files... Done + No errors found All operations performed in working directory. Please review changes and commit them. @@ -714,8 +714,8 @@ def self.gather_constants Done - Typechecking RBI files... Done - No error found + Checking generated RBI files... Done + No errors found All operations performed in working directory. Please review changes and commit them. @@ -806,8 +806,8 @@ def self.gather_constants Done - Typechecking RBI files... Done - No error found + Checking generated RBI files... Done + No errors found All operations performed in working directory. Please review changes and commit them. @@ -913,8 +913,8 @@ def self.gather_constants Done - Typechecking RBI files... Done - No error found + Checking generated RBI files... Done + No errors found All operations performed in working directory. Please review changes and commit them. @@ -974,8 +974,8 @@ class Post Done - Typechecking RBI files... Done - No error found + Checking generated RBI files... Done + No errors found All operations performed in working directory. Please review changes and commit them. @@ -1094,8 +1094,8 @@ class Image Done - Typechecking RBI files... Done - No error found + Checking generated RBI files... Done + No errors found All operations performed in working directory. Please review changes and commit them. @@ -1329,7 +1329,7 @@ class Post result = @project.tapioca("dsl Post") assert_includes(result.out, <<~OUT) - Typechecking RBI files... Done + Checking generated RBI files... Done Changed strictness of sorbet/rbi/gems/bar@1.0.0.rbi to `typed: false` (conflicting with DSL files) OUT @@ -1404,6 +1404,67 @@ module GeneratedBar; end assert_success_status(result) end end + + describe "sanity" do + before(:all) do + @project.require_real_gem("smart_properties", "1.15.0") + @project.bundle_install + @project.tapioca("init") + + @project.write("lib/post.rb", <<~RB) + require "smart_properties" + + class Post + include SmartProperties + property :title, accepts: String + end + RB + end + + after do + project.remove("sorbet/rbi/gems") + project.remove("sorbet/rbi/dsl") + end + + it "must display an error message when a generated gem RBI file contains a parse error" do + @project.write("sorbet/rbi/dsl/bar.rbi", <<~RBI) + # typed: true + + module Bar + # This method is missing a `)` + sig { params(block: T.proc.params(x: T.any(String, Integer).void).void } + def bar(&block); end + end + RBI + + result = @project.tapioca("dsl Post") + + assert_includes(result.err, <<~ERR) + ##### INTERNAL ERROR ##### + + There are parse errors in the generated RBI files. + + This seems related to a bug in Tapioca. + Please open an issue at https://github.com/Shopify/tapioca/issues/new with the following information: + + Tapioca v#{Tapioca::VERSION} + + Command: + bin/tapioca dsl Post + + Compilers: + Tapioca::Dsl::Compilers::SmartProperties + + Errors: + sorbet/rbi/dsl/bar.rbi:5: unexpected token tRCURLY (2001) + sorbet/rbi/dsl/bar.rbi:6: unexpected token "end" (2001) + + ########################## + ERR + + refute_success_status(result) + end + end end end end diff --git a/spec/tapioca/cli/gem_spec.rb b/spec/tapioca/cli/gem_spec.rb index 45d7edb70..e6cfb05e1 100644 --- a/spec/tapioca/cli/gem_spec.rb +++ b/spec/tapioca/cli/gem_spec.rb @@ -1340,7 +1340,7 @@ def quux(x); end result = @project.tapioca("gem --all") assert_includes(result.out, <<~OUT) - Typechecking RBI files... Done + Checking generated RBI files... Done Changed strictness of sorbet/rbi/gems/bar@0.3.0.rbi to `typed: false` (conflicting with DSL files) @@ -1356,30 +1356,6 @@ def quux(x); end assert_success_status(result) end - it "must turn the strictness of files with error to false only in asked gems" do - @project.tapioca("gem --all --no-auto-strictness") - - assert_file_strictness("true", "sorbet/rbi/gems/foo@0.0.1.rbi") - assert_file_strictness("true", "sorbet/rbi/gems/bar@0.3.0.rbi") - assert_file_strictness("true", "sorbet/rbi/gems/baz@1.0.0.rbi") - - result = @project.tapioca("gem foo bar") - - assert_includes(result.out, <<~OUT) - Typechecking RBI files... Done - - Changed strictness of sorbet/rbi/gems/bar@0.3.0.rbi to `typed: false` (conflicting with DSL files) - OUT - - assert_file_strictness("true", "sorbet/rbi/gems/foo@0.0.1.rbi") - assert_file_strictness("false", "sorbet/rbi/gems/bar@0.3.0.rbi") - assert_file_strictness("true", "sorbet/rbi/gems/baz@1.0.0.rbi") - assert_file_strictness("true", "sorbet/rbi/dsl/foo.rbi") - - assert_empty_stderr(result) - assert_success_status(result) - end - it "must turn the strictness of files with error to false with a custom dir" do @project.write("sorbet/rbi/shims/foo.rbi", <<~RBI) # typed: true @@ -1392,7 +1368,7 @@ def foo; end result = @project.tapioca("gem --dsl-dir sorbet/rbi/shims") assert_includes(result.out, <<~OUT) - Typechecking RBI files... Done + Checking generated RBI files... Done Changed strictness of sorbet/rbi/gems/foo@0.0.1.rbi to `typed: false` (conflicting with DSL files) OUT @@ -1409,6 +1385,70 @@ def foo; end @project.remove("sorbet/rbi/shims/foo.rbi") end end + + describe "sanity" do + before(:all) do + foo = mock_gem("foo", "0.0.1") do + write("lib/foo.rb", FOO_RB) + end + + bar = mock_gem("bar", "1.0.0") do + write("lib/bar.rb", BAR_RB) + end + + @project.require_mock_gem(foo) + @project.require_mock_gem(bar) + @project.bundle_install + @project.tapioca("init") + end + + after do + project.remove("sorbet/rbi/gems") + project.remove("sorbet/rbi/dsl") + end + + it "must display an error message when a generated gem RBI file contains a parse error" do + @project.write("sorbet/rbi/gems/bar@1.0.0.rbi", <<~RBI) + # typed: true + + module Bar + # This method is missing a `)` + sig { params(block: T.proc.params(x: T.any(String, Integer).void).void } + def bar(&block); end + end + RBI + + result = @project.tapioca("gem foo") + + assert_includes(result.err, <<~ERR) + ##### INTERNAL ERROR ##### + + There are parse errors in the generated RBI files. + + This seems related to a bug in Tapioca. + Please open an issue at https://github.com/Shopify/tapioca/issues/new with the following information: + + Tapioca v#{Tapioca::VERSION} + + Command: + bin/tapioca gem foo + + Gems: + ERR + + assert_includes(result.err, "foo (0.0.1)") + + assert_includes(result.err, <<~ERR) + Errors: + sorbet/rbi/gems/bar@1.0.0.rbi:5: unexpected token tRCURLY (2001) + sorbet/rbi/gems/bar@1.0.0.rbi:6: unexpected token "end" (2001) + + ########################## + ERR + + refute_success_status(result) + end + end end end end