Class: RuboCop::Cop::DocsKit::EscapedInterpolationInHeredoc

Inherits:
Base
  • Object
show all
Extended by:
AutoCorrector
Defined in:
lib/rubocop/cop/docs_kit/escaped_interpolation_in_heredoc.rb

Overview

Flags an ESCAPED interpolation (\#{...}) inside a double-quoted heredoc and steers to the single-quoted delimiter (<<~'RUBY'), where #{...} is already literal so no backslash is needed.

Docs pages constantly embed Ruby examples that themselves contain #{...}. In a double-quoted heredoc every one of those has to be escaped as \#{...} or Ruby interpolates it — the recurring "escape tax" every audited docs site paid. A single-quoted heredoc delimiter turns the whole body literal, so the examples read exactly as they will render.

Ruby interpolates three sigils in a double-quoted string — #{expr}, #@ivar (also #@@cvar), and #$global — so the cop treats all three escape forms (\#{, \#@, \#$) as the escape tax, and a LIVE (unescaped) occurrence of any of them blocks autocorrection.

Autocorrection is UNSAFE and only offered when the heredoc has no LIVE (unescaped) interpolation: switching the delimiter to single-quoted would freeze a live interpolation into literal text, changing behaviour. When a live interpolation is present the cop reports but leaves the fix to a human.

Examples:

# bad — escape tax
source = <<~RUBY
  puts "hello \#{name}"
RUBY

# good — single-quoted delimiter, `#{...}` is literal
source = <<~'RUBY'
  puts "hello #{name}"
RUBY

Constant Summary collapse

MSG =
"Use a single-quoted heredoc delimiter (`%<delimiter>s`) so " \
"`\#{...}` is literal without escaping."
MSG_LIVE =
"#{MSG} This heredoc also has a live interpolation — fix by hand.".freeze
ESCAPED_INTERPOLATION =

A backslash directly in front of an interpolation opener. Ruby opens an interpolation with #{, #@ (ivar/cvar), or #$ (global), so an escape is a backslash + # + one of those sigil characters. The lookahead keeps the sigil out of the match, so de-escaping only strips the backslash.

/\\#(?=[{@$])/

Instance Method Summary collapse

Instance Method Details

#on_dstr(node) ⇒ Object

A heredoc with a live #{...} parses as a dstr; the escaped ones inside it still show up in the body source. Same check.



55
56
57
# File 'lib/rubocop/cop/docs_kit/escaped_interpolation_in_heredoc.rb', line 55

def on_dstr(node)
  check_heredoc(node)
end

#on_str(node) ⇒ Object



49
50
51
# File 'lib/rubocop/cop/docs_kit/escaped_interpolation_in_heredoc.rb', line 49

def on_str(node)
  check_heredoc(node)
end