Ameba v0.12.0 has been released

Checkout the release notes to see a full scope of changes. Here we will go through the most important changes.

Globs and Excluded configuration options

Ameba now allows to globally configure the list of sources to run the inspection on. There are two new sections which can be added to .ameba.yml:

1
2
3
4
5
6
Globs:
- **/*.cr
- !lib

Excluded:
- src/compiler
  • Globs is used to define paths to include to the inspection. Defaults to %w(**/*.cr !lib)
  • Excluded is used to exclude from the list defined by Globs

Crystal 0.34 compatibility

Crystal 0.34 is not yet released, but thanks to @bcardiff Ameba is now ready to the upcoming release and is still compatible to the current Crystal version 0.33.

New Rules

Style/RedundantNext

Crystal has next keyword which can be used to go to the next iteration in a loop. However, it can also be used to exit from a block, for example:

1
2
3
4
(1..3).each do |e|
break if e.even?
puts e
end # => 1, 3

And in some places next can be overused, especially when it combines in the last expression in the block. For example:

1
2
3
4
5
6
7
8
9
10
block do |v|
case v
when .nil?
next "nil"
when .blank?
next "blank"
else
next "empty"
end
end

In all three places above the next keyword is redundant and is reported by the new rule.


Lint/SharedVarInFiber

To achieve concurrency and parallelism Crystal uses Fibers and Channels. There is a tricky behavior which happens when a shared variable is used across multiple fibers and is mutated during iterations. For example:

1
2
3
4
5
6
7
8
9
n = 0
channel = Channel(Int32).new

while n ‹ 3
n = n + 1
spawn { channel.send n }
end

3.times { puts channel.receive }

You might expect the code above to print 1 2 3, however it prints 3 3 3. The problem is there is a shared variable n and when channel.receive is executed its value is 3.

To solve it, the code above can be written to the following:

1
2
3
4
5
6
7
8
9
10
11
n = 0
channel = Channel(Int32).new

while n ‹ 3
n = n + 1
+ m = n
- spawn do { channel.send n }
+ spawn do { channel.send m }
end

3.times { puts channel.receive }

So instead of using a shared variable n which is declared at the top and mutated in a while loop, we reassign the value to variable m and use it in our spawn. As the result, the code above prints the expected 1 2 3. The new rule properly reports the issue on the first sample and passes on the second one.

There are also other technics to solve the problem above which are officially documented.


Lint/EmptyLoop

After some round of refactoring it can happen that the loop body becomes empty but for whatever reason such an empty loop is forgotten to be removed. A new rule is able to detect a few situations:

1
2
3
4
5
6
7
8
9
10
11
while true
# empty body
end

until false
# empty body
end

loop do
# empty block
end

And these samples are valid and not reported:

1
2
3
4
5
6
7
8
a = 1
while a ‹ 10
a += 1
end

until socket_opened?; end

loop { run }

Lint/RedundantStringCoercion

This is typical situation when a value is being converted to string using Object.to_s method in the interpolation.

1
"Hello, #{name.to_s}"

Since each value enclosed by { ... } ends up invoking Object.to_s explicit calls are redundant and are now reported by Ameba. The code above is forced to be changed to

1
"Hello, #{name}"

Support

A new Patreon page has been created recently to support Ameba. If you enjoy the project please consider becoming a patreon which will give more attention to the project from the development perspective and make it better.

Ameba v0.13.0 has been released Ameba v0.11.0 - New rules, lint in parallel, GitHub action and more.

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×