Skip to content

Commit 2eec91b

Browse files
authored
Merge pull request rails#54050 from skipkayhil/hm-rail-inspector-prism
Replace SyntaxTree with Prism in `rail_inspector`
2 parents db4451e + 5a9ef5c commit 2eec91b

File tree

14 files changed

+207
-178
lines changed

14 files changed

+207
-178
lines changed

Gemfile

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,6 @@ gem "uri", ">= 0.13.1", require: false
4545

4646
gem "prism"
4747

48-
group :lint do
49-
gem "syntax_tree", "6.1.1", require: false
50-
end
51-
5248
group :rubocop do
5349
gem "rubocop", ">= 1.25.1", require: false
5450
gem "rubocop-minitest", require: false

Gemfile.lock

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,6 @@ GEM
414414
racc
415415
path_expander (1.1.3)
416416
pg (1.5.8)
417-
prettier_print (1.2.1)
418417
prism (1.2.0)
419418
propshaft (1.1.0)
420419
actionpack (>= 7.0.0)
@@ -608,8 +607,6 @@ GEM
608607
stringio (3.1.1)
609608
sucker_punch (3.2.0)
610609
concurrent-ruby (~> 1.0)
611-
syntax_tree (6.1.1)
612-
prettier_print (>= 1.2.0)
613610
tailwindcss-rails (3.0.0)
614611
railties (>= 7.0.0)
615612
tailwindcss-ruby
@@ -737,7 +734,6 @@ DEPENDENCIES
737734
stackprof
738735
stimulus-rails
739736
sucker_punch
740-
syntax_tree (= 6.1.1)
741737
tailwindcss-rails
742738
terser (>= 1.1.4)
743739
thruster

guides/source/configuring.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ Below are the default values associated with each target version. In cases of co
7272

7373
- [`config.active_record.postgresql_adapter_decode_dates`](#config-active-record-postgresql-adapter-decode-dates): `true`
7474
- [`config.active_record.validate_migration_timestamps`](#config-active-record-validate-migration-timestamps): `true`
75-
- [`config.active_storage.web_image_content_types`](#config-active-storage-web-image-content-types): `%w[image/png image/jpeg image/gif image/webp]`
75+
- [`config.active_storage.web_image_content_types`](#config-active-storage-web-image-content-types): `%w( image/png image/jpeg image/gif image/webp )`
7676
- [`config.yjit`](#config-yjit): `true`
7777

7878
#### Default Values for Target Version 7.1

tools/rail_inspector/lib/rail_inspector/configuring.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def initialize
1212
end
1313

1414
def call(path)
15-
@cache[path] ||= SyntaxTree.parse(SyntaxTree.read(path))
15+
@cache[path] ||= Prism.parse_file(path.to_s).value
1616
end
1717
end
1818

tools/rail_inspector/lib/rail_inspector/configuring/check/general_configuration.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def initialize(checker)
1414
def call
1515
visitor = Visitor::Attribute.new
1616
visitor.visit(app_config_tree)
17-
visitor.attribute_map[APP_CONFIG_CONST]["attr_accessor"]
17+
visitor.attribute_map[APP_CONFIG_CONST][:attr_accessor]
1818
end
1919

2020
private
Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# frozen_string_literal: true
22

3-
require "syntax_tree"
3+
require "prism"
44

55
module RailInspector
66
module Visitor
7-
class Attribute < SyntaxTree::Visitor
7+
class Attribute < Prism::Visitor
88
attr_reader :attribute_map
99

1010
def initialize
@@ -13,31 +13,30 @@ def initialize
1313
end
1414

1515
def with_namespace(node)
16-
@namespace_stack << node.constant.constant.value
16+
@namespace_stack << node.constant_path.name
1717
visit_child_nodes(node)
1818
@namespace_stack.pop
1919
end
2020

21-
visit_method alias_method :visit_module, :with_namespace
21+
alias visit_class_node with_namespace
22+
alias visit_module_node with_namespace
2223

23-
visit_method alias_method :visit_class, :with_namespace
24-
25-
visit_method def visit_command(node)
26-
attr_access = node.message.value
24+
def visit_call_node(node)
25+
attr_access = node.name
2726
return unless ATTRIBUTE_METHODS.include?(attr_access)
2827

2928
full_namespace = @namespace_stack.join("::")
3029

3130
@attribute_map[full_namespace] ||= {}
3231
@attribute_map[full_namespace][attr_access] ||= Set.new
3332

34-
attributes = node.arguments.parts.map { |p| p.value.value }
33+
attributes = node.arguments.arguments.map { |p| p.value }
3534

3635
@attribute_map[full_namespace][attr_access].merge(attributes)
3736
end
3837

3938
private
40-
ATTRIBUTE_METHODS = %w[attr_accessor attr_reader attr_writer]
39+
ATTRIBUTE_METHODS = %i[attr_accessor attr_reader attr_writer]
4140
end
4241
end
4342
end

tools/rail_inspector/lib/rail_inspector/visitor/framework_default.rb

Lines changed: 79 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,137 +1,129 @@
11
# frozen_string_literal: true
22

3-
require "syntax_tree"
3+
require "prism"
44

55
require_relative "./hash_to_string"
6-
require_relative "./multiline_to_string"
76

87
module RailInspector
98
module Visitor
109
class FrameworkDefault
11-
TargetVersionCaseFinder =
12-
SyntaxTree::Search.new(
13-
->(node) do
14-
node in SyntaxTree::Case[
15-
value: SyntaxTree::CallNode[
16-
receiver: SyntaxTree::VarRef[
17-
value: SyntaxTree::Ident[value: "target_version"]
18-
]
19-
]
20-
]
21-
end
22-
)
23-
2410
attr_reader :config_map
2511

2612
def initialize
2713
@config_map = {}
2814
end
2915

3016
def visit(node)
31-
case_node, *others = TargetVersionCaseFinder.scan(node).to_a
32-
raise "#{others.length} other cases?" unless others.empty?
17+
target_version_case = node.breadth_first_search do |n|
18+
n in Prism::CaseNode[
19+
predicate: Prism::CallNode[receiver: Prism::LocalVariableReadNode[name: :target_version]]
20+
]
21+
end
3322

34-
visit_when(case_node.consequent)
23+
target_version_case.conditions.each { |cond| visit_when(cond) }
3524
end
3625

3726
private
3827
def visit_when(node)
39-
version = node.arguments.parts[0].parts[0].value
40-
41-
config_map[version] = VersionedConfig.new.config_for(node.statements)
28+
version = node.conditions[0].unescaped
4229

43-
visit_when(node.consequent) if node.consequent.is_a? SyntaxTree::When
30+
config_map[version] = VersionedConfig.new.tap { |v| v.visit(node.statements) }.configs
4431
end
4532

46-
class VersionedConfig < SyntaxTree::Visitor
33+
class VersionedConfig < Prism::Visitor
4734
attr_reader :configs
4835

4936
def initialize
5037
@configs = {}
5138
@framework_stack = []
5239
end
5340

54-
def config_for(node)
55-
visit(node)
56-
@configs
57-
end
41+
def visit_if_node(node)
42+
unless new_framework = respond_to_framework?(node.predicate)
43+
return visit_child_nodes(node)
44+
end
5845

59-
visit_methods do
60-
def visit_if(node)
61-
unless new_framework = respond_to_framework?(node.predicate)
62-
return super
63-
end
46+
if ENV["STRICT"] && current_framework
47+
raise "Potentially nested framework? Current: '#{current_framework}', found: '#{new_framework}'"
48+
end
6449

65-
if ENV["STRICT"] && current_framework
66-
raise "Potentially nested framework? Current: '#{current_framework}', found: '#{new_framework}'"
67-
end
50+
@framework_stack << new_framework
51+
visit_child_nodes(node)
52+
@framework_stack.pop
53+
end
54+
55+
def visit_call_node(node)
56+
name = node.name.to_s
6857

69-
@framework_stack << new_framework
70-
super
71-
@framework_stack.pop
58+
unless name.end_with? "="
59+
return super
7260
end
7361

74-
def visit_assign(node)
75-
assert_framework(node)
76-
77-
target = SyntaxTree::Formatter.format(nil, node.target)
78-
value =
79-
case node.value
80-
when SyntaxTree::HashLiteral
81-
HashToString.new.tap { |v| v.visit(node.value) }.to_s
82-
when SyntaxTree::StringConcat
83-
MultilineToString.new.tap { |v| v.visit(node.value) }.to_s
84-
else
85-
SyntaxTree::Formatter.format(nil, node.value)
86-
end
87-
@configs[target] = value
62+
handle_assignment(node, name[...-1], node.arguments.arguments[0])
63+
end
64+
65+
def visit_call_or_write_node(node)
66+
name = node.write_name.to_s
67+
68+
unless name.end_with? "="
69+
return super
8870
end
8971

90-
def visit_opassign(node)
91-
if node.operator.name == :"||="
92-
visit_assign(node)
93-
else
94-
super
72+
handle_assignment(node, node.read_name.to_s, node.value)
73+
end
74+
75+
def handle_assignment(node, name, value)
76+
prefix = case node.receiver
77+
in Prism::ConstantReadNode[name: constant_name]
78+
constant_name
79+
in Prism::SelfNode
80+
"self"
81+
in Prism::CallNode[receiver: nil, name: framework]
82+
framework_string = framework.to_s
83+
84+
unless current_framework == framework_string
85+
raise "expected: #{current_framework}, actual: #{framework_string}"
9586
end
87+
88+
framework_string
89+
else
90+
node.receiver.location.slice
9691
end
97-
end
9892

99-
private
100-
def assert_framework(node)
101-
framework =
102-
case node.target.parent
103-
in { value: SyntaxTree::Const } |
104-
{ value: SyntaxTree::Kw[value: "self"] }
105-
nil
106-
in receiver: { value: { value: framework } }
107-
framework
108-
in value: { value: framework }
109-
framework
110-
end
111-
112-
return if current_framework == framework
113-
114-
raise "Expected #{current_framework} to match #{framework}"
93+
target = "#{prefix}.#{name}"
94+
95+
string_value = case value
96+
in Prism::ConstantPathNode
97+
value.full_name
98+
in Prism::HashNode
99+
HashToString.new.tap { |v| v.visit(value) }.to_s
100+
in Prism::InterpolatedStringNode
101+
"\"#{value.parts.map(&:content).join("")}\""
102+
in Prism::FalseNode
103+
"false"
104+
in Prism::TrueNode
105+
"true"
106+
else
107+
value.location.slice
115108
end
116109

110+
@configs[target] = string_value
111+
end
112+
113+
private
117114
def current_framework
118115
@framework_stack.last
119116
end
120117

121118
def respond_to_framework?(node)
122-
if node in SyntaxTree::CallNode[
123-
receiver: nil,
124-
message: SyntaxTree::Ident[value: "respond_to?"],
125-
arguments: SyntaxTree::ArgParen[
126-
arguments: SyntaxTree::Args[
127-
parts: [
128-
SyntaxTree::SymbolLiteral[
129-
value: SyntaxTree::Ident[value: new_framework]
130-
]
131-
]
132-
]
133-
]
134-
]
119+
if node in Prism::CallNode[
120+
name: :respond_to?,
121+
arguments: Prism::ArgumentsNode[
122+
arguments: [
123+
Prism::SymbolNode[unescaped: new_framework]
124+
]
125+
]
126+
]
135127
new_framework
136128
end
137129
end

0 commit comments

Comments
 (0)