Lint for Ruby

Hi folks.

I was wondering whether anyone had done any work on a Lint style tool
for Ruby programs?

A google for "Ruby Lint" didn't turn up anything obviously useful.

Regards,

Matt

What would it check for? I know I grep for "elseif" and I think my
personal extensions library still includes something like:

     def elseif; raise "It's elsif you fool!" end

but (in general) I'm not sure what you would want to catch. Maybe we
could take up a collection ("My dumbest ruby gaffes" or something)?

   -- Markus

···

On Mon, 2004-10-25 at 11:10, Matt Mower wrote:

Hi folks.

I was wondering whether anyone had done any work on a Lint style tool
for Ruby programs?

A google for "Ruby Lint" didn't turn up anything obviously useful.

Regards,

Matt

Short version of this reply...
   * "ruby -w"
   * Unit tests and .respond_to? and kind_of? blur the boundary
     between static analysis and dynamic testing.

Long winded version of this reply...

A very interesting question, what static analysis do we need for Ruby?

"ruby -w" does a lot for you, don't leave the command line without it.

Unit tests really do catch a lot of problems and are so easy to do.

I tend to unit test and cross brace with a smattering of asserts in my
code at critical places..

For example this gives me similar sort of safety that static typing
would have, but with the flexibility of duck typing...

   def dostuff(duck)
     raise "The duck '#{duck}' should be able to :quack" unless duck.respond_to? :quack
     do_deeper_stuff(duck)
   end

   def do_deeper_stuff(duck)
     do_really_deep_stuff(duck)
   end

   def do_really_deep_stuff(duck)
     duck.quack
   end

This example is too trivial to justify it, but in more complex code it
can be a good way of forcing such type errors to the surface even when
they are not exercised by your unit test.

eg. The unit test may not be extensive enough to force control (in a
more complex example) all the way down to "do_really_deep_stuff" but
the assert catches the type error on _all_ runs.

The other trick is Design by Contract style invariants.

A lot of these suggestions are dynamic run-time stuff, not lint
static analysis, but ruby's introspective abilities like respond_to? and
kind_of? and unit tests makes that boundary a bit fuzzy.

   class MyClass
     def invariant
       unless BIG_HAIRY_EXPRESSION_THAT_IS_ALWAYS_TRUE_IF_THIS_OBJECT_IS_SANE
         pp self
         raise "Invariant failure"
       end
     end

     def initialize
       do stuff
       invariant
     end

     def complex_thingy
       invariant
       do complex stuff
       invariant
     end

     def hairy_stuff( inputs)
      raise hell unless ASSERT_STUFF_ABOUT_INPUT
      do hairy stuff
      invariant
     end
   end

The another habit is to use rubyish style that resolves a lot of lint-able problems.

C++ uses the "Resource Acquisition Is Initialization" idiom to minimise such things, the Ruby equivalent is

using_resource do |resource|
   do stuff with resource
end

A lot of lint-able problems like the evil C switch statement has
simply been eliminated by Matz's excellent design.

The vast majority of the bugs left in my code are either in the
"ruby-lint --read-bosses-mind --pendantic mycode.rb" category or in a failure
handling case.

I'll admit it.

My unit tests are fairly good at testing that my code does what it
should do, but I'm pretty lousy at imagining and testing ways in which
external entities fail.

Hence I would say most of my latent bugs are in the area of failure
handling.

Would lint help there? Maybe, but more creative use of mock objects in
my unit tests would help more.

I'm looking at Needle as a very promising way of doing that.
   http://needle.rubyforge.org/

John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : john.carter@tait.co.nz
New Zealand

The universe is absolutely plastered with the dashed lines exactly one
space long.

···

On Tue, 26 Oct 2004, Matt Mower wrote:

I was wondering whether anyone had done any work on a Lint style tool
for Ruby programs?

A google for "Ruby Lint" didn't turn up anything obviously useful.

"Matt Mower" <matt.mower@gmail.com> schrieb im Newsbeitrag
news:d563731904102511108047fd3@mail.gmail.com...

Hi folks.

I was wondering whether anyone had done any work on a Lint style tool
for Ruby programs?

A google for "Ruby Lint" didn't turn up anything obviously useful.

Others have provided some solutions (I'd use "ruby -cw") but IMHO it boils
down to the statement that static checks will not gain you much with Ruby
because of its dynamic nature and typeless variables. For example, you
usually can't iterate through the wrong index range on an array because
most of the time you'll be using Array#each anyway leaving little room for
off by one errors.

Kind regards

    robert

Matt Mower ha scritto:

Hi folks.

I was wondering whether anyone had done any work on a Lint style tool
for Ruby programs?

A google for "Ruby Lint" didn't turn up anything obviously useful.

if you google for rubychecker you may find a thesis from a robert feldt's student that has done something like this. I don't think he released anything, but you may ask, maybe.

On Tue, Oct 26, 2004 at 02:22:26PM +0900, John Carter scribed:

A very interesting question, what static analysis do we need for Ruby?

Things that look like accidental violations of variable scoping,
until the language has something a bit more elegant to handle said
scoping:

  i = 3
  ...
  arr.each { |i| do_something_with(i) }
  ...
  print "Why is i not 3?\n"

if the project is such that it's not worth implementing unit
tests (aka, it's a quick hack), the more static analysis you
can get, the better. Plus, it's always nice to have redundancy
in your sanity checking.

  -Dave

···

--
work: dga@lcs.mit.edu me: dga@pobox.com
      MIT Laboratory for Computer Science http://www.angio.net/

gabriele renzi ha scritto:

if you google for rubychecker you may find a thesis from a robert

                     ^^^^^^^^^^^

sorry, that was CheckR (http://www.pronovomundo.com/htu/theses2004/checkr_olofsson_thesis_final_040521.pdf\)

I take part of what I said back. There is one idea in that thesis that
would be pretty useful and very easy to do.

There is a really really very simple lint program I want to write.

* Find all identifiers in a body of code.
* Give me a list of near misses in name space.

For example if the code contained the words do_stuff, dostuff,
doStuff, dstuff,dostuf I would want a list of all of them.

That should be really pretty easy to write.

Anybody want to play with?

John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : john.carter@tait.co.nz
New Zealand

The universe is absolutely plastered with the dashed lines exactly one
space long.

···

On Wed, 27 Oct 2004, gabriele renzi wrote:

(http://www.pronovomundo.com/htu/theses2004/checkr_olofsson_thesis_final_040521.pdf\)

John Carter wrote:

(http://www.pronovomundo.com/htu/theses2004/checkr_olofsson_thesis_final_040521.pdf\)

I take part of what I said back. There is one idea in that thesis that
would be pretty useful and very easy to do.

Only one; maybe I should rethink the approval then... :wink:

I truly think there is some power in having a static analyser also for Ruby, but after taking the "low-hanging" fruits (like the one you mention and is talked about in the thesis) you need some kind of intelligence / pattern-matching to find other bugs. I hope to attract another student to go deeper/further...

Regards,

Robert

···

On Wed, 27 Oct 2004, gabriele renzi wrote:

John Carter wrote:

(http://www.pronovomundo.com/htu/theses2004/checkr_olofsson_thesis_final_040521.pdf\)

I take part of what I said back. There is one idea in that thesis that
would be pretty useful and very easy to do.

Only one; maybe I should rethink the approval then... :wink:

Only one that was useful _and_ very easy. There were many others in the not easy basket.

I truly think there is some power in having a static analyser also for Ruby, but after taking the "low-hanging" fruits (like the one you mention and is talked about in the thesis) you need some kind of intelligence / pattern-matching to find other bugs. I hope to attract another student to go deeper/further...

Hmm. The next highest source of bugs in Ruby programs is getting
Regexes wrong.

I have one RCR in on that subject already...
   RCR 179: Create a 'NoMatchData' object on Regex match failing.

There after I really think you are into annotated checking like
splint. Yuck.

As I said, most of the bugs left in Ruby programs are functional in
nature. You need a "ruby-lint --read-my-mind myprog.rb"

Perhaps the most bang for analysis buck could be obtained from...
   1) Propagating duck typing constraints up the call tree.
   2) Static analysis of Design by Contract assertions. DbC being
      closest to formally stating what is on your mind.

For example allowing the programmer to insert some structured form of
Design by Contract style pre and post condition assertions and
attempting to statically check them.

Also allow the programmer to declare, in a structured manner, an
invariant, and attempt to statically check that it is valid after
construction, and before and after every public: method.

The nice thing is you can afford to really sloppy and heuristic about
it. To be valuable your checker need only find _any_ single case where
the contract _is_ violated. It needn't even try find every case.

John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : john.carter@tait.co.nz
New Zealand

The universe is absolutely plastered with the dashed lines exactly one
space long.

···

On Sun, 31 Oct 2004, Robert Feldt wrote:

On Wed, 27 Oct 2004, gabriele renzi wrote:

[snip]

Hmm. The next highest source of bugs in Ruby programs is getting
Regexes wrong.

I have one RCR in on that subject already...
   RCR 179: Create a 'NoMatchData' object on Regex match failing.

Sorry.. I want to advertise for my regexp package.
it can prettyprint a regexp, which sometimes can be helpful.

screenshot:
http://aeditor.rubyforge.org/aeditor_shots/042.png

raa-entry:
http://raa.ruby-lang.org/project/regexp/

It can be installed via RPA.. by typing
rpa install re

···

On Sunday 31 October 2004 22:34, John Carter wrote:

--
Simon Strandgaard

Cute.

Nice to have, almost what I want.

I would assume that deep inside you have translated the Regex into some sort of finite automaton.

What I would like is at the transition where the FA jams, it returns the index to the char in the string it jammed on, and the range of chars for which it would have accepted at that point.

For example
  /ab[cde]|ab(xy|yz)|ab_s/.try_match("abf")
should return something like...
"Jammed on index 2, character 'f' expected one of [cdexy_]"

John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : john.carter@tait.co.nz
New Zealand

The universe is absolutely plastered with the dashed lines exactly one
space long.

···

On Mon, 1 Nov 2004, Simon Strandgaard wrote:

On Sunday 31 October 2004 22:34, John Carter wrote:
[snip]

Hmm. The next highest source of bugs in Ruby programs is getting
Regexes wrong.

I have one RCR in on that subject already...
   RCR 179: Create a 'NoMatchData' object on Regex match failing.

Sorry.. I want to advertise for my regexp package.
it can prettyprint a regexp, which sometimes can be helpful.

[snip]

I would assume that deep inside you have translated the Regex into some
sort of finite automaton.

What I would like is at the transition where the FA jams, it returns the
index to the char in the string it jammed on, and the range of chars for
which it would have accepted at that point.

For example
  /ab[cde]|ab(xy|yz)|ab_s/.try_match("abf")
should return something like...
"Jammed on index 2, character 'f' expected one of [cdexy_]"

Interesting.. however I don't think its possible, because of backtracking.
Though, I could do more logging of what is going on, so that one can inspect
the sequence of events (lots of data).

This is somehow related to maybe_match, which returns true as long as there is
a chance for the regexp to match.. and false when there is no chance it can
match. (maybe_match is not implemented.. but I expect to add it one day).

···

On Monday 01 November 2004 01:42, John Carter wrote:

--
Simon Strandgaard