Skip to content

Commit f2f6a13

Browse files
authored
Merge pull request #22681 from Homebrew/java-version-rubocop
rubocops/lines: add JavaVersions cop
2 parents ffc51b7 + 1e8a93f commit f2f6a13

3 files changed

Lines changed: 391 additions & 0 deletions

File tree

Library/Homebrew/rubocops/lines.rb

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,127 @@ def audit_formula(formula_nodes)
455455
EOS
456456
end
457457

458+
# This cop makes sure that Java versions are consistent.
459+
class JavaVersions < FormulaCop
460+
extend AutoCorrector
461+
462+
sig { override.params(formula_nodes: FormulaNodes).void }
463+
def audit_formula(formula_nodes)
464+
return if formula_tap != "homebrew-core"
465+
return if (body_node = formula_nodes.body_node).nil?
466+
467+
openjdk_dependency_matches = find_every_method_call_by_name(body_node, :depends_on).filter_map do |method|
468+
dep = parameters(method).first
469+
dep = dep.keys.first if dep.is_a?(RuboCop::AST::HashNode)
470+
string_content(dep).match(/^openjdk(?:@(\d+(?:\.\d+)*))?$/)
471+
end
472+
473+
# Only handle single OpenJDK dependency scenario
474+
return if openjdk_dependency_matches.count != 1
475+
476+
openjdk_dependency_match = openjdk_dependency_matches.fetch(0)
477+
openjdk_version = openjdk_dependency_match[1]
478+
openjdk_version = "1.8" if openjdk_version == "8"
479+
message = "Java version argument should match the specified dependency (`#{openjdk_dependency_match[0]}`)"
480+
variables = []
481+
482+
# e.g. Language::Java.overridable_java_home_env("25")
483+
java_home_method_call(body_node) do |java_home_node, method, args|
484+
java_version_node = args.first
485+
java_version = nil
486+
487+
case java_version_node
488+
when nil
489+
java_version = nil
490+
when RuboCop::AST::StrNode
491+
java_version = string_content(java_version_node)
492+
when RuboCop::AST::VarNode
493+
variables << java_version_node.name
494+
next
495+
else
496+
next unless java_version_node.nil_type?
497+
498+
if openjdk_version.nil?
499+
offending_node(java_version_node)
500+
problem "Argument is unnecessary when using unversioned OpenJDK" do |corrector|
501+
corrector.replace(java_home_node.source_range, "Language::Java.#{method}")
502+
end
503+
end
504+
end
505+
next if java_version == openjdk_version
506+
507+
correct = "Language::Java.#{method}"
508+
correct += "(\"#{openjdk_version}\")" if openjdk_version
509+
offending_node(java_version_node || java_home_node)
510+
problem message do |corrector|
511+
corrector.replace(java_home_node.source_range, correct)
512+
end
513+
end
514+
515+
# e.g. bin.write_jar_script libexec/"<name>.jar", "<name>", java_version: "25"
516+
find_instance_method_call(body_node, :bin, :write_jar_script) do |method|
517+
params = parameters(method)
518+
next unless (last_param = params.last)
519+
520+
java_version = nil
521+
java_version_node = nil
522+
if last_param.is_a?(RuboCop::AST::HashNode)
523+
java_version_arg_node = last_param.pairs.find { |pair| pair.key.value == :java_version }
524+
java_version_node = java_version_arg_node&.value
525+
526+
case java_version_node
527+
when nil
528+
java_version = nil
529+
when RuboCop::AST::StrNode
530+
java_version = string_content(java_version_node)
531+
when RuboCop::AST::VarNode
532+
variables << java_version_node.name
533+
next
534+
else
535+
next unless java_version_node.nil_type?
536+
end
537+
end
538+
next if openjdk_version == java_version
539+
540+
offending_node(java_version_node || method)
541+
problem message do |corrector|
542+
if java_version_node.nil?
543+
corrector.insert_after(last_param.source_range, ", java_version: \"#{openjdk_version}\"")
544+
elsif openjdk_version.nil?
545+
range = range_with_surrounding_space(range: last_param.source_range, side: :left)
546+
corrector.remove(range_with_surrounding_comma(range, :left))
547+
else
548+
corrector.replace(java_version_node.source_range, "\"#{openjdk_version}\"")
549+
end
550+
end
551+
end
552+
553+
# e.g. java_version = "25"; Language::Java.overridable_java_home_env(java_version)
554+
variables.uniq!
555+
variables.each do |variable|
556+
java_version_assignment(body_node, variable:) do |java_version_node|
557+
java_version = nil
558+
java_version = string_content(java_version_node) if java_version_node.str_type?
559+
next if java_version == openjdk_version
560+
561+
correct = openjdk_version ? "\"#{openjdk_version}\"" : "nil"
562+
offending_node(java_version_node)
563+
problem message do |corrector|
564+
corrector.replace(java_version_node.source_range, correct)
565+
end
566+
end
567+
end
568+
end
569+
570+
def_node_search :java_home_method_call, <<~PATTERN
571+
$(send (const (const nil? :Language) :Java) ${:java_home :java_home_env :overridable_java_home_env} $...)
572+
PATTERN
573+
574+
def_node_search :java_version_assignment, <<~PATTERN
575+
(lvasgn %variable ${nil | str})
576+
PATTERN
577+
end
578+
458579
# This cop makes sure that Python versions are consistent.
459580
class PythonVersions < FormulaCop
460581
extend AutoCorrector

Library/Homebrew/sorbet/rbi/dsl/rubo_cop/cop/formula_audit/java_versions.rbi

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
# typed: true
2+
# frozen_string_literal: true
3+
4+
require "rubocops/lines"
5+
6+
RSpec.describe RuboCop::Cop::FormulaAudit::JavaVersions do
7+
subject(:cop) { described_class.new }
8+
9+
context "when auditing Java versions" do
10+
it "reports no offenses for non-core formulae" do
11+
expect_no_offenses(<<~RUBY)
12+
class Foo < Formula
13+
depends_on "openjdk@25"
14+
15+
def install
16+
java_version = "17"
17+
Language::Java.java_home("21")
18+
Language::Java.java_home_env(java_version)
19+
Language::Java.overridable_java_home_env
20+
bin.write_jar_script libexec/"test.jar", "test"
21+
end
22+
end
23+
RUBY
24+
end
25+
26+
it "reports no offenses when there is no OpenJDK dependency" do
27+
expect_no_offenses(<<~RUBY, "/homebrew-core/")
28+
class Foo < Formula
29+
def install
30+
java_version = "17"
31+
Language::Java.java_home("21")
32+
Language::Java.java_home_env(java_version)
33+
Language::Java.overridable_java_home_env
34+
bin.write_jar_script libexec/"test.jar", "test"
35+
end
36+
end
37+
RUBY
38+
end
39+
40+
it "reports no offenses when there are multiple OpenJDK dependencies" do
41+
expect_no_offenses(<<~RUBY, "/homebrew-core/")
42+
class Foo < Formula
43+
depends_on "openjdk@21" => :build
44+
depends_on "openjdk@25"
45+
46+
def install
47+
java_version = "25"
48+
Language::Java.java_home("21")
49+
Language::Java.java_home_env(java_version)
50+
Language::Java.overridable_java_home_env("21")
51+
bin.write_jar_script libexec/"test.jar", "test", java_version: "25"
52+
end
53+
end
54+
RUBY
55+
end
56+
57+
it "reports no offenses when Java version arguments match versioned OpenJDK dependency" do
58+
expect_no_offenses(<<~RUBY, "/homebrew-core/")
59+
class Foo < Formula
60+
depends_on "openjdk@25"
61+
62+
def install
63+
java_version = "25"
64+
Language::Java.java_home("25")
65+
Language::Java.java_home_env(java_version)
66+
Language::Java.overridable_java_home_env "25"
67+
bin.write_jar_script libexec/"test.jar", "test", java_version: "25"
68+
end
69+
end
70+
RUBY
71+
end
72+
73+
it "reports no offenses when Java version arguments match unversioned OpenJDK dependency" do
74+
expect_no_offenses(<<~RUBY, "/homebrew-core/")
75+
class Foo < Formula
76+
depends_on "openjdk"
77+
78+
def install
79+
java_version = nil
80+
Language::Java.java_home
81+
Language::Java.java_home_env(java_version)
82+
Language::Java.overridable_java_home_env
83+
bin.write_jar_script libexec/"test.jar", "test"
84+
end
85+
end
86+
RUBY
87+
end
88+
89+
it "reports and corrects mismatched java_home version arguments for versioned OpenJDK dependency" do
90+
expect_offense(<<~RUBY, "/homebrew-core/")
91+
class Foo < Formula
92+
depends_on "openjdk@25"
93+
94+
def install
95+
java_version = "21"
96+
^^^^ FormulaAudit/JavaVersions: Java version argument should match the specified dependency (`openjdk@25`)
97+
openjdk_version = nil
98+
^^^ FormulaAudit/JavaVersions: Java version argument should match the specified dependency (`openjdk@25`)
99+
100+
Language::Java.java_home(java_version)
101+
Language::Java.java_home openjdk_version
102+
Language::Java.java_home("17")
103+
^^^^ FormulaAudit/JavaVersions: Java version argument should match the specified dependency (`openjdk@25`)
104+
Language::Java.java_home_env nil
105+
^^^ FormulaAudit/JavaVersions: Java version argument should match the specified dependency (`openjdk@25`)
106+
Language::Java.overridable_java_home_env
107+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FormulaAudit/JavaVersions: Java version argument should match the specified dependency (`openjdk@25`)
108+
end
109+
end
110+
RUBY
111+
112+
expect_correction(<<~RUBY)
113+
class Foo < Formula
114+
depends_on "openjdk@25"
115+
116+
def install
117+
java_version = "25"
118+
openjdk_version = "25"
119+
120+
Language::Java.java_home(java_version)
121+
Language::Java.java_home openjdk_version
122+
Language::Java.java_home("25")
123+
Language::Java.java_home_env("25")
124+
Language::Java.overridable_java_home_env("25")
125+
end
126+
end
127+
RUBY
128+
end
129+
130+
it "reports and corrects mismatched java_home version arguments for unversioned OpenJDK dependency" do
131+
expect_offense(<<~RUBY, "/homebrew-core/")
132+
class Foo < Formula
133+
depends_on "openjdk"
134+
135+
def install
136+
java_version = "21"
137+
^^^^ FormulaAudit/JavaVersions: Java version argument should match the specified dependency (`openjdk`)
138+
openjdk_version = nil
139+
140+
Language::Java.java_home(java_version)
141+
Language::Java.java_home openjdk_version
142+
Language::Java.java_home("17")
143+
^^^^ FormulaAudit/JavaVersions: Java version argument should match the specified dependency (`openjdk`)
144+
Language::Java.java_home_env(nil)
145+
^^^ FormulaAudit/JavaVersions: Argument is unnecessary when using unversioned OpenJDK
146+
Language::Java.overridable_java_home_env "21"
147+
^^^^ FormulaAudit/JavaVersions: Java version argument should match the specified dependency (`openjdk`)
148+
end
149+
end
150+
RUBY
151+
152+
expect_correction(<<~RUBY)
153+
class Foo < Formula
154+
depends_on "openjdk"
155+
156+
def install
157+
java_version = nil
158+
openjdk_version = nil
159+
160+
Language::Java.java_home(java_version)
161+
Language::Java.java_home openjdk_version
162+
Language::Java.java_home
163+
Language::Java.java_home_env
164+
Language::Java.overridable_java_home_env
165+
end
166+
end
167+
RUBY
168+
end
169+
170+
it "reports and corrects mismatched write_jar_script version arguments for versioned OpenJDK dependency" do
171+
expect_offense(<<~RUBY, "/homebrew-core/")
172+
class Foo < Formula
173+
depends_on "openjdk@25"
174+
175+
def install
176+
java_version = "21" # intentionally unused so expected to remain unmodified
177+
openjdk_version = nil
178+
^^^ FormulaAudit/JavaVersions: Java version argument should match the specified dependency (`openjdk@25`)
179+
180+
bin.write_jar_script libexec/"test.jar", "test-1"
181+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FormulaAudit/JavaVersions: Java version argument should match the specified dependency (`openjdk@25`)
182+
bin.write_jar_script libexec/"test.jar", "test-2", java_version: "21"
183+
^^^^ FormulaAudit/JavaVersions: Java version argument should match the specified dependency (`openjdk@25`)
184+
bin.write_jar_script(libexec/"test.jar", "test-3", java_version: nil)
185+
^^^ FormulaAudit/JavaVersions: Java version argument should match the specified dependency (`openjdk@25`)
186+
bin.write_jar_script(libexec/"test.jar", "test-4", java_version: openjdk_version)
187+
end
188+
end
189+
RUBY
190+
191+
expect_correction(<<~RUBY)
192+
class Foo < Formula
193+
depends_on "openjdk@25"
194+
195+
def install
196+
java_version = "21" # intentionally unused so expected to remain unmodified
197+
openjdk_version = "25"
198+
199+
bin.write_jar_script libexec/"test.jar", "test-1", java_version: "25"
200+
bin.write_jar_script libexec/"test.jar", "test-2", java_version: "25"
201+
bin.write_jar_script(libexec/"test.jar", "test-3", java_version: "25")
202+
bin.write_jar_script(libexec/"test.jar", "test-4", java_version: openjdk_version)
203+
end
204+
end
205+
RUBY
206+
end
207+
208+
it "reports and corrects mismatched write_jar_script version arguments for unversioned OpenJDK dependency" do
209+
expect_offense(<<~RUBY, "/homebrew-core/")
210+
class Foo < Formula
211+
depends_on "openjdk"
212+
213+
def install
214+
java_version = "21" # intentionally unused so expected to remain unmodified
215+
openjdk_version = "21"
216+
^^^^ FormulaAudit/JavaVersions: Java version argument should match the specified dependency (`openjdk`)
217+
218+
bin.write_jar_script libexec/"test.jar", "test-1"
219+
bin.write_jar_script libexec/"test.jar", "test-2", java_version: "25"
220+
^^^^ FormulaAudit/JavaVersions: Java version argument should match the specified dependency (`openjdk`)
221+
bin.write_jar_script(libexec/"test.jar", "test-3", java_version: openjdk_version)
222+
end
223+
end
224+
RUBY
225+
226+
expect_correction(<<~RUBY)
227+
class Foo < Formula
228+
depends_on "openjdk"
229+
230+
def install
231+
java_version = "21" # intentionally unused so expected to remain unmodified
232+
openjdk_version = nil
233+
234+
bin.write_jar_script libexec/"test.jar", "test-1"
235+
bin.write_jar_script libexec/"test.jar", "test-2"
236+
bin.write_jar_script(libexec/"test.jar", "test-3", java_version: openjdk_version)
237+
end
238+
end
239+
RUBY
240+
end
241+
end
242+
end

0 commit comments

Comments
 (0)