diff --git a/Library/Homebrew/rubocops/extend/formula_cop.rb b/Library/Homebrew/rubocops/extend/formula_cop.rb index 4aec1a4c95..c8288ef9f7 100644 --- a/Library/Homebrew/rubocops/extend/formula_cop.rb +++ b/Library/Homebrew/rubocops/extend/formula_cop.rb @@ -108,6 +108,8 @@ module RuboCop # Matches a method with a receiver, # EX: to match `Formula.factory(name)` # call `find_instance_method_call(node, "Formula", :factory)` + # EX: to match `build.head?` + # call `find_instance_method_call(node, :build, :head?)` # yields to a block with matching method node def find_instance_method_call(node, instance, method_name) methods = find_every_method_call_by_name(node, method_name) diff --git a/Library/Homebrew/rubocops/lines_cop.rb b/Library/Homebrew/rubocops/lines_cop.rb index 241f9860e0..ab10f6552a 100644 --- a/Library/Homebrew/rubocops/lines_cop.rb +++ b/Library/Homebrew/rubocops/lines_cop.rb @@ -129,7 +129,7 @@ module RuboCop end # Node Pattern search for Language::Node - def_node_search :languageNodeModule?, <<-EOS.undent + def_node_search :languageNodeModule?, <<-EOS.undent (const (const nil :Language) :Node) EOS end diff --git a/Library/Homebrew/test/rubocops/lines_cop_spec.rb b/Library/Homebrew/test/rubocops/lines_cop_spec.rb index b0ed8f4d1d..31aafbcf8e 100644 --- a/Library/Homebrew/test/rubocops/lines_cop_spec.rb +++ b/Library/Homebrew/test/rubocops/lines_cop_spec.rb @@ -200,5 +200,276 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do expect_offense(expected, actual) end end + + it "with invalid rebuild" do + source = <<-EOS.undent + class Foo < Formula + desc "foo" + url 'http://example.com/foo-1.0.tgz' + bottle do + rebuild 0 + sha256 "fe0679b932dd43a87fd415b609a7fbac7a069d117642ae8ebaac46ae1fb9f0b3" => :sierra + end + end + EOS + + expected_offenses = [{ message: "'rebuild 0' should be removed", + severity: :convention, + line: 5, + column: 4, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "with OS.linux? check" do + source = <<-EOS.undent + class Foo < Formula + desc "foo" + url 'http://example.com/foo-1.0.tgz' + bottle do + if OS.linux? + nil + end + sha256 "fe0679b932dd43a87fd415b609a7fbac7a069d117642ae8ebaac46ae1fb9f0b3" => :sierra + end + end + EOS + + expected_offenses = [{ message: "Don't use OS.linux?; Homebrew/core only supports macOS", + severity: :convention, + line: 5, + column: 7, + source: source }] + + inspect_source(cop, source, "/homebrew-core/") + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "with fails_with :llvm" do + source = <<-EOS.undent + class Foo < Formula + desc "foo" + url 'http://example.com/foo-1.0.tgz' + bottle do + sha256 "fe0679b932dd43a87fd415b609a7fbac7a069d117642ae8ebaac46ae1fb9f0b3" => :sierra + end + fails_with :llvm do + build 2335 + cause "foo" + end + end + EOS + + expected_offenses = [{ message: "'fails_with :llvm' is now a no-op so should be removed", + severity: :convention, + line: 7, + column: 2, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "with def test" do + source = <<-EOS.undent + class Foo < Formula + desc "foo" + url 'http://example.com/foo-1.0.tgz' + + def test + assert_equals "1", "1" + end + end + EOS + + expected_offenses = [{ message: "Use new-style test definitions (test do)", + severity: :convention, + line: 5, + column: 2, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "with def options" do + source = <<-EOS.undent + class Foo < Formula + desc "foo" + url 'http://example.com/foo-1.0.tgz' + + def options + [["--bar", "desc"]] + end + end + EOS + + expected_offenses = [{ message: "Use new-style option definitions", + severity: :convention, + line: 5, + column: 2, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "with deprecated skip_clean call" do + source = <<-EOS.undent + class Foo < Formula + desc "foo" + url 'http://example.com/foo-1.0.tgz' + skip_clean :all + end + EOS + + expected_offenses = [{ message: <<-EOS.undent.chomp, + `skip_clean :all` is deprecated; brew no longer strips symbols + Pass explicit paths to prevent Homebrew from removing empty folders. + EOS + severity: :convention, + line: 4, + column: 2, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "with build.universal?" do + source = <<-EOS.undent + class Foo < Formula + desc "foo" + url 'http://example.com/foo-1.0.tgz' + if build.universal? + "foo" + end + end + EOS + + expected_offenses = [{ message: "macOS has been 64-bit only since 10.6 so build.universal? is deprecated.", + severity: :convention, + line: 4, + column: 5, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "with ENV.universal_binary" do + source = <<-EOS.undent + class Foo < Formula + desc "foo" + url 'http://example.com/foo-1.0.tgz' + if build? + ENV.universal_binary + end + end + EOS + + expected_offenses = [{ message: "macOS has been 64-bit only since 10.6 so ENV.universal_binary is deprecated.", + severity: :convention, + line: 5, + column: 5, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "with ENV.universal_binary" do + source = <<-EOS.undent + class Foo < Formula + desc "foo" + url 'http://example.com/foo-1.0.tgz' + if build? + ENV.x11 + end + end + EOS + + expected_offenses = [{ message: 'Use "depends_on :x11" instead of "ENV.x11"', + severity: :convention, + line: 5, + column: 5, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "with ruby-macho alternatives" do + source = <<-EOS.undent + class Foo < Formula + desc "foo" + url 'http://example.com/foo-1.0.tgz' + system "install_name_tool", "-id" + end + EOS + + expected_offenses = [{ message: 'Use ruby-macho instead of calling "install_name_tool"', + severity: :convention, + line: 4, + column: 10, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "with npm install without language::Node args" do + source = <<-EOS.undent + class Foo < Formula + desc "foo" + url 'http://example.com/foo-1.0.tgz' + system "npm", "install" + end + EOS + + expected_offenses = [{ message: "Use Language::Node for npm install args", + severity: :convention, + line: 4, + column: 2, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end end end