class Rcov::FileStatistics

def extend_heredocs

def extend_heredocs
  i = 0
  while i < @lines.size
    unless is_code? i
      i += 1
      next
    end
    #FIXME: using a restrictive regexp so that only <<[A-Z_a-z]\w*
    # matches when unquoted, so as to avoid problems with 1<<2
    # (keep in mind that whereas puts <<2 is valid, puts 1<<2 is a
    # parse error, but  a = 1<<2  is of course fine)
    scanner = StringScanner.new(@lines[i])
    j = k = i
    loop do
      scanned_text = scanner.search_full(/<<(-?)(?:(['"`])((?:(?!\2).)+)\2|([A-Z_a-z]\w*))/, true, true)
      # k is the first line after the end delimiter for the last heredoc
      # scanned so far
      unless scanner.matched?
        i = k
        break
      end
      term = scanner[3] || scanner[4]
      # try to ignore symbolic bitshifts like  1<<LSHIFT
      ident_text = "<<#{scanner[1]}#{scanner[2]}#{term}#{scanner[2]}"
      if scanned_text[/\d+\s*#{Regexp.escape(ident_text)}/]
        # it was preceded by a number, ignore
        i = k
        break
      end
      must_mark = []
      end_of_heredoc = (scanner[1] == "-") ? /^\s*#{Regexp.escape(term)}$/ : /^#{Regexp.escape(term)}$/
      loop do
        break if j == @lines.size
        must_mark << j
        if end_of_heredoc =~ @lines[j]
          must_mark.each do |n|
            @heredoc_start[n] = i
          end
          if (must_mark + [i]).any?{|lineidx| @coverage[lineidx]}
            @coverage[i] ||= :inferred
            must_mark.each{|lineidx| @coverage[lineidx] ||= :inferred}
          end
          # move the "first line after heredocs" index
          if @lines[j+=1] =~ /^\s*\n$/
            k = j
          end
          break
        end
        j += 1
      end
    end
    i += 1
  end
end