Class: Prism::Merge::FileAnalysis

Inherits:
Object
  • Object
show all
Includes:
Ast::Merge::FileAnalyzable
Defined in:
lib/prism/merge/file_analysis.rb

Overview

Simplified file analysis using Prism’s native comment attachment.
This version leverages parse_result.attach_comments! to automatically
attach comments to nodes, eliminating the need for manual comment tracking
and the CommentNode class.

Key improvements over V1:

  • Uses Prism’s native node.location.leading_comments and trailing_comments
  • No manual comment tracking or CommentNode class
  • Simpler freeze block extraction via comment scanning
  • Better performance (one attach_comments! call vs multiple iterations)
  • Enhanced freeze block validation (detects partial nodes and non-class/module contexts)

Constant Summary collapse

DEFAULT_FREEZE_TOKEN =

Default freeze token for identifying freeze blocks

"prism-merge"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(source, freeze_token: DEFAULT_FREEZE_TOKEN, signature_generator: nil, **options) ⇒ FileAnalysis

Initialize file analysis with Prism’s native comment handling

Parameters:

  • source (String)

    Ruby source code to analyze

  • freeze_token (String) (defaults to: DEFAULT_FREEZE_TOKEN)

    Token for freeze block markers (default: “prism-merge”)

  • signature_generator (Proc, nil) (defaults to: nil)

    Custom signature generator

  • options (Hash)

    Additional options for forward compatibility



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/prism/merge/file_analysis.rb', line 33

def initialize(source, freeze_token: DEFAULT_FREEZE_TOKEN, signature_generator: nil, **options)
  @source = source
  @lines = source.lines
  @freeze_token = freeze_token
  @signature_generator = signature_generator
  # **options captured for forward compatibility
  @parse_result = DebugLogger.time("FileAnalysis#parse") { Prism.parse(source) }

  # Use Prism's native comment attachment
  # On JRuby, the Comments class may not be loaded yet, so we need to require it
  attach_comments_safely!

  # Extract and validate structure
  @statements = extract_and_integrate_all_nodes

  DebugLogger.debug("FileAnalysis initialized", {
    signature_generator: signature_generator ? "custom" : "default",
    statements_count: @statements.size,
    frozen_nodes_count: frozen_nodes.size,
  })
end

Instance Attribute Details

#parse_resultPrism::ParseResult (readonly)

Returns The parse result from Prism.

Returns:

  • (Prism::ParseResult)

    The parse result from Prism



25
26
27
# File 'lib/prism/merge/file_analysis.rb', line 25

def parse_result
  @parse_result
end

Class Method Details

.attach_comments_safely!(parse_result) ⇒ void

This method returns an undefined value.

Safely attach comments to nodes, handling JRuby compatibility issues.
On JRuby, the Prism::ParseResult::Comments class may not be autoloaded,
so we need to explicitly require it.

This is a class method so it can be used anywhere in prism-merge code
that needs to attach comments to a parse result.

Parameters:

  • parse_result (Prism::ParseResult)

    The parse result to attach comments to



136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/prism/merge/file_analysis.rb', line 136

def attach_comments_safely!(parse_result)
  parse_result.attach_comments!
# :nocov: defensive - JRuby compatibility for Comments class autoloading
rescue NameError => e
  if e.message.include?("Comments")
    # On JRuby, the Comments class needs to be explicitly required
    require "prism/parse_result/comments"
    parse_result.attach_comments!
  else
    raise
  end
  # :nocov:
end

Instance Method Details

#errorsArray<Prism::ParseError>

Get parse errors for compatibility with SmartMergerBase.

Returns:

  • (Array<Prism::ParseError>)

    Array of parse errors



63
64
65
# File 'lib/prism/merge/file_analysis.rb', line 63

def errors
  @parse_result.errors
end

#fallthrough_node?(value) ⇒ Boolean

Override to detect Prism nodes for signature generator fallthrough

Parameters:

  • value (Object)

    The value to check

Returns:

  • (Boolean)

    true if this is a fallthrough node



77
78
79
# File 'lib/prism/merge/file_analysis.rb', line 77

def fallthrough_node?(value)
  value.is_a?(::Prism::Node) || super
end

#frozen_node?(node) ⇒ Boolean

Determine if a node is frozen (has a freeze marker in its leading comments).

For Ruby AST nodes, a freeze marker applies only to the node it directly
precedes in leading comments. If a freeze marker appears INSIDE a block
(nested in the body), it applies to that nested statement, NOT the outer
block. This is different from comment-only formats like Markdown where
checking content containment makes sense.

Nested freeze markers inside the node’s body are handled during recursive
body merging, where each nested statement gets its own freeze detection.

Parameters:

  • node (Prism::Node, Ast::Merge::NodeTyping::FrozenWrapper)

    The node to check

Returns:

  • (Boolean)

    true if the node has a freeze marker in its leading comments



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/prism/merge/file_analysis.rb', line 94

def frozen_node?(node)
  # Already wrapped as frozen
  return true if node.is_a?(Ast::Merge::Freezable)

  return false unless @freeze_token

  # Get the actual node (in case it's a Wrapper)
  actual_node = node.respond_to?(:unwrap) ? node.unwrap : node

  freeze_pattern = /#{Regexp.escape(@freeze_token)}:freeze/i

  # Check for freeze marker in leading comments ONLY
  if actual_node.respond_to?(:location) && actual_node.location.respond_to?(:leading_comments)
    return true if actual_node.location.leading_comments.any? { |c| c.slice.match?(freeze_pattern) }
  end

  false
end

#frozen_nodesArray<Ast::Merge::NodeTyping::FrozenWrapper>

Get nodes that are frozen (have a freeze marker).
Returns FrozenWrapper instances that include the Freezable behavior,
allowing them to satisfy both is_a?(Freezable) and is_a?(NodeTyping::Wrapper).

Returns:

  • (Array<Ast::Merge::NodeTyping::FrozenWrapper>)

    Wrapped frozen nodes



118
119
120
121
122
123
124
# File 'lib/prism/merge/file_analysis.rb', line 118

def frozen_nodes
  # Return the underlying Prism nodes for tests and callers that expect
  # Prism node types. Statements may be wrapped in FrozenWrapper; unwrap
  # them here.
  statements.select { |node| node.is_a?(Ast::Merge::Freezable) }
    .map { |node| node.respond_to?(:unwrap) ? node.unwrap : node }
end

#nodes_with_commentsArray<Hash>

Get nodes with their associated comments and metadata
Comments are now accessed via Prism’s native node.location API

Returns:

  • (Array<Hash>)

    Array of node info hashes



70
71
72
# File 'lib/prism/merge/file_analysis.rb', line 70

def nodes_with_comments
  @nodes_with_comments ||= extract_nodes_with_comments
end

#valid?Boolean

Check if parse was successful

Returns:

  • (Boolean)


57
58
59
# File 'lib/prism/merge/file_analysis.rb', line 57

def valid?
  @parse_result.success?
end