Lint for Ruby

M

Matt Mower

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
 
M

Markus

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
 
J

John Carter

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.

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 : (e-mail address removed)
New Zealand

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

Robert Klemme

Matt Mower said:
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
 
G

gabriele renzi

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.
 
D

David G. Andersen

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
 
J

John Carter



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 : (e-mail address removed)
New Zealand

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

Robert Feldt

John said:
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... ;)

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
 
J

John Carter

Only one; maybe I should rethink the approval then... ;)

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...
http://rcrchive.net/rcr/show/179


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 : (e-mail address removed)
New Zealand

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

Simon Strandgaard

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...
http://rcrchive.net/rcr/show/179

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
 
J

John Carter

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...
http://rcrchive.net/rcr/show/179

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

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 : (e-mail address removed)
New Zealand

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

Simon Strandgaard

On Monday 01 November 2004 01:42, John Carter wrote:
[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).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,160
Messages
2,570,889
Members
47,422
Latest member
LatashiaZc

Latest Threads

Top