Module: SimpleCov::SourceFile::SourceLoader

Defined in:
lib/simplecov/source_file/source_loader.rb

Overview

Reads a source file into an array of lines, honoring the source's shebang and coding: magic comment when present. Always transcodes to UTF-8 with invalid/undef bytes replaced — both for JRuby compatibility and to keep encoding shenanigans in one place (see #866).

Constant Summary collapse

SHEBANG_REGEX =
/\A#!/
RUBY_FILE_ENCODING_MAGIC_COMMENT_REGEX =
/\A#\s*(?:-\*-)?\s*(?:en)?coding:\s*(\S+)\s*(?:-\*-)?\s*\z/

Class Method Summary collapse

Class Method Details

.call(filename) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/simplecov/source_file/source_loader.rb', line 16

def call(filename)
  lines = [] #: Array[String]
  # The default encoding is UTF-8
  File.open(filename, "rb:UTF-8") do |file|
    current_line = scrub_invalid(file.gets)

    if current_line && shebang?(current_line)
      lines << current_line
      current_line = scrub_invalid(file.gets)
    end

    read_lines(file, lines, current_line)
  end
end

.ensure_remove_undefs(file_lines) ⇒ Object

Guarantee every line leaves the loader as valid UTF-8, replacing what can't be represented: transcode non-UTF-8 lines (setting invalid/undef options on file.set_encoding doesn't work properly, and this also works around a JRuby incompatibility) and scrub UTF-8-tagged lines that carry invalid bytes, which would otherwise raise from every regex the classifier runs.



66
67
68
69
70
71
72
73
74
75
76
# File 'lib/simplecov/source_file/source_loader.rb', line 66

def ensure_remove_undefs(file_lines)
  file_lines.each do |line|
    # simplecov:disable — defensive: only fires for non-UTF-8 source files
    if line.encoding == Encoding::UTF_8
      line.scrub! unless line.valid_encoding?
    else
      line.encode!("UTF-8", invalid: :replace, undef: :replace)
    end
    # simplecov:enable
  end
end

.read_lines(file, lines, current_line) ⇒ Object



45
46
47
48
49
50
# File 'lib/simplecov/source_file/source_loader.rb', line 45

def read_lines(file, lines, current_line)
  return lines unless current_line

  set_encoding_based_on_magic_comment(file, current_line)
  lines.concat([current_line], ensure_remove_undefs(file.readlines))
end

.scrub_invalid(line) ⇒ Object

A line read as UTF-8 can still carry invalid bytes (a Latin-1 source file without a magic comment, say). Replace them before any regex sees the line: the shebang and magic-comment checks would otherwise raise ArgumentError and take the report down.



35
36
37
38
39
# File 'lib/simplecov/source_file/source_loader.rb', line 35

def scrub_invalid(line)
  return line if line.nil? || line.valid_encoding?

  line.scrub
end

.set_encoding_based_on_magic_comment(file, line) ⇒ Object

Encoding magic comment must be placed at first line except for shebang.



54
55
56
57
58
# File 'lib/simplecov/source_file/source_loader.rb', line 54

def set_encoding_based_on_magic_comment(file, line)
  if (match = RUBY_FILE_ENCODING_MAGIC_COMMENT_REGEX.match(line))
    file.set_encoding(match[1], "UTF-8")
  end
end

.shebang?(line) ⇒ Boolean

Returns:

  • (Boolean)


41
42
43
# File 'lib/simplecov/source_file/source_loader.rb', line 41

def shebang?(line)
  SHEBANG_REGEX.match?(line)
end