Checkout the release notes to see a full scope of changes. Here we will go through the most important changes.
New Lint Rules
Lint/DuplicatedRequire
Duplicated requires in the source file do not have too much effect on the running program, however it creates a mess. This rule reports such cases.
1 | require "./thing" |
Lint/SpecFocus
In specs focus: true
is mainly used to focus on a spec item locally during development. However, if such change is committed, it silently runs only focused spec on all other enviroments, which is undesired.
This rule reports if specs are focused.
For example, this is considered invalid:
1 | it "works", focus: true do |
New Performance Rules
Performance/AnyInsteadOfEmpty
Using Enumerable#any?
instead of Enumerable#empty?
might lead to an unexpected results (like [nil, false].any? # => false
). In some cases it also might be less efficient, since it iterates until the block will return a truthy value, instead of just checking if there’s at least one value present.
Now this is considered bad:
1 | [1, 2, 3].any? |
and it should be written as this:
1 | ![1, 2, 3].empty? |
Performance/ChainedCallWithNoBang
This rule is used to identify usage of chained calls not utilizing the bang method variants. This is a nice way to find all the places to reduce unnecessary collection allocations.
For example, this is considered inefficient:
1 | names = %w[Alice Bob] |
And can be written as this:
1 | names = %w[Alice Bob] |
There is a configuration property that allows to add/remove the method call names to take into account during the check:
1 | Performance/ChainedCallWithNoBang: |
Performance/CompactAfterMap
This rule is used to identify usage of compact
calls that follow map
. Such cases can be improved using compact_map
.
For example, this is considered inefficient:
1 | %w[Alice Bob].map(&.match(/^A./)).compact |
And can be written as this:
1 | %w[Alice Bob].compact_map(&.match(/^A./)) |
Performance/FlattenAfterMap
There is a close counterpart of a previous example: flatten
after map
and flat_map
. So there is another rule to which is used to identify this usecase.
For example, this is considered inefficient:
1 | %w[Alice Bob].map(&.chars).flatten |
And can be written as this:
1 | %w[Alice Bob].flat_map(&.chars) |
Performance/MapInsteadOfBlock
Another performance rule which is used to identify usage of join/sum/product
calls that follow map
.
For example, this is considered inefficient:
1 | (1..3).map(&.to_s).join('.') |
And can be written as this:
1 | (1..3).join('.', &.to_s) |
New Style Rules
Style/IsAFilter
This rule is used to identify usage of is_a?/nil?
calls within filters. It helps to avoid the boilerplate in filters and improve the readability.
For example, this is considered invalid:
1 | matches.any?(&.is_a?(Regex::MatchData)) |
And it should be written as this:
1 | matches.any?(Regex::MatchData) |
And of course there is a configuration property that allow to change the list of filter name to inspect:
1 | Style/IsAFilter: |
Style/VerboseBlock
As you might know some blocks in Crystal can have a shorter and much nicer form. This rule is used to identify usage of single expression blocks with argument as a receiver, that can be collapsed into such a short form.
For example, this is considered invalid:
1 | (1..3).any? { |i| i.odd? } |
And it should be written as this:
1 | (1..3).any?(&.odd?) |
There is a bag of configuration properties, which are not yet documented, but you can give it a shot:
1 | Style/VerboseBlock: |
Comments