ruby / syntax_suggest

Searching for unexpected `end` syntax errors takes a lot of time. Let this gem do it for you!

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Capture and annotate NoMethodError

schneems opened this issue · comments

Currently this code:

class Pet
  def initialize
    @name = "cinco"
  end

  def call
    puts "Come here #{@neam.upcase}"
  end
end

Pet.new.call

Gives us this error message

scratch.rb:7:in `call': undefined method `upcase' for nil:NilClass (NoMethodError)

Which gives us no context or info about the code causing this error. I would love it to instead show:

  1  class Pet
  6    def call
❯ 7      puts "Come here #{@neam.upcase}"
  8    end
  9  end

scratch.rb:7:in `call': undefined method `upcase' for nil:NilClass (NoMethodError)

here's a super hacky version:

class NoMethodError
  def message
    begin
    fn = backtrace[0].split(":").first
    line = backtrace[0].split(":")[1].to_i
    code = File.open(fn).read.split("\n")

    nearest_klass, klass_idx = code[0...line-1].each_with_index.select { |x, idx| /class/ === x }.last
    nearest_klass_indent = nearest_klass.chars.take_while { |x| x == " "}.length
    matching_end, end_idx = code[klass_idx...(code.length-1)].each_with_index.select { |x, idx| x.start_with?("#{" "*nearest_klass_indent}end") }.first
    end_idx += klass_idx + 1

    nearest_def, def_idx = code[0...line-1].each_with_index.select { |x, idx| /def/ === x }.last
    nearest_def_indent = nearest_def.chars.take_while { |x| x == " "}.length
    def_matching_end, def_end_idx = code[def_idx...(code.length-1)].each_with_index.select { |x, idx| x.start_with?("#{" "*nearest_def_indent}end") }.first
    def_end_idx += def_idx + 1

    klass_idx += 1
    def_idx += 1
    m = "\nSurrounding code was:\n"
    m += " #{klass_idx.to_s.rjust(3)}       " + nearest_klass + "\n"
    m += " #{def_idx.to_s.rjust(3)  }       " + nearest_def + "\n"
    m += " #{line.to_s.rjust(3)     }     > " + code[line-1] + "\n"
    m += " #{def_end_idx.to_s.rjust(3)  }       " + def_matching_end + "\n"
    m += " #{end_idx.to_s.rjust(3)  }       " + matching_end + "\n"
    m + "\n" + super
    rescue => e
      binding.pry
    end
    #super
  end
end

and output:

Traceback (most recent call last):
	1: from hi.rb:13:in `<main>'
hi.rb:9:in `call':  (NoMethodError)
Surrounding code was:
   3       class Pet
   8         def call
   9     >     puts "Come here #{@neam.upcase}"
  10         end
  11       end

undefined method `upcase' for nil:NilClass