Skip to content

Commit 6900858

Browse files
committed
Add Rake/ConstantDefinitionInTask cop
Adds a new cop, `Rake/ConstantDefinitionInTask`, that registers an offense for a constant assignment inside a task or namespace. ```ruby task :foo do CONST = 1 end namespace :foo do CONST = 1 end CONST = 1 task :foo do end ``` A constant assigned inside a `task` or `namespace` block is defined to the top level, not scoped to the block. That is the same surprise the existing cops already cover for methods and classes: * `Rake/MethodDefinitionInTask` for methods * `Rake/ClassDefinitionInTask` for classes and modules * `Rake/ConstantDefinitionInTask` (new) for plain constant assignments Rake files are also sometimes loaded rather than required, so a constant that leaks to the top level can produce "already initialized constant" warnings on reload. The cop reuses `Helper::TaskDefinition`, and like `Rake/ClassDefinitionInTask` it skips nodes inside a `class`, `module`, or `Class.new` body via `Helper::ClassDefinition.in_class_definition?`, because those constants are not defined to the top level. Adds the cop and its spec, a `config/default.yml` entry (`Enabled: pending`, `VersionAdded: '<<next>>'`), a `register_cop` directive in `lib/rubocop/cop/rake.rb` so the cop is registered for lazy loading, the cop count in `spec/lazy_loading_spec.rb`, and a CHANGELOG entry. `bundle exec rake` is green.
1 parent 1b1183d commit 6900858

6 files changed

Lines changed: 97 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## master (unreleased)
44

5+
### New features
6+
7+
* [#65](https://github.com/rubocop/rubocop-rake/pull/65): Add `Rake/ConstantDefinitionInTask` cop. ([@corsonknowles][])
8+
59
### Changes
610

711
* [#66](https://github.com/rubocop/rubocop-rake/pull/66): Speed up loading rubocop-rake by lazily loading only the cops needed for a run. This requires RuboCop 1.89.0+. ([@koic][])
@@ -82,3 +86,4 @@
8286
[@jaruuuu]: https://github.com/jaruuuu
8387
[@koic]: https://github.com/koic
8488
[@tejasbubane]: https://github.com/tejasbubane
89+
[@corsonknowles]: https://github.com/corsonknowles

config/default.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ Rake/ClassDefinitionInTask:
99
Enabled: true
1010
VersionAdded: '0.3.0'
1111

12+
Rake/ConstantDefinitionInTask:
13+
Description: 'Do not assign a constant in rake task, because it will be defined to the top level.'
14+
Enabled: pending
15+
VersionAdded: '<<next>>'
16+
1217
Rake/Desc:
1318
Description: 'Describe the task with `desc` method.'
1419
Enabled: true

lib/rubocop/cop/rake.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module Rake
1010
extend LazyLoader
1111

1212
register_cop :ClassDefinitionInTask, "#{__dir__}/rake/class_definition_in_task"
13+
register_cop :ConstantDefinitionInTask, "#{__dir__}/rake/constant_definition_in_task"
1314
register_cop :Desc, "#{__dir__}/rake/desc"
1415
register_cop :DuplicateTask, "#{__dir__}/rake/duplicate_task"
1516
register_cop :DuplicateNamespace, "#{__dir__}/rake/duplicate_namespace"
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# frozen_string_literal: true
2+
3+
module RuboCop
4+
module Cop
5+
module Rake
6+
# Detects constant assignment in a task or namespace,
7+
# because it is defined to the top level.
8+
# It is confusing because the scope looks in the task or namespace,
9+
# but actually it is defined to the top level.
10+
#
11+
# Class and module definitions are covered by `Rake/ClassDefinitionInTask`.
12+
#
13+
# @example
14+
# # bad
15+
# task :foo do
16+
# CONST = 1
17+
# end
18+
#
19+
# # bad
20+
# namespace :foo do
21+
# CONST = 1
22+
# end
23+
#
24+
# # good - It is also defined to the top level,
25+
# # but it looks expected behavior.
26+
# CONST = 1
27+
# task :foo do
28+
# end
29+
#
30+
class ConstantDefinitionInTask < Base
31+
MSG = 'Do not assign a constant in rake task, because it will be defined to the top level.'
32+
33+
def on_casgn(node)
34+
return if Helper::ClassDefinition.in_class_definition?(node)
35+
return unless Helper::TaskDefinition.in_task_or_namespace?(node)
36+
37+
add_offense(node)
38+
end
39+
end
40+
end
41+
end
42+
end

spec/lazy_loading_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def run_script(source)
2424
puts "loaded_cop_files=\#{loaded.size}"
2525
RUBY
2626

27-
expect(output).to include('registered=5', 'loaded_cop_files=0')
27+
expect(output).to include('registered=6', 'loaded_cop_files=0')
2828
end
2929

3030
it 'resolves every helper file in `lib/rubocop/cop/rake/helper` through an autoload' do
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe RuboCop::Cop::Rake::ConstantDefinitionInTask, :config do
4+
it 'registers an offense to a constant assignment in a task' do
5+
expect_offense(<<~RUBY)
6+
task :foo do
7+
CONST = 1
8+
^^^^^^^^^ Do not assign a constant in rake task, because it will be defined to the top level.
9+
end
10+
RUBY
11+
end
12+
13+
it 'registers an offense to a constant assignment in a namespace' do
14+
expect_offense(<<~RUBY)
15+
namespace :foo do
16+
CONST = 1
17+
^^^^^^^^^ Do not assign a constant in rake task, because it will be defined to the top level.
18+
19+
task :bar do
20+
end
21+
end
22+
RUBY
23+
end
24+
25+
it 'does not register an offense to a constant assignment at the top level' do
26+
expect_no_offenses(<<~RUBY)
27+
CONST = 1
28+
29+
task :foo do
30+
end
31+
RUBY
32+
end
33+
34+
it 'does not register an offense to a constant assignment inside a class in a task' do
35+
expect_no_offenses(<<~RUBY)
36+
task :foo do
37+
Class.new do
38+
CONST = 1
39+
end
40+
end
41+
RUBY
42+
end
43+
end

0 commit comments

Comments
 (0)